From 013c08659c6b8d1905dcdf5d38bd3942b6f0249c Mon Sep 17 00:00:00 2001 From: Anton Tranelis Date: Wed, 28 May 2025 16:46:40 +0200 Subject: [PATCH] registration handling --- src/Components/Gaming/Modal.tsx | 16 +- src/Components/Onboarding/Onboarding.tsx | 194 +++++++++++------- src/Components/Onboarding/Steps/SetAvatar.tsx | 20 ++ src/Components/Onboarding/Steps/Signup.tsx | 64 ++++++ src/index.tsx | 2 +- 5 files changed, 210 insertions(+), 86 deletions(-) create mode 100644 src/Components/Onboarding/Steps/SetAvatar.tsx create mode 100644 src/Components/Onboarding/Steps/Signup.tsx diff --git a/src/Components/Gaming/Modal.tsx b/src/Components/Gaming/Modal.tsx index af1e1f6e..02a65c54 100644 --- a/src/Components/Gaming/Modal.tsx +++ b/src/Components/Gaming/Modal.tsx @@ -9,24 +9,24 @@ export function Modal({ }) { useEffect(() => { if (showOnStartup) { - window.my_modal_3?.showModal() + window.my_modal_3.showModal() } - }, []) + }, [showOnStartup]) return ( - -
+ +
{children}
-
+
) -} \ No newline at end of file +} diff --git a/src/Components/Onboarding/Onboarding.tsx b/src/Components/Onboarding/Onboarding.tsx index 6f692f61..6c4bb515 100644 --- a/src/Components/Onboarding/Onboarding.tsx +++ b/src/Components/Onboarding/Onboarding.tsx @@ -1,13 +1,17 @@ -import { useState } from 'react' +import { useRef, useState } from 'react' import SVG from 'react-inlinesvg' -import { Link, useNavigate } from 'react-router-dom' +import { useNavigate } from 'react-router-dom' +import { toast } from 'react-toastify' +import { useAuth } from '#components/Auth/useAuth' import { useLayers } from '#components/Map/hooks/useLayers' -import { AvatarWidget } from '#components/Profile/Subcomponents/AvatarWidget' -import type { LayerProps } from '#src/index' +import { SetAvatar } from './Steps/SetAvatar' +import { Signup } from './Steps/Signup' + +import type { LayerProps, UserItem } from '#src/index' +import type { SignupHandle } from './Steps/Signup' -// Schritt-Komponenten const Step1 = () => { const layers = useLayers() return ( @@ -34,67 +38,6 @@ const Step1 = () => { ) } -const Step2 = () => { - const [userName, setUserName] = useState('') - const [email, setEmail] = useState('') - const [password, setPassword] = useState('') - return ( -
-

Erstelle dir deinen Acount

-

- Werde Teil des Netzwerks und erstelle dir dein Profil und zeige dich auf der Karte! -

- setUserName(e.target.value)} - className='tw:input tw:input-bordered tw:w-full tw:max-w-xs' - /> - setEmail(e.target.value)} - className='tw:input tw:input-bordered tw:w-full tw:max-w-xs' - /> - setPassword(e.target.value)} - className='tw:input tw:input-bordered tw:w-full tw:max-w-xs' - /> -

- Du hast schon einen Account?{' '} - close()} - to='/login' - > - Dann logge dich ein! - -

-
- ) -} - -const Step3 = () => { - const [avatar, setAvatar] = useState('') - return ( -
-

Lade ein Bild von dir hoch

-
- -
-
- -
-
- ) -} - const Step4 = () => (

Place your Profile on the Map!

@@ -102,18 +45,111 @@ const Step4 = () => (
) -const stepsTitles = ['Willkommen', 'Account', 'Avatar', 'Marker'] - export const Onboarding = () => { - const close = () => { - navigate('/') - } - + const signupRef = useRef(null) + const [loading, setLoading] = useState(false) const navigate = useNavigate() + const { isAuthenticated } = useAuth() + + const handleNext = async () => { + const currentStep = steps[stepIndex] + + if (loading) return + + if (currentStep.onNext) { + try { + setLoading(true) + const result = await currentStep.onNext() + + if (result) { + const user = result as UserItem + const successMessage = currentStep.toastSuccess?.message?.replace( + '{firstName}', + user.first_name ?? 'Traveler', + ) + toast.success(successMessage ?? `Hi ${user.first_name ?? 'Traveler'}`, { + icon: currentStep.toastSuccess?.icon ?? '✌️', + }) + setStepIndex((i) => i + 1) + } + // eslint-disable-next-line no-catch-all/no-catch-all + } catch (error) { + toast.error( + currentStep.toastError?.message ?? + (error instanceof Error ? error.message : 'Ein Fehler ist aufgetreten'), + { + autoClose: currentStep.toastError?.autoClose ?? 10000, + }, + ) + } finally { + setLoading(false) + } + } + } const [stepIndex, setStepIndex] = useState(0) - const steps = [, , , ] + interface StepDefinition { + component: JSX.Element + onNext?: () => Promise | void + toastSuccess?: { + message?: string + icon?: string + } + toastError?: { + message?: string + autoClose?: number + } + loading?: { + message?: string + } + } + + const steps: StepDefinition[] = [ + { + component: , + onNext: () => setStepIndex((i) => i + 1), + }, + { + component: , + onNext: async () => { + return await signupRef.current?.submit() + }, + toastSuccess: { + message: 'Hi {firstName}!', + icon: '✌️', + }, + toastError: { + message: 'Registration failed. Please try again.', + autoClose: 8000, + }, + loading: { + message: 'Creating your account...', + }, + }, + { + component: , + onNext: () => setStepIndex((i) => i + 1), + toastSuccess: { + message: 'Avatar uploaded successfully!', + icon: '🖼️', + }, + loading: { + message: 'Uploading avatar...', + }, + }, + { + component: , + onNext: () => navigate('/'), + toastSuccess: { + message: 'Welcome to the community!', + icon: '🎉', + }, + loading: { + message: 'Finalizing your profile...', + }, + }, + ] const isLast = stepIndex === steps.length - 1 const isFirst = stepIndex === 0 @@ -121,7 +157,7 @@ export const Onboarding = () => { return (
- {steps[stepIndex]} + {steps[stepIndex].component}
{!isLast ? ( - ) : ( - )} diff --git a/src/Components/Onboarding/Steps/SetAvatar.tsx b/src/Components/Onboarding/Steps/SetAvatar.tsx new file mode 100644 index 00000000..336b2850 --- /dev/null +++ b/src/Components/Onboarding/Steps/SetAvatar.tsx @@ -0,0 +1,20 @@ +import { useState } from 'react' + +import { AvatarWidget } from '#components/Profile/Subcomponents/AvatarWidget' + +export const SetAvatar = () => { + const [avatar, setAvatar] = useState('') + return ( +
+

Lade ein Bild von dir hoch

+
+ +
+
+ +
+
+ ) +} diff --git a/src/Components/Onboarding/Steps/Signup.tsx b/src/Components/Onboarding/Steps/Signup.tsx new file mode 100644 index 00000000..81518af5 --- /dev/null +++ b/src/Components/Onboarding/Steps/Signup.tsx @@ -0,0 +1,64 @@ +import { forwardRef, useImperativeHandle, useState } from 'react' +import { Link } from 'react-router-dom' + +import { useAuth } from '#components/Auth/useAuth' + +import type { UserItem } from '#components/Auth/useAuth' + +export interface SignupHandle { + submit: () => Promise +} + +export const Signup = forwardRef((_, ref) => { + const [userName, setUserName] = useState('') + const [email, setEmail] = useState('') + const [password, setPassword] = useState('') + + const { register } = useAuth() + + useImperativeHandle(ref, () => ({ + submit: async () => { + return register({ email, password }, userName) + }, + })) + + return ( +
+

Erstelle dir deinen Account

+

+ Werde Teil des Netzwerks und erstelle dein Profil und zeige dich auf der Karte! +

+ setUserName(e.target.value)} + className='tw:input tw:input-bordered tw:w-full tw:max-w-xs' + /> + setEmail(e.target.value)} + className='tw:input tw:input-bordered tw:w-full tw:max-w-xs' + /> + setPassword(e.target.value)} + className='tw:input tw:input-bordered tw:w-full tw:max-w-xs' + /> +

+ Du hast schon einen Account?{' '} + + Dann logge dich ein! + +

+
+ ) +}) +Signup.displayName = 'Signup' diff --git a/src/index.tsx b/src/index.tsx index 498ad695..3265132d 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -14,7 +14,7 @@ export * from './Components/Onboarding' declare global { interface Window { my_modal_3: { - [x: string]: any + close(): void showModal(): void } }