Skip to content

Commit 7b2a049

Browse files
feat: added google auth button
1 parent 9711222 commit 7b2a049

File tree

7 files changed

+202
-109
lines changed

7 files changed

+202
-109
lines changed

frontend/public/locales/en/translation.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,7 @@
150150
"github": "GitHub",
151151
"medium": "Medium",
152152
"noCardsAdded": "No cards added yet.",
153+
"or": "or",
153154
"password": "Password",
154155
"statistics": "Statistics",
155156
"totalCards": "Total Cards",

frontend/public/locales/es/translation.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,7 @@
150150
"github": "GitHub",
151151
"medium": "Medio",
152152
"noCardsAdded": "No se han agregado tarjetas aún.",
153+
"or": "o",
153154
"password": "Contraseña",
154155
"statistics": "Estadísticas",
155156
"totalCards": "Total de Tarjetas",

frontend/public/locales/nl/translation.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,7 @@
150150
"github": "GitHub",
151151
"medium": "Gemiddeld",
152152
"noCardsAdded": "Nog geen kaarten toegevoegd.",
153+
"or": "of",
153154
"password": "Wachtwoord",
154155
"statistics": "Statistieken",
155156
"totalCards": "Totaal Aantal Kaarten",
Lines changed: 6 additions & 0 deletions
Loading
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import { Button, type ButtonProps, HStack, Image, Text } from '@chakra-ui/react'
2+
import { forwardRef } from 'react'
3+
import GoogleIcon from '@/assets/google-icon.svg'
4+
5+
interface GoogleAuthButtonProps extends ButtonProps {
6+
action: 'login' | 'signup'
7+
}
8+
9+
export const GoogleAuthButton = forwardRef<HTMLButtonElement, GoogleAuthButtonProps>(
10+
({ action, ...props }, ref) => (
11+
<Button
12+
ref={ref}
13+
variant="outline"
14+
width="100%"
15+
boxShadow="rgba(0, 0, 0, 0.12) 0px 1px 1px 0px, var(--chakra-colors-bg-200) 0px 0px 0px 1px, rgba(0, 0, 0, 0.2) 0px 2px 5px 0px"
16+
borderWidth="1px"
17+
borderColor="bg.200"
18+
bg="bg.50"
19+
color="fg.primary"
20+
borderRadius="md"
21+
height="40px"
22+
_hover={{
23+
bg: 'bg.100',
24+
}}
25+
{...props}
26+
>
27+
<HStack spacing={2} width="100%" justifyContent="center">
28+
<Image src={GoogleIcon} alt="Google" boxSize="18px" />
29+
<Text>
30+
{action === 'login' ? 'Continue with Google' : 'Sign up with Google'}
31+
</Text>
32+
</HStack>
33+
</Button>
34+
)
35+
)
36+
37+
GoogleAuthButton.displayName = 'GoogleAuthButton'
Lines changed: 72 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,31 @@
11
import Logo from '@/assets/Logo.svg'
22
import useAuth from '@/hooks/useAuth'
3-
import { Container, Field, Fieldset, Image, Text } from '@chakra-ui/react'
3+
import {
4+
Container,
5+
Field,
6+
Fieldset,
7+
HStack,
8+
Image,
9+
Text,
10+
VStack,
11+
Box,
12+
} from '@chakra-ui/react'
413
import { Link, createFileRoute, redirect } from '@tanstack/react-router'
514
import { type SubmitHandler, useForm } from 'react-hook-form'
615
import { useTranslation } from 'react-i18next'
716
import type { Body_login_login_access_token as AccessToken } from '../../client'
817
import { DefaultButton } from '../../components/commonUI/Button'
18+
import { GoogleAuthButton } from '../../components/commonUI/GoogleAuthButton'
919
import { DefaultInput } from '../../components/commonUI/Input'
1020
import { emailPattern } from '../../utils'
1121

1222
export const Route = createFileRoute('/_publicLayout/login')({
1323
component: Login,
1424
beforeLoad: async () => {
15-
// NOTE: Direct localStorage access is used here because React context is not available in router guards.
16-
// For all React components, use useAuthContext() from './hooks/useAuthContext' instead.
1725
const isGuest = localStorage.getItem('guest_mode') === 'true'
1826
const isLoggedIn = Boolean(localStorage.getItem('access_token')) || isGuest
1927
if (isLoggedIn) {
20-
throw redirect({
21-
to: '/collections',
22-
})
28+
throw redirect({ to: '/collections' })
2329
}
2430
},
2531
})
@@ -42,16 +48,18 @@ function Login() {
4248

4349
const onSubmit: SubmitHandler<AccessToken> = async (data) => {
4450
if (isSubmitting) return
45-
4651
resetError()
47-
4852
try {
4953
await loginMutation.mutateAsync(data)
5054
} catch {
5155
// error is handled by useAuth hook
5256
}
5357
}
5458

59+
const handleGoogleLogin = () => {
60+
console.log('Google login clicked')
61+
}
62+
5563
return (
5664
<Container
5765
h="100dvh"
@@ -72,54 +80,71 @@ function Login() {
7280
cursor="pointer"
7381
/>
7482
</Link>
75-
<form onSubmit={handleSubmit(onSubmit)}>
76-
<Fieldset.Root maxW="sm">
77-
<Fieldset.Content>
78-
<Field.Root>
79-
<Field.Label>{t('general.words.email')}</Field.Label>
80-
<DefaultInput
81-
type="email"
82-
placeholder={t('general.words.email')}
83-
{...register('username', {
84-
required: t('general.errors.usernameIsRequired'),
85-
pattern: emailPattern,
86-
})}
87-
/>
88-
{errors.username && (
89-
<Text color="red.500" fontSize="sm">
90-
{errors.username.message}
91-
</Text>
92-
)}
83+
84+
<VStack gap={2} width="100%">
85+
<form onSubmit={handleSubmit(onSubmit)} style={{ width: '100%' }}>
86+
<Fieldset.Root maxW="sm">
87+
<Fieldset.Content>
9388
<Field.Root>
94-
<Field.Label>{t('general.words.password')}</Field.Label>
89+
<Field.Label>{t('general.words.email')}</Field.Label>
9590
<DefaultInput
96-
type="password"
97-
placeholder={t('general.words.password')}
98-
{...register('password', {
99-
required: t('general.errors.passwordIsRequired'),
91+
type="email"
92+
placeholder={t('general.words.email')}
93+
{...register('username', {
94+
required: t('general.errors.usernameIsRequired'),
95+
pattern: emailPattern,
10096
})}
10197
/>
102-
{error && (
98+
{errors.username && (
10399
<Text color="red.500" fontSize="sm">
104-
{error}
100+
{errors.username.message}
105101
</Text>
106102
)}
103+
<Field.Root>
104+
<Field.Label>{t('general.words.password')}</Field.Label>
105+
<DefaultInput
106+
type="password"
107+
placeholder={t('general.words.password')}
108+
{...register('password', {
109+
required: t('general.errors.passwordIsRequired'),
110+
})}
111+
/>
112+
{error && (
113+
<Text color="red.500" fontSize="sm">
114+
{error}
115+
</Text>
116+
)}
117+
</Field.Root>
107118
</Field.Root>
108-
</Field.Root>
109-
</Fieldset.Content>
110-
<DefaultButton type="submit" loading={isSubmitting} color="fg.primary">
111-
{t('general.actions.login')}
112-
</DefaultButton>
113-
</Fieldset.Root>
114-
</form>
115-
<Text>
116-
{t('routes.publicLayout.login.dontHaveAccount')}{' '}
117-
<Link to="/signup">
118-
<Text as="span" color="blue.500">
119-
{t('general.actions.signUp')}
119+
</Fieldset.Content>
120+
<DefaultButton type="submit" loading={isSubmitting} color="fg.primary" width="100%">
121+
{t('general.actions.login')}
122+
</DefaultButton>
123+
</Fieldset.Root>
124+
</form>
125+
126+
<HStack width="100%" gap={2} my={0.5}>
127+
<Box flex="1" height="1px" bg="bg.200" />
128+
<Text fontSize="sm" color="fg.muted" px={2}>
129+
{t('general.words.or')}
120130
</Text>
121-
</Link>
122-
</Text>
131+
<Box flex="1" height="1px" bg="bg.200" />
132+
</HStack>
133+
134+
<GoogleAuthButton action="login" onClick={handleGoogleLogin} />
135+
136+
137+
</VStack>
138+
<Box pl={0} ml={0}>
139+
<Text textAlign="left" pl={0} ml={0}>
140+
{t('routes.publicLayout.login.dontHaveAccount')}{' '}
141+
<Link to="/signup">
142+
<Text as="span" color="blue.500">
143+
{t('general.actions.signUp')}
144+
</Text>
145+
</Link>
146+
</Text>
147+
</Box>
123148
</Container>
124149
)
125150
}

0 commit comments

Comments
 (0)