mirror of
https://github.com/utopia-os/utopia-ui.git
synced 2025-12-13 07:46:10 +00:00
registration handling
This commit is contained in:
parent
9be07334f5
commit
013c08659c
@ -9,22 +9,22 @@ export function Modal({
|
||||
}) {
|
||||
useEffect(() => {
|
||||
if (showOnStartup) {
|
||||
window.my_modal_3?.showModal()
|
||||
window.my_modal_3.showModal()
|
||||
}
|
||||
}, [])
|
||||
}, [showOnStartup])
|
||||
|
||||
return (
|
||||
<dialog id="my_modal_3" className="tw:modal tw:transition-all tw:duration-300">
|
||||
<div className="tw:modal-box tw:transition-none">
|
||||
<dialog id='my_modal_3' className='tw:modal tw:transition-all tw:duration-300'>
|
||||
<div className='tw:modal-box tw:transition-none'>
|
||||
<button
|
||||
className="tw:btn tw:btn-sm tw:btn-circle tw:btn-ghost tw:absolute tw:right-2 tw:top-2"
|
||||
onClick={() => window.my_modal_3?.close()}
|
||||
className='tw:btn tw:btn-sm tw:btn-circle tw:btn-ghost tw:absolute tw:right-2 tw:top-2'
|
||||
onClick={() => window.my_modal_3.close()}
|
||||
>
|
||||
✕
|
||||
</button>
|
||||
{children}
|
||||
</div>
|
||||
<form method="dialog" className="tw:modal-backdrop">
|
||||
<form method='dialog' className='tw:modal-backdrop'>
|
||||
<button>close</button>
|
||||
</form>
|
||||
</dialog>
|
||||
|
||||
@ -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<string>('')
|
||||
const [email, setEmail] = useState<string>('')
|
||||
const [password, setPassword] = useState<string>('')
|
||||
return (
|
||||
<div className='tw:space-y-2'>
|
||||
<h3 className='tw:text-lg tw:font-bold'>Erstelle dir deinen Acount</h3>
|
||||
<p className='tw:my-4'>
|
||||
Werde Teil des Netzwerks und erstelle dir dein Profil und zeige dich auf der Karte!
|
||||
</p>
|
||||
<input
|
||||
type='text'
|
||||
placeholder='Name'
|
||||
value={userName}
|
||||
onChange={(e) => setUserName(e.target.value)}
|
||||
className='tw:input tw:input-bordered tw:w-full tw:max-w-xs'
|
||||
/>
|
||||
<input
|
||||
type='email'
|
||||
placeholder='E-Mail'
|
||||
value={email}
|
||||
onChange={(e) => setEmail(e.target.value)}
|
||||
className='tw:input tw:input-bordered tw:w-full tw:max-w-xs'
|
||||
/>
|
||||
<input
|
||||
type='password'
|
||||
placeholder='Password'
|
||||
onChange={(e) => setPassword(e.target.value)}
|
||||
className='tw:input tw:input-bordered tw:w-full tw:max-w-xs'
|
||||
/>
|
||||
<p className='tw:mt-4 tw:mb-8'>
|
||||
Du hast schon einen Account?{' '}
|
||||
<Link
|
||||
className='tw:inline-block tw:hover:text-primary tw:hover:underline tw:hover:cursor-pointer tw:transition tw:duration-200 tw:text-primary'
|
||||
onClick={() => close()}
|
||||
to='/login'
|
||||
>
|
||||
Dann logge dich ein!
|
||||
</Link>
|
||||
</p>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
const Step3 = () => {
|
||||
const [avatar, setAvatar] = useState<string>('')
|
||||
return (
|
||||
<div>
|
||||
<h3 className='tw:text-lg tw:font-bold tw:text-center'>Lade ein Bild von dir hoch</h3>
|
||||
<div className='tw:mt-4 tw:flex tw:justify-center tw:items-center'>
|
||||
<AvatarWidget avatar={avatar} setAvatar={setAvatar} />
|
||||
</div>
|
||||
<div className='tw:mt-4 tw:flex tw:justify-center'>
|
||||
<button className='tw:btn tw:justify-center' onClick={() => setAvatar('')}>
|
||||
Select
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
const Step4 = () => (
|
||||
<div>
|
||||
<h3 className='tw:text-lg tw:font-bold'>Place your Profile on the Map!</h3>
|
||||
@ -102,18 +45,111 @@ const Step4 = () => (
|
||||
</div>
|
||||
)
|
||||
|
||||
const stepsTitles = ['Willkommen', 'Account', 'Avatar', 'Marker']
|
||||
|
||||
export const Onboarding = () => {
|
||||
const close = () => {
|
||||
navigate('/')
|
||||
}
|
||||
|
||||
const signupRef = useRef<SignupHandle>(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 = [<Step1 key={1} />, <Step2 key={2} />, <Step3 key={3} />, <Step4 key={4} />]
|
||||
interface StepDefinition {
|
||||
component: JSX.Element
|
||||
onNext?: () => Promise<unknown> | void
|
||||
toastSuccess?: {
|
||||
message?: string
|
||||
icon?: string
|
||||
}
|
||||
toastError?: {
|
||||
message?: string
|
||||
autoClose?: number
|
||||
}
|
||||
loading?: {
|
||||
message?: string
|
||||
}
|
||||
}
|
||||
|
||||
const steps: StepDefinition[] = [
|
||||
{
|
||||
component: <Step1 />,
|
||||
onNext: () => setStepIndex((i) => i + 1),
|
||||
},
|
||||
{
|
||||
component: <Signup ref={signupRef} />,
|
||||
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: <SetAvatar />,
|
||||
onNext: () => setStepIndex((i) => i + 1),
|
||||
toastSuccess: {
|
||||
message: 'Avatar uploaded successfully!',
|
||||
icon: '🖼️',
|
||||
},
|
||||
loading: {
|
||||
message: 'Uploading avatar...',
|
||||
},
|
||||
},
|
||||
{
|
||||
component: <Step4 />,
|
||||
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 (
|
||||
<div className='tw:max-w-xl tw:w-full tw:mx-auto tw:p-4'>
|
||||
<div>
|
||||
{steps[stepIndex]}
|
||||
{steps[stepIndex].component}
|
||||
|
||||
<div className='tw:flex tw:justify-between tw:mt-6'>
|
||||
<button
|
||||
@ -141,11 +177,15 @@ export const Onboarding = () => {
|
||||
))}
|
||||
</div>
|
||||
{!isLast ? (
|
||||
<button className='tw:btn tw:btn-primary' onClick={() => setStepIndex((i) => i + 1)}>
|
||||
Next
|
||||
<button
|
||||
className={`tw:btn ${loading ? 'tw:btn-disabled' : 'tw:btn-primary'}`}
|
||||
// eslint-disable-next-line @typescript-eslint/no-misused-promises
|
||||
onClick={handleNext}
|
||||
>
|
||||
{loading ? <span className='tw:loading tw:loading-spinner'></span> : 'Next'}
|
||||
</button>
|
||||
) : (
|
||||
<button className='tw:btn tw:btn-success' onClick={() => close()}>
|
||||
<button className='tw:btn tw:btn-success' onClick={close}>
|
||||
Close
|
||||
</button>
|
||||
)}
|
||||
|
||||
20
src/Components/Onboarding/Steps/SetAvatar.tsx
Normal file
20
src/Components/Onboarding/Steps/SetAvatar.tsx
Normal file
@ -0,0 +1,20 @@
|
||||
import { useState } from 'react'
|
||||
|
||||
import { AvatarWidget } from '#components/Profile/Subcomponents/AvatarWidget'
|
||||
|
||||
export const SetAvatar = () => {
|
||||
const [avatar, setAvatar] = useState<string>('')
|
||||
return (
|
||||
<div>
|
||||
<h3 className='tw:text-lg tw:font-bold tw:text-center'>Lade ein Bild von dir hoch</h3>
|
||||
<div className='tw:mt-4 tw:flex tw:justify-center tw:items-center'>
|
||||
<AvatarWidget avatar={avatar} setAvatar={setAvatar} />
|
||||
</div>
|
||||
<div className='tw:mt-4 tw:flex tw:justify-center'>
|
||||
<button className='tw:btn tw:justify-center' onClick={() => setAvatar('')}>
|
||||
Select
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
64
src/Components/Onboarding/Steps/Signup.tsx
Normal file
64
src/Components/Onboarding/Steps/Signup.tsx
Normal file
@ -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<UserItem | undefined>
|
||||
}
|
||||
|
||||
export const Signup = forwardRef<SignupHandle>((_, 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 (
|
||||
<div className='tw:space-y-2'>
|
||||
<h3 className='tw:text-lg tw:font-bold'>Erstelle dir deinen Account</h3>
|
||||
<p className='tw:my-4'>
|
||||
Werde Teil des Netzwerks und erstelle dein Profil und zeige dich auf der Karte!
|
||||
</p>
|
||||
<input
|
||||
type='text'
|
||||
placeholder='Name'
|
||||
value={userName}
|
||||
onChange={(e) => setUserName(e.target.value)}
|
||||
className='tw:input tw:input-bordered tw:w-full tw:max-w-xs'
|
||||
/>
|
||||
<input
|
||||
type='email'
|
||||
placeholder='E-Mail'
|
||||
value={email}
|
||||
onChange={(e) => setEmail(e.target.value)}
|
||||
className='tw:input tw:input-bordered tw:w-full tw:max-w-xs'
|
||||
/>
|
||||
<input
|
||||
type='password'
|
||||
placeholder='Password'
|
||||
value={password}
|
||||
onChange={(e) => setPassword(e.target.value)}
|
||||
className='tw:input tw:input-bordered tw:w-full tw:max-w-xs'
|
||||
/>
|
||||
<p className='tw:mt-4 tw:mb-8'>
|
||||
Du hast schon einen Account?{' '}
|
||||
<Link
|
||||
className='tw:inline-block tw:hover:text-primary tw:hover:underline tw:hover:cursor-pointer tw:transition tw:duration-200 tw:text-primary'
|
||||
to='/login'
|
||||
>
|
||||
Dann logge dich ein!
|
||||
</Link>
|
||||
</p>
|
||||
</div>
|
||||
)
|
||||
})
|
||||
Signup.displayName = 'Signup'
|
||||
@ -14,7 +14,7 @@ export * from './Components/Onboarding'
|
||||
declare global {
|
||||
interface Window {
|
||||
my_modal_3: {
|
||||
[x: string]: any
|
||||
close(): void
|
||||
showModal(): void
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user