website refactor
This commit is contained in:
@@ -5,13 +5,12 @@ import { AuthFooterLinks } from '@/components/auth/AuthFooterLinks';
|
||||
import { AuthForm } from '@/components/auth/AuthForm';
|
||||
import { ForgotPasswordViewData } from '@/lib/builders/view-data/types/ForgotPasswordViewData';
|
||||
import { routes } from '@/lib/routing/RouteConfig';
|
||||
import { Box } from '@/ui/Box';
|
||||
import { Button } from '@/ui/Button';
|
||||
import { Group } from '@/ui/Group';
|
||||
import { Icon } from '@/ui/Icon';
|
||||
import { Input } from '@/ui/Input';
|
||||
import { Link } from '@/ui/Link';
|
||||
import { LoadingSpinner } from '@/ui/LoadingSpinner';
|
||||
import { Stack } from '@/ui/Stack';
|
||||
import { Text } from '@/ui/Text';
|
||||
import { AlertCircle, ArrowLeft, CheckCircle2, Mail, Shield } from 'lucide-react';
|
||||
import React from 'react';
|
||||
@@ -54,12 +53,10 @@ export function ForgotPasswordTemplate({ viewData, formActions, mutationState }:
|
||||
/>
|
||||
|
||||
{mutationState.error && (
|
||||
<Box p={4} bg="critical-red/10" border borderColor="critical-red/30" rounded="md">
|
||||
<Stack direction="row" align="start" gap={3}>
|
||||
<Icon icon={AlertCircle} size={4.5} color="var(--color-critical)" />
|
||||
<Text size="sm" color="text-critical-red">{mutationState.error}</Text>
|
||||
</Stack>
|
||||
</Box>
|
||||
<Group direction="row" align="start" gap={3} fullWidth>
|
||||
<Icon icon={AlertCircle} size={4.5} color="var(--color-critical)" />
|
||||
<Text size="sm" color="text-critical-red">{mutationState.error}</Text>
|
||||
</Group>
|
||||
)}
|
||||
|
||||
<Button
|
||||
@@ -72,36 +69,34 @@ export function ForgotPasswordTemplate({ viewData, formActions, mutationState }:
|
||||
{isSubmitting ? 'Sending...' : 'Send Reset Link'}
|
||||
</Button>
|
||||
|
||||
<Box textAlign="center">
|
||||
<Group justify="center" fullWidth>
|
||||
<Link href={routes.auth.login}>
|
||||
<Stack direction="row" align="center" justify="center" gap={2} group>
|
||||
<Icon icon={ArrowLeft} size={3.5} color="var(--color-primary)" groupHoverScale />
|
||||
<Group direction="row" align="center" justify="center" gap={2}>
|
||||
<Icon icon={ArrowLeft} size={3.5} color="var(--color-primary)" />
|
||||
<Text size="sm" weight="bold" color="text-primary-accent">Back to Login</Text>
|
||||
</Stack>
|
||||
</Group>
|
||||
</Link>
|
||||
</Box>
|
||||
</Group>
|
||||
</AuthForm>
|
||||
) : (
|
||||
<Stack gap={6}>
|
||||
<Box p={4} bg="success-green/10" border borderColor="success-green/30" rounded="md">
|
||||
<Stack direction="row" align="start" gap={3}>
|
||||
<Icon icon={CheckCircle2} size={5} color="var(--color-success)" />
|
||||
<Box>
|
||||
<Text size="sm" color="text-success-green" weight="bold" block>Check your email</Text>
|
||||
<Text size="xs" color="text-gray-400" block mt={1}>{viewData.successMessage}</Text>
|
||||
</Box>
|
||||
</Stack>
|
||||
</Box>
|
||||
<Group direction="column" gap={6} fullWidth>
|
||||
<Group direction="row" align="start" gap={3} fullWidth>
|
||||
<Icon icon={CheckCircle2} size={5} color="var(--color-success)" />
|
||||
<Group direction="column" gap={1}>
|
||||
<Text size="sm" color="text-success-green" weight="bold" block>Check your email</Text>
|
||||
<Text size="xs" color="text-gray-400" block>{viewData.successMessage}</Text>
|
||||
</Group>
|
||||
</Group>
|
||||
|
||||
{viewData.magicLink && (
|
||||
<Box p={3} bg="surface-charcoal" border borderColor="outline-steel" rounded="md">
|
||||
<Text size="xs" color="text-gray-500" block mb={2} weight="bold">DEVELOPMENT MAGIC LINK</Text>
|
||||
<Group direction="column" gap={2} fullWidth>
|
||||
<Text size="xs" color="text-gray-500" block weight="bold">DEVELOPMENT MAGIC LINK</Text>
|
||||
<Link href={viewData.magicLink}>
|
||||
<Text size="xs" color="text-primary-accent" block>
|
||||
{viewData.magicLink}
|
||||
</Text>
|
||||
</Link>
|
||||
</Box>
|
||||
</Group>
|
||||
)}
|
||||
|
||||
<Button
|
||||
@@ -112,7 +107,7 @@ export function ForgotPasswordTemplate({ viewData, formActions, mutationState }:
|
||||
>
|
||||
Return to Login
|
||||
</Button>
|
||||
</Stack>
|
||||
</Group>
|
||||
)}
|
||||
|
||||
<AuthFooterLinks>
|
||||
|
||||
@@ -7,14 +7,14 @@ import { EnhancedFormError } from '@/components/errors/EnhancedFormError';
|
||||
import { FormState } from '@/lib/builders/view-data/types/FormState';
|
||||
import { LoginViewData } from '@/lib/builders/view-data/types/LoginViewData';
|
||||
import { routes } from '@/lib/routing/RouteConfig';
|
||||
import { Box } from '@/ui/Box';
|
||||
import { Button } from '@/ui/Button';
|
||||
import { Checkbox } from '@/ui/Checkbox';
|
||||
import { Group } from '@/ui/Group';
|
||||
import { Icon } from '@/ui/Icon';
|
||||
import { Input } from '@/ui/Input';
|
||||
import { Link } from '@/ui/Link';
|
||||
import { LoadingSpinner } from '@/ui/LoadingSpinner';
|
||||
import { PasswordField } from '@/ui/PasswordField';
|
||||
import { Stack } from '@/ui/Stack';
|
||||
import { Text } from '@/ui/Text';
|
||||
import { AlertCircle, LogIn, Mail } from 'lucide-react';
|
||||
import React from 'react';
|
||||
@@ -43,7 +43,7 @@ export function LoginTemplate({ viewData, formActions, mutationState }: LoginTem
|
||||
description="Sign in to access your racing dashboard"
|
||||
>
|
||||
<AuthForm onSubmit={formActions.handleSubmit}>
|
||||
<Stack gap={4}>
|
||||
<Group direction="column" gap={4} fullWidth>
|
||||
<Input
|
||||
label="Email Address"
|
||||
id="email"
|
||||
@@ -58,7 +58,7 @@ export function LoginTemplate({ viewData, formActions, mutationState }: LoginTem
|
||||
icon={<Mail size={16} />}
|
||||
/>
|
||||
|
||||
<Stack gap={1.5}>
|
||||
<Group direction="column" gap={1.5} fullWidth>
|
||||
<PasswordField
|
||||
label="Password"
|
||||
id="password"
|
||||
@@ -72,50 +72,43 @@ export function LoginTemplate({ viewData, formActions, mutationState }: LoginTem
|
||||
showPassword={viewData.showPassword}
|
||||
onTogglePassword={() => formActions.setShowPassword(!viewData.showPassword)}
|
||||
/>
|
||||
<Box textAlign="right">
|
||||
<Group justify="end" fullWidth>
|
||||
<Link href={routes.auth.forgotPassword}>
|
||||
<Text size="xs" color="text-primary-accent">
|
||||
Forgot password?
|
||||
</Text>
|
||||
</Link>
|
||||
</Box>
|
||||
</Stack>
|
||||
</Group>
|
||||
</Group>
|
||||
|
||||
<Stack direction="row" align="center" gap={2}>
|
||||
<Box
|
||||
as="input"
|
||||
id="rememberMe"
|
||||
name="rememberMe"
|
||||
type="checkbox"
|
||||
rounded="sm"
|
||||
borderColor="outline-steel"
|
||||
bg="surface-charcoal"
|
||||
color="text-primary-accent"
|
||||
ring="focus:ring-primary-accent/50"
|
||||
w="1rem"
|
||||
h="1rem"
|
||||
checked={viewData.formState.fields.rememberMe.value as boolean}
|
||||
onChange={formActions.handleChange}
|
||||
disabled={isSubmitting}
|
||||
/>
|
||||
<Text as="label" htmlFor="rememberMe" size="sm" color="text-med" cursor="pointer">
|
||||
Keep me signed in
|
||||
</Text>
|
||||
</Stack>
|
||||
</Stack>
|
||||
<Checkbox
|
||||
label="Keep me signed in"
|
||||
checked={viewData.formState.fields.rememberMe.value as boolean}
|
||||
onChange={(checked) => {
|
||||
const event = {
|
||||
target: {
|
||||
name: 'rememberMe',
|
||||
value: checked,
|
||||
type: 'checkbox',
|
||||
checked
|
||||
}
|
||||
} as any;
|
||||
formActions.handleChange(event);
|
||||
}}
|
||||
disabled={isSubmitting}
|
||||
/>
|
||||
</Group>
|
||||
|
||||
{viewData.hasInsufficientPermissions && (
|
||||
<Box p={4} bg="warning-amber/10" border borderColor="warning-amber/30" rounded="md">
|
||||
<Stack direction="row" align="start" gap={3}>
|
||||
<Icon icon={AlertCircle} size={5} color="var(--color-warning)" />
|
||||
<Box>
|
||||
<Text weight="bold" color="text-warning-amber" block size="sm">Insufficient Permissions</Text>
|
||||
<Text size="xs" color="text-gray-400" block mt={1}>
|
||||
Please log in with an account that has the required role.
|
||||
</Text>
|
||||
</Box>
|
||||
</Stack>
|
||||
</Box>
|
||||
<Group direction="row" align="start" gap={3} fullWidth>
|
||||
<Icon icon={AlertCircle} size={5} color="var(--color-warning)" />
|
||||
<Group direction="column" gap={1}>
|
||||
<Text weight="bold" color="text-warning-amber" block size="sm">Insufficient Permissions</Text>
|
||||
<Text size="xs" color="text-gray-400" block>
|
||||
Please log in with an account that has the required role.
|
||||
</Text>
|
||||
</Group>
|
||||
</Group>
|
||||
)}
|
||||
|
||||
{viewData.submitError && (
|
||||
@@ -149,14 +142,14 @@ export function LoginTemplate({ viewData, formActions, mutationState }: LoginTem
|
||||
</Link>
|
||||
</Text>
|
||||
|
||||
<Box mt={2}>
|
||||
<Group direction="column" gap={1} align="center" fullWidth>
|
||||
<Text size="xs" color="text-gray-600">
|
||||
By signing in, you agree to our{' '}
|
||||
<Link href="/terms">Terms</Link>
|
||||
{' '}and{' '}
|
||||
<Link href="/privacy">Privacy</Link>
|
||||
</Text>
|
||||
</Box>
|
||||
</Group>
|
||||
</AuthFooterLinks>
|
||||
</AuthCard>
|
||||
);
|
||||
|
||||
@@ -5,13 +5,12 @@ import { AuthFooterLinks } from '@/components/auth/AuthFooterLinks';
|
||||
import { AuthForm } from '@/components/auth/AuthForm';
|
||||
import { ResetPasswordViewData } from '@/lib/builders/view-data/types/ResetPasswordViewData';
|
||||
import { routes } from '@/lib/routing/RouteConfig';
|
||||
import { Box } from '@/ui/Box';
|
||||
import { Button } from '@/ui/Button';
|
||||
import { Group } from '@/ui/Group';
|
||||
import { Icon } from '@/ui/Icon';
|
||||
import { Link } from '@/ui/Link';
|
||||
import { LoadingSpinner } from '@/ui/LoadingSpinner';
|
||||
import { PasswordField } from '@/ui/PasswordField';
|
||||
import { Stack } from '@/ui/Stack';
|
||||
import { Text } from '@/ui/Text';
|
||||
import { AlertCircle, ArrowLeft, CheckCircle2, Shield } from 'lucide-react';
|
||||
import React from 'react';
|
||||
@@ -50,7 +49,7 @@ export function ResetPasswordTemplate({
|
||||
>
|
||||
{!viewData.showSuccess ? (
|
||||
<AuthForm onSubmit={formActions.handleSubmit}>
|
||||
<Stack gap={4}>
|
||||
<Group direction="column" gap={4} fullWidth>
|
||||
<PasswordField
|
||||
label="New Password"
|
||||
id="newPassword"
|
||||
@@ -78,15 +77,13 @@ export function ResetPasswordTemplate({
|
||||
showPassword={uiState.showConfirmPassword}
|
||||
onTogglePassword={() => formActions.setShowConfirmPassword(!uiState.showConfirmPassword)}
|
||||
/>
|
||||
</Stack>
|
||||
</Group>
|
||||
|
||||
{mutationState.error && (
|
||||
<Box p={4} bg="critical-red/10" border borderColor="critical-red/30" rounded="md">
|
||||
<Stack direction="row" align="start" gap={3}>
|
||||
<Icon icon={AlertCircle} size={4.5} color="var(--color-critical)" />
|
||||
<Text size="sm" color="text-critical-red">{mutationState.error}</Text>
|
||||
</Stack>
|
||||
</Box>
|
||||
<Group direction="row" align="start" gap={3} fullWidth>
|
||||
<Icon icon={AlertCircle} size={4.5} color="var(--color-critical)" />
|
||||
<Text size="sm" color="text-critical-red">{mutationState.error}</Text>
|
||||
</Group>
|
||||
)}
|
||||
|
||||
<Button
|
||||
@@ -99,26 +96,24 @@ export function ResetPasswordTemplate({
|
||||
{isSubmitting ? 'Resetting...' : 'Reset Password'}
|
||||
</Button>
|
||||
|
||||
<Box textAlign="center">
|
||||
<Group justify="center" fullWidth>
|
||||
<Link href={routes.auth.login}>
|
||||
<Stack direction="row" align="center" justify="center" gap={2} group>
|
||||
<Icon icon={ArrowLeft} size={3.5} color="var(--color-primary)" groupHoverScale />
|
||||
<Group direction="row" align="center" justify="center" gap={2}>
|
||||
<Icon icon={ArrowLeft} size={3.5} color="var(--color-primary)" />
|
||||
<Text size="sm" weight="bold" color="text-primary-accent">Back to Login</Text>
|
||||
</Stack>
|
||||
</Group>
|
||||
</Link>
|
||||
</Box>
|
||||
</Group>
|
||||
</AuthForm>
|
||||
) : (
|
||||
<Stack gap={6}>
|
||||
<Box p={4} bg="success-green/10" border borderColor="success-green/30" rounded="md">
|
||||
<Stack direction="row" align="start" gap={3}>
|
||||
<Icon icon={CheckCircle2} size={5} color="var(--color-success)" />
|
||||
<Box>
|
||||
<Text size="sm" color="text-success-green" weight="bold" block>Password Reset</Text>
|
||||
<Text size="xs" color="text-gray-400" block mt={1}>{viewData.successMessage}</Text>
|
||||
</Box>
|
||||
</Stack>
|
||||
</Box>
|
||||
<Group direction="column" gap={6} fullWidth>
|
||||
<Group direction="row" align="start" gap={3} fullWidth>
|
||||
<Icon icon={CheckCircle2} size={5} color="var(--color-success)" />
|
||||
<Group direction="column" gap={1}>
|
||||
<Text size="sm" color="text-success-green" weight="bold" block>Password Reset</Text>
|
||||
<Text size="xs" color="text-gray-400" block>{viewData.successMessage}</Text>
|
||||
</Group>
|
||||
</Group>
|
||||
|
||||
<Button
|
||||
type="button"
|
||||
@@ -128,7 +123,7 @@ export function ResetPasswordTemplate({
|
||||
>
|
||||
Return to Login
|
||||
</Button>
|
||||
</Stack>
|
||||
</Group>
|
||||
)}
|
||||
|
||||
<AuthFooterLinks>
|
||||
|
||||
@@ -5,15 +5,16 @@ import { AuthFooterLinks } from '@/components/auth/AuthFooterLinks';
|
||||
import { AuthForm } from '@/components/auth/AuthForm';
|
||||
import { SignupViewData } from '@/lib/builders/view-data/types/SignupViewData';
|
||||
import { checkPasswordStrength } from '@/lib/utils/validation';
|
||||
import { Box } from '@/ui/Box';
|
||||
import { Button } from '@/ui/Button';
|
||||
import { Grid } from '@/ui/Grid';
|
||||
import { Group } from '@/ui/Group';
|
||||
import { Icon } from '@/ui/Icon';
|
||||
import { Input } from '@/ui/Input';
|
||||
import { Link } from '@/ui/Link';
|
||||
import { LoadingSpinner } from '@/ui/LoadingSpinner';
|
||||
import { PasswordField } from '@/ui/PasswordField';
|
||||
import { Stack } from '@/ui/Stack';
|
||||
import { Text } from '@/ui/Text';
|
||||
import { ProgressBar } from '@/ui/ProgressBar';
|
||||
import { AlertCircle, Check, Mail, User, UserPlus, X } from 'lucide-react';
|
||||
import React from 'react';
|
||||
|
||||
@@ -47,16 +48,22 @@ export function SignupTemplate({ viewData, formActions, uiState, mutationState }
|
||||
{ met: /[^a-zA-Z\d]/.test(passwordValue), label: 'Special' },
|
||||
];
|
||||
|
||||
const getStrengthIntent = () => {
|
||||
if (passwordStrength.score <= 2) return 'critical';
|
||||
if (passwordStrength.score <= 4) return 'warning';
|
||||
return 'success';
|
||||
};
|
||||
|
||||
return (
|
||||
<AuthCard
|
||||
title="Create Account"
|
||||
description="Join the GridPilot racing community"
|
||||
>
|
||||
<AuthForm onSubmit={formActions.handleSubmit}>
|
||||
<Stack gap={6}>
|
||||
<Stack gap={4}>
|
||||
<Group direction="column" gap={6} fullWidth>
|
||||
<Group direction="column" gap={4} fullWidth>
|
||||
<Text size="xs" weight="bold" color="text-low" uppercase letterSpacing="wide" block>Personal Information</Text>
|
||||
<Box display="grid" gridCols={{ base: 1, md: 2 }} gap={4}>
|
||||
<Grid cols={{ base: 1, md: 2 }} gap={4}>
|
||||
<Input
|
||||
label="First Name"
|
||||
id="firstName"
|
||||
@@ -81,16 +88,14 @@ export function SignupTemplate({ viewData, formActions, uiState, mutationState }
|
||||
autoComplete="family-name"
|
||||
icon={<User size={16} />}
|
||||
/>
|
||||
</Box>
|
||||
</Grid>
|
||||
|
||||
<Box p={3} bg="warning-amber/5" border borderColor="warning-amber/20" rounded="md">
|
||||
<Stack direction="row" align="start" gap={2}>
|
||||
<Icon icon={AlertCircle} size={3.5} color="var(--color-warning)" mt={0.5} />
|
||||
<Text size="xs" color="text-med">
|
||||
<Text weight="bold" color="text-warning-amber">Note:</Text> Your name cannot be changed after signup.
|
||||
</Text>
|
||||
</Stack>
|
||||
</Box>
|
||||
<Group direction="row" align="start" gap={2} fullWidth>
|
||||
<Icon icon={AlertCircle} size={3.5} color="var(--color-warning)" />
|
||||
<Text size="xs" color="text-med">
|
||||
<Text weight="bold" color="text-warning-amber">Note:</Text> Your name cannot be changed after signup.
|
||||
</Text>
|
||||
</Group>
|
||||
|
||||
<Input
|
||||
label="Email Address"
|
||||
@@ -105,9 +110,9 @@ export function SignupTemplate({ viewData, formActions, uiState, mutationState }
|
||||
autoComplete="email"
|
||||
icon={<Mail size={16} />}
|
||||
/>
|
||||
</Stack>
|
||||
</Group>
|
||||
|
||||
<Stack gap={4}>
|
||||
<Group direction="column" gap={4} fullWidth>
|
||||
<Text size="xs" weight="bold" color="text-low" uppercase letterSpacing="wide" block>Security</Text>
|
||||
<PasswordField
|
||||
label="Password"
|
||||
@@ -124,34 +129,30 @@ export function SignupTemplate({ viewData, formActions, uiState, mutationState }
|
||||
/>
|
||||
|
||||
{passwordValue && (
|
||||
<Stack gap={3}>
|
||||
<Stack direction="row" align="center" gap={2}>
|
||||
<Box flexGrow={1} h="1px" bg="outline-steel" rounded="full" overflow="hidden">
|
||||
<Box
|
||||
h="full"
|
||||
transition
|
||||
w={`${(passwordStrength.score / 5) * 100}%`}
|
||||
bg={
|
||||
passwordStrength.score <= 2 ? 'critical-red' :
|
||||
passwordStrength.score <= 4 ? 'warning-amber' : 'success-green'
|
||||
}
|
||||
<Group direction="column" gap={3} fullWidth>
|
||||
<Group direction="row" align="center" gap={2} fullWidth>
|
||||
<Group fullWidth>
|
||||
<ProgressBar
|
||||
value={(passwordStrength.score / 5) * 100}
|
||||
intent={getStrengthIntent()}
|
||||
size="sm"
|
||||
/>
|
||||
</Box>
|
||||
</Group>
|
||||
<Text size="xs" weight="bold" color="text-low" uppercase>
|
||||
{passwordStrength.label}
|
||||
</Text>
|
||||
</Stack>
|
||||
<Box display="grid" gridCols={2} gap={2}>
|
||||
</Group>
|
||||
<Grid cols={2} gap={2}>
|
||||
{passwordRequirements.map((req, index) => (
|
||||
<Stack key={index} direction="row" align="center" gap={1.5}>
|
||||
<Group key={index} direction="row" align="center" gap={1.5}>
|
||||
<Icon icon={req.met ? Check : X} size={3} color={req.met ? 'var(--color-success)' : 'var(--color-text-low)'} />
|
||||
<Text size="xs" color={req.met ? 'text-med' : 'text-low'}>
|
||||
{req.label}
|
||||
</Text>
|
||||
</Stack>
|
||||
</Group>
|
||||
))}
|
||||
</Box>
|
||||
</Stack>
|
||||
</Grid>
|
||||
</Group>
|
||||
)}
|
||||
|
||||
<PasswordField
|
||||
@@ -167,16 +168,14 @@ export function SignupTemplate({ viewData, formActions, uiState, mutationState }
|
||||
showPassword={uiState.showConfirmPassword}
|
||||
onTogglePassword={() => formActions.setShowConfirmPassword(!uiState.showConfirmPassword)}
|
||||
/>
|
||||
</Stack>
|
||||
</Stack>
|
||||
</Group>
|
||||
</Group>
|
||||
|
||||
{mutationState.error && (
|
||||
<Box p={4} bg="critical-red/10" border borderColor="critical-red/30" rounded="md">
|
||||
<Stack direction="row" align="start" gap={3}>
|
||||
<Icon icon={AlertCircle} size={4.5} color="var(--color-critical)" />
|
||||
<Text size="sm" color="text-critical-red">{mutationState.error}</Text>
|
||||
</Stack>
|
||||
</Box>
|
||||
<Group direction="row" align="start" gap={3} fullWidth>
|
||||
<Icon icon={AlertCircle} size={4.5} color="var(--color-critical)" />
|
||||
<Text size="sm" color="text-critical-red">{mutationState.error}</Text>
|
||||
</Group>
|
||||
)}
|
||||
|
||||
<Button
|
||||
@@ -200,14 +199,14 @@ export function SignupTemplate({ viewData, formActions, uiState, mutationState }
|
||||
</Link>
|
||||
</Text>
|
||||
|
||||
<Box mt={2}>
|
||||
<Group direction="column" gap={1} align="center" fullWidth>
|
||||
<Text size="xs" color="text-gray-600">
|
||||
By creating an account, you agree to our{' '}
|
||||
<Link href="/terms">Terms</Link>
|
||||
{' '}and{' '}
|
||||
<Link href="/privacy">Privacy</Link>
|
||||
</Text>
|
||||
</Box>
|
||||
</Group>
|
||||
</AuthFooterLinks>
|
||||
</AuthCard>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user