239 lines
9.9 KiB
TypeScript
239 lines
9.9 KiB
TypeScript
import { motion, useReducedMotion } from 'motion/react'
|
|
import { useTranslation } from 'react-i18next'
|
|
import loginBg from '@/assets/system/login-bg.webp'
|
|
import { SmartBackground } from '@/components/smart-background.tsx'
|
|
import { Input } from '@/components/ui/input.tsx'
|
|
import {
|
|
DesktopAuthFieldRow,
|
|
DesktopAuthInputError,
|
|
DesktopAuthSubmitError,
|
|
} from './desktop-auth-form-parts'
|
|
|
|
interface DesktopRegisterFormViewProps {
|
|
errors: {
|
|
confirmPassword?: string
|
|
inviteCode?: string
|
|
password?: string
|
|
username?: string
|
|
}
|
|
inviteCode: string
|
|
isSubmitting: boolean
|
|
onConfirmPasswordChange: (value: string) => void
|
|
onInviteCodeChange: (value: string) => void
|
|
onPasswordChange: (value: string) => void
|
|
onSubmit: () => void
|
|
onSwitchToLogin: () => void
|
|
onUsernameChange: (value: string) => void
|
|
password: string
|
|
confirmPassword: string
|
|
submitError?: string | null
|
|
username: string
|
|
}
|
|
|
|
export function DesktopRegisterFormView({
|
|
confirmPassword,
|
|
errors,
|
|
inviteCode,
|
|
isSubmitting,
|
|
onConfirmPasswordChange,
|
|
onInviteCodeChange,
|
|
onPasswordChange,
|
|
onSubmit,
|
|
onSwitchToLogin,
|
|
onUsernameChange,
|
|
password,
|
|
submitError,
|
|
username,
|
|
}: DesktopRegisterFormViewProps) {
|
|
const { t } = useTranslation()
|
|
const prefersReducedMotion = useReducedMotion()
|
|
|
|
return (
|
|
<form
|
|
onSubmit={(event) => {
|
|
event.preventDefault()
|
|
onSubmit()
|
|
}}
|
|
className={
|
|
'relative isolate flex flex-col px-design-30 mt-design-10 box-border h-design-700 !overflow-hidden'
|
|
}
|
|
>
|
|
<div
|
|
className={
|
|
'relative w-full overflow-hidden rounded-[calc(var(--design-unit)*18)] border border-[#214B53] bg-[linear-gradient(180deg,rgba(7,21,27,0.94)_0%,rgba(5,15,20,0.82)_100%)] shadow-[0_0_calc(var(--design-unit)*26)_rgba(6,112,126,0.14),inset_0_0_0_calc(var(--design-unit)*1)_rgba(120,222,227,0.08)] backdrop-blur-[calc(var(--design-unit)*10)]'
|
|
}
|
|
>
|
|
<div className="pointer-events-none absolute inset-0 bg-[radial-gradient(circle_at_top_right,rgba(93,211,218,0.16),transparent_30%),radial-gradient(circle_at_bottom_left,rgba(63,109,137,0.22),transparent_34%)]" />
|
|
<div className="pointer-events-none absolute inset-x-0 top-0 h-design-140 bg-[linear-gradient(180deg,rgba(87,196,201,0.12),transparent)]" />
|
|
<div className="relative flex flex-col gap-design-20 p-design-50">
|
|
<div className="flex items-center gap-design-14">
|
|
<div className="h-design-10 w-design-10 rounded-full bg-[#6EE4E6] shadow-[0_0_calc(var(--design-unit)*12)_rgba(110,228,230,0.75)]" />
|
|
<div className="h-px flex-1 bg-[linear-gradient(90deg,rgba(110,228,230,0.5),rgba(110,228,230,0))]" />
|
|
</div>
|
|
|
|
<DesktopAuthFieldRow label={t('auth.register.fields.username.label')}>
|
|
<Input
|
|
id="desktop-register-username"
|
|
name="username"
|
|
autoComplete="username"
|
|
spellCheck={false}
|
|
value={username}
|
|
onChange={(event) => onUsernameChange(event.target.value)}
|
|
placeholder={t('auth.register.fields.username.placeholder')}
|
|
aria-describedby="desktop-register-username-error"
|
|
aria-invalid={Boolean(errors.username)}
|
|
className={
|
|
'h-design-58 border border-transparent bg-[linear-gradient(180deg,rgba(23,98,105,0.72),rgba(11,62,68,0.82))] text-left shadow-[inset_0_0_calc(var(--design-unit)*10)_rgba(129,239,243,0.05)]'
|
|
}
|
|
/>
|
|
<div
|
|
id="desktop-register-username-error"
|
|
className="relative h-design-30 overflow-hidden"
|
|
>
|
|
<div className="absolute inset-x-0 top-0">
|
|
<DesktopAuthInputError
|
|
message={errors.username ? t(errors.username) : undefined}
|
|
/>
|
|
</div>
|
|
</div>
|
|
</DesktopAuthFieldRow>
|
|
|
|
<DesktopAuthFieldRow label={t('auth.register.fields.password.label')}>
|
|
<Input
|
|
id="desktop-register-password"
|
|
name="password"
|
|
type="password"
|
|
autoComplete="new-password"
|
|
value={password}
|
|
onChange={(event) => onPasswordChange(event.target.value)}
|
|
placeholder={t('auth.register.fields.password.placeholder')}
|
|
aria-describedby="desktop-register-password-error"
|
|
aria-invalid={Boolean(errors.password)}
|
|
className={
|
|
'h-design-58 border border-transparent bg-[linear-gradient(180deg,rgba(23,98,105,0.72),rgba(11,62,68,0.82))] text-left shadow-[inset_0_0_calc(var(--design-unit)*10)_rgba(129,239,243,0.05)]'
|
|
}
|
|
/>
|
|
<div
|
|
id="desktop-register-password-error"
|
|
className="relative h-design-30 overflow-hidden"
|
|
>
|
|
<div className="absolute inset-x-0 top-0">
|
|
<DesktopAuthInputError
|
|
message={errors.password ? t(errors.password) : undefined}
|
|
/>
|
|
</div>
|
|
</div>
|
|
</DesktopAuthFieldRow>
|
|
|
|
<DesktopAuthFieldRow
|
|
label={t('auth.register.fields.confirmPassword.label')}
|
|
>
|
|
<Input
|
|
id="desktop-register-confirm-password"
|
|
name="confirmPassword"
|
|
type="password"
|
|
autoComplete="new-password"
|
|
value={confirmPassword}
|
|
onChange={(event) => onConfirmPasswordChange(event.target.value)}
|
|
placeholder={t(
|
|
'auth.register.fields.confirmPassword.placeholder',
|
|
)}
|
|
aria-describedby="desktop-register-confirm-password-error"
|
|
aria-invalid={Boolean(errors.confirmPassword)}
|
|
className={
|
|
'h-design-58 border border-transparent bg-[linear-gradient(180deg,rgba(23,98,105,0.72),rgba(11,62,68,0.82))] text-left shadow-[inset_0_0_calc(var(--design-unit)*10)_rgba(129,239,243,0.05)]'
|
|
}
|
|
/>
|
|
<div
|
|
id="desktop-register-confirm-password-error"
|
|
className="relative h-design-30 overflow-hidden"
|
|
>
|
|
<div className="absolute inset-x-0 top-0">
|
|
<DesktopAuthInputError
|
|
message={
|
|
errors.confirmPassword
|
|
? t(errors.confirmPassword)
|
|
: undefined
|
|
}
|
|
/>
|
|
</div>
|
|
</div>
|
|
</DesktopAuthFieldRow>
|
|
|
|
<DesktopAuthFieldRow
|
|
label={t('auth.register.fields.inviteCode.label')}
|
|
labelClassName="whitespace-nowrap"
|
|
>
|
|
<Input
|
|
id="desktop-register-invite-code"
|
|
name="inviteCode"
|
|
autoComplete="off"
|
|
spellCheck={false}
|
|
value={inviteCode}
|
|
onChange={(event) => onInviteCodeChange(event.target.value)}
|
|
placeholder={t('auth.register.fields.inviteCode.placeholder')}
|
|
aria-describedby="desktop-register-invite-code-error"
|
|
aria-invalid={Boolean(errors.inviteCode)}
|
|
className={
|
|
'h-design-58 max-w-design-520 border border-transparent bg-[linear-gradient(180deg,rgba(23,98,105,0.72),rgba(11,62,68,0.82))] text-left shadow-[inset_0_0_calc(var(--design-unit)*10)_rgba(129,239,243,0.05)]'
|
|
}
|
|
/>
|
|
<div
|
|
id="desktop-register-invite-code-error"
|
|
className="relative h-design-30 overflow-hidden"
|
|
>
|
|
<div className="absolute inset-x-0 top-0">
|
|
<DesktopAuthInputError
|
|
message={errors.inviteCode ? t(errors.inviteCode) : undefined}
|
|
/>
|
|
</div>
|
|
</div>
|
|
</DesktopAuthFieldRow>
|
|
|
|
<div className="mt-auto flex flex-col">
|
|
<DesktopAuthSubmitError
|
|
message={submitError ? t(submitError) : undefined}
|
|
/>
|
|
|
|
<motion.div
|
|
whileTap={prefersReducedMotion ? undefined : { scale: 0.98 }}
|
|
className="flex items-center justify-center gap-design-12 text-center text-design-20 text-[#6DB5B9]"
|
|
>
|
|
<div className="h-px w-design-90 bg-[linear-gradient(90deg,rgba(109,181,185,0),rgba(109,181,185,0.7))]" />
|
|
<button
|
|
type="button"
|
|
onClick={onSwitchToLogin}
|
|
className="cursor-pointer underline underline-offset-[calc(var(--design-unit)*4)] transition-colors duration-200 ease-out hover:text-[#90DBDE] focus-visible:outline-none focus-visible:text-[#90DBDE]"
|
|
>
|
|
{t('auth.register.footer.alreadyHaveAccount')}
|
|
</button>
|
|
<div className="h-px w-design-90 bg-[linear-gradient(90deg,rgba(109,181,185,0.7),rgba(109,181,185,0))]" />
|
|
</motion.div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="relative flex w-full justify-center">
|
|
<div className="pointer-events-none absolute inset-x-[24%] top-1/2 h-design-40 -translate-y-1/2 rounded-full bg-[rgba(76,213,216,0.22)] blur-[calc(var(--design-unit)*22)]" />
|
|
<SmartBackground
|
|
as={motion.button}
|
|
type="submit"
|
|
whileTap={prefersReducedMotion ? undefined : { scale: 0.97 }}
|
|
src={loginBg}
|
|
size="100% 100%"
|
|
className={
|
|
'relative z-10 flex h-design-110 w-design-390 cursor-pointer items-center justify-center overflow-hidden text-design-32 font-bold text-[#F2FFFF] duration-200 ease-out hover:brightness-110 disabled:pointer-events-none disabled:opacity-60'
|
|
}
|
|
disabled={isSubmitting}
|
|
>
|
|
<span className="modal-title-glow text-design-24">
|
|
{isSubmitting
|
|
? t('auth.common.actions.submitting')
|
|
: t('auth.register.actions.submit')}
|
|
</span>
|
|
</SmartBackground>
|
|
</div>
|
|
</form>
|
|
)
|
|
}
|