|
1 | 1 | import { useState } from 'react'; |
2 | | -import { Button, Input, Label, tokens, Card, Text, mergeClasses } from '@fluentui/react-components'; |
| 2 | +import { Button, Input, Label, tokens, Card, Text } from '@fluentui/react-components'; |
| 3 | +import { PersonRegular, LockClosedRegular } from '@fluentui/react-icons'; |
3 | 4 | import { useNavigate } from 'react-router'; |
4 | 5 | import { useForm } from 'react-hook-form'; |
5 | 6 | import { mainLayoutStyles } from '../styles/Styles'; |
@@ -37,51 +38,114 @@ export default function Login() { |
37 | 38 | }; |
38 | 39 |
|
39 | 40 | return ( |
40 | | - <Card className={mergeClasses(styles.layoutPadding, styles.flexColFit, styles.componentBorder)} style={{ width: '55%' }}> |
41 | | - <h2 className={styles.brand}>Sign in</h2> |
42 | | - <form className={styles.formSection} onSubmit={handleSubmit(onSubmit)}> |
43 | | - <div className={styles.formField}> |
44 | | - <Label htmlFor="userNameOrEmail">Username or Email</Label> |
45 | | - <Input |
46 | | - className={styles.formField} |
47 | | - id="userNameOrEmail" |
48 | | - type="text" |
49 | | - placeholder="username or you@email.com" |
50 | | - autoComplete="username" |
51 | | - {...register('userNameOrEmail', { required: 'Username or Email is required' })} |
52 | | - /> |
53 | | - {errors.userNameOrEmail && ( |
54 | | - <Text as="span" className={styles.errorText}>{errors.userNameOrEmail.message}</Text> |
55 | | - )} |
56 | | - </div> |
57 | | - <div className={styles.formField}> |
58 | | - <Label htmlFor="password">Password</Label> |
59 | | - <Input |
60 | | - className={styles.formField} |
61 | | - id="password" |
62 | | - type="password" |
63 | | - placeholder="Password" |
64 | | - autoComplete="current-password" |
65 | | - {...register('password', { required: 'Password is required' })} |
66 | | - /> |
67 | | - {errors.password && ( |
68 | | - <Text as="span" className={styles.errorText}>{errors.password.message}</Text> |
| 41 | + <Card style={{ |
| 42 | + width: '100%', |
| 43 | + maxWidth: '520px', |
| 44 | + margin: '0 auto', |
| 45 | + padding: `${tokens.spacingVerticalXXL} ${tokens.spacingHorizontalXXXL}` |
| 46 | + }}> |
| 47 | + {/* Header - Visual Hierarchy (HCI Principle) */} |
| 48 | + <div style={{ marginBottom: tokens.spacingVerticalXXL, textAlign: 'center' }}> |
| 49 | + <Text size={800} weight="semibold" block style={{ marginBottom: tokens.spacingVerticalM }}> |
| 50 | + Welcome back |
| 51 | + </Text> |
| 52 | + <Text size={400} style={{ color: tokens.colorNeutralForeground3 }}> |
| 53 | + Sign in to continue to Flowboard |
| 54 | + </Text> |
| 55 | + </div> |
| 56 | + |
| 57 | + <form onSubmit={handleSubmit(onSubmit)}> |
| 58 | + {/* Adequate spacing for readability and reduced cognitive load */} |
| 59 | + <div style={{ display: 'flex', flexDirection: 'column', gap: tokens.spacingVerticalL }}> |
| 60 | + <div className={styles.formField}> |
| 61 | + <Label htmlFor="userNameOrEmail" required size="medium" style={{ marginBottom: tokens.spacingVerticalXS }}> |
| 62 | + Username or Email |
| 63 | + </Label> |
| 64 | + <Input |
| 65 | + id="userNameOrEmail" |
| 66 | + type="text" |
| 67 | + placeholder="Enter your username or email" |
| 68 | + autoComplete="username" |
| 69 | + size="large" |
| 70 | + contentBefore={<PersonRegular />} |
| 71 | + style={{ width: '100%' }} |
| 72 | + {...register('userNameOrEmail', { required: 'Username or Email is required' })} |
| 73 | + /> |
| 74 | + {errors.userNameOrEmail && ( |
| 75 | + <Text className={styles.errorText} style={{ marginTop: tokens.spacingVerticalXS }}> |
| 76 | + {errors.userNameOrEmail.message} |
| 77 | + </Text> |
| 78 | + )} |
| 79 | + </div> |
| 80 | + |
| 81 | + <div className={styles.formField}> |
| 82 | + <Label htmlFor="password" required size="medium" style={{ marginBottom: tokens.spacingVerticalXS }}> |
| 83 | + Password |
| 84 | + </Label> |
| 85 | + <Input |
| 86 | + id="password" |
| 87 | + type="password" |
| 88 | + placeholder="Enter your password" |
| 89 | + autoComplete="current-password" |
| 90 | + size="large" |
| 91 | + contentBefore={<LockClosedRegular />} |
| 92 | + style={{ width: '100%' }} |
| 93 | + {...register('password', { required: 'Password is required' })} |
| 94 | + /> |
| 95 | + {errors.password && ( |
| 96 | + <Text className={styles.errorText} style={{ marginTop: tokens.spacingVerticalXS }}> |
| 97 | + {errors.password.message} |
| 98 | + </Text> |
| 99 | + )} |
| 100 | + </div> |
| 101 | + |
| 102 | + {formError && ( |
| 103 | + <Text className={styles.errorText} style={{ display: 'block', textAlign: 'center' }}> |
| 104 | + {formError} |
| 105 | + </Text> |
69 | 106 | )} |
70 | | - </div> |
71 | | - {formError && ( |
72 | | - <Text as="span" className={styles.errorText} style={{ marginTop: tokens.spacingVerticalXS }}> |
73 | | - {formError} |
74 | | - </Text> |
75 | | - )} |
76 | | - <div className={styles.formSection}> |
77 | | - <Button className={styles.formField} appearance="primary" type="submit" size="large" disabled={loading}> |
| 107 | + |
| 108 | + {/* Fitts's Law - Large clickable target for primary action */} |
| 109 | + <Button |
| 110 | + appearance="primary" |
| 111 | + type="submit" |
| 112 | + size="large" |
| 113 | + disabled={loading} |
| 114 | + style={{ |
| 115 | + width: '100%', |
| 116 | + marginTop: tokens.spacingVerticalM, |
| 117 | + padding: `${tokens.spacingVerticalM} ${tokens.spacingHorizontalL}` |
| 118 | + }} |
| 119 | + > |
78 | 120 | {loading ? 'Signing In...' : 'Sign In'} |
79 | 121 | </Button> |
80 | | - <Button className={styles.formField} appearance="outline" size="large" onClick={() => { navigate("/register") }} type="button"> |
81 | | - Create Account |
82 | | - </Button> |
83 | 122 | </div> |
84 | 123 | </form> |
| 124 | + |
| 125 | + {/* Footer - Clear visual separation and secondary action */} |
| 126 | + <div style={{ |
| 127 | + marginTop: tokens.spacingVerticalXXL, |
| 128 | + paddingTop: tokens.spacingVerticalL, |
| 129 | + borderTop: `1px solid ${tokens.colorNeutralStroke2}`, |
| 130 | + textAlign: 'center', |
| 131 | + display: 'flex', |
| 132 | + alignItems: 'center', |
| 133 | + justifyContent: 'center', |
| 134 | + gap: tokens.spacingHorizontalXS |
| 135 | + }}> |
| 136 | + <Text size={400} style={{ color: tokens.colorNeutralForeground3 }}> |
| 137 | + Don't have an account? |
| 138 | + </Text> |
| 139 | + <Button |
| 140 | + appearance="transparent" |
| 141 | + size="medium" |
| 142 | + onClick={() => navigate('/register')} |
| 143 | + type="button" |
| 144 | + style={{ padding: `0 ${tokens.spacingHorizontalXS}`, minWidth: 'auto', fontWeight: 600 }} |
| 145 | + > |
| 146 | + Create one |
| 147 | + </Button> |
| 148 | + </div> |
85 | 149 | </Card> |
86 | 150 | ); |
87 | 151 | } |
0 commit comments