Files
36-character-flower/src/features/auth/components/desktop-register-form-view.tsx

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>
)
}