- Added new date formatting function for better user experience. - Updated layout of the public booking page for improved responsiveness and aesthetics. - Integrated sport selection with visual icons for better clarity. - Enhanced error handling and loading states for availability queries. feat: revamp select complex page for better user interaction - Redesigned the select complex page layout for a more modern look. - Added contextual information about complex selection. - Improved button interactions with loading indicators and icons. style: update CSS variables for a cohesive sports-club theme - Adjusted color variables to reflect a sports-club identity. - Enhanced background styles for both light and dark modes. - Improved overall styling consistency across components.
109 lines
3.3 KiB
TypeScript
109 lines
3.3 KiB
TypeScript
import { Button } from '@/components/ui/button';
|
|
import { Field, FieldError, FieldLabel } from '@/components/ui/field';
|
|
import {
|
|
InputOTP,
|
|
InputOTPGroup,
|
|
InputOTPSeparator,
|
|
InputOTPSlot,
|
|
} from '@/components/ui/input-otp';
|
|
import type { VerifyOtpValues } from '@/features/onboard/onboarding.types';
|
|
import { RefreshCw } from 'lucide-react';
|
|
import type { UseFormReturn } from 'react-hook-form';
|
|
|
|
type OnboardingVerifyOtpStepProps = {
|
|
form: UseFormReturn<VerifyOtpValues>;
|
|
email: string | null;
|
|
errorMessage: string | null;
|
|
infoMessage: string | null;
|
|
remainingAttempts: number;
|
|
resendCooldownSeconds: number;
|
|
isResending: boolean;
|
|
onSubmit: (values: VerifyOtpValues) => Promise<void>;
|
|
onResend: () => Promise<void>;
|
|
};
|
|
|
|
export function OnboardingVerifyOtpStep({
|
|
form,
|
|
email,
|
|
errorMessage,
|
|
infoMessage,
|
|
remainingAttempts,
|
|
resendCooldownSeconds,
|
|
isResending,
|
|
onSubmit,
|
|
onResend,
|
|
}: OnboardingVerifyOtpStepProps) {
|
|
const resendDisabled = resendCooldownSeconds > 0 || isResending;
|
|
|
|
return (
|
|
<form className="space-y-4" onSubmit={form.handleSubmit(onSubmit)}>
|
|
<p className="text-sm text-muted-foreground">
|
|
Ingresa el código de 6 dígitos que enviamos a{' '}
|
|
<span className="font-medium text-foreground">{email ?? 'tu email'}</span>.
|
|
</p>
|
|
|
|
<Field data-invalid={Boolean(form.formState.errors.otp)}>
|
|
<div className="mb-2 flex items-center justify-between gap-3">
|
|
<FieldLabel htmlFor="onboard-otp">Código de verificación</FieldLabel>
|
|
<Button
|
|
type="button"
|
|
variant="outline"
|
|
size="sm"
|
|
disabled={resendDisabled}
|
|
onClick={() => {
|
|
void onResend();
|
|
}}
|
|
>
|
|
<RefreshCw className="mr-1 h-3.5 w-3.5" />
|
|
{isResending
|
|
? 'Reenviando...'
|
|
: resendCooldownSeconds > 0
|
|
? `Reenviar en ${resendCooldownSeconds}s`
|
|
: 'Reenviar código'}
|
|
</Button>
|
|
</div>
|
|
|
|
<InputOTP
|
|
id="onboard-otp"
|
|
maxLength={6}
|
|
inputMode="numeric"
|
|
pattern="\d*"
|
|
value={form.watch('otp')}
|
|
onChange={(value) => {
|
|
form.setValue('otp', value, { shouldValidate: true });
|
|
}}
|
|
>
|
|
<InputOTPGroup>
|
|
<InputOTPSlot index={0} />
|
|
<InputOTPSlot index={1} />
|
|
<InputOTPSlot index={2} />
|
|
</InputOTPGroup>
|
|
<InputOTPSeparator />
|
|
<InputOTPGroup>
|
|
<InputOTPSlot index={3} />
|
|
<InputOTPSlot index={4} />
|
|
<InputOTPSlot index={5} />
|
|
</InputOTPGroup>
|
|
</InputOTP>
|
|
|
|
<FieldError errors={[form.formState.errors.otp]} />
|
|
</Field>
|
|
|
|
<p className="text-xs text-muted-foreground">
|
|
Intentos restantes: <span className="font-medium">{remainingAttempts}</span>
|
|
</p>
|
|
|
|
{errorMessage && <p className="text-sm text-destructive">{errorMessage}</p>}
|
|
{infoMessage && <p className="text-sm text-muted-foreground">{infoMessage}</p>}
|
|
|
|
<Button
|
|
type="submit"
|
|
className="w-full"
|
|
disabled={form.formState.isSubmitting || !form.formState.isValid}
|
|
>
|
|
{form.formState.isSubmitting ? 'Verificando...' : 'Verificar código'}
|
|
</Button>
|
|
</form>
|
|
);
|
|
}
|