@@ -4,7 +4,7 @@ import { toTypedSchema } from '@vee-validate/zod'
44import * as z from ' zod'
55import { ref } from ' vue'
66import { useRouter } from ' vue-router'
7- import { Mail , Lock } from ' lucide-vue-next'
7+ import { Mail , Lock , AlertTriangle } from ' lucide-vue-next'
88import { useI18n } from ' vue-i18n'
99
1010import {
@@ -13,6 +13,12 @@ import {
1313 CardFooter ,
1414} from ' @/components/ui/card'
1515
16+ import {
17+ Alert ,
18+ AlertDescription ,
19+ AlertTitle ,
20+ } from ' @/components/ui/alert'
21+
1622import { Button } from ' @/components/ui/button'
1723import {
1824 FormControl ,
@@ -25,6 +31,7 @@ import { Input } from '@/components/ui/input'
2531
2632const router = useRouter ()
2733const isLoading = ref (false )
34+ const errorMessage = ref (' ' )
2835const { t } = useI18n () // Initialize i18n composable
2936
3037// Define validation schema using Zod
@@ -50,16 +57,73 @@ const form = useForm({
5057 },
5158})
5259
53- const onSubmit = form .handleSubmit ((values ) => {
54- // Mock login functionality
60+ // Clear error when user starts typing
61+ const clearError = () => {
62+ errorMessage .value = ' '
63+ }
64+
65+ // Handle different types of errors
66+ const handleError = (error : any ) => {
67+ if (error .name === ' TypeError' && error .message .includes (' fetch' )) {
68+ // Network error - backend is down
69+ errorMessage .value = t (' login.errors.networkError' )
70+ } else if (error .status === 401 ) {
71+ // Unauthorized - invalid credentials
72+ errorMessage .value = t (' login.errors.invalidCredentials' )
73+ } else if (error .status >= 500 ) {
74+ // Server error
75+ errorMessage .value = t (' login.errors.serverError' )
76+ } else if (error .name === ' AbortError' ) {
77+ // Request timeout
78+ errorMessage .value = t (' login.errors.timeout' )
79+ } else {
80+ // Unknown error
81+ errorMessage .value = t (' login.errors.unknownError' )
82+ }
83+ }
84+
85+ const onSubmit = form .handleSubmit (async (values ) => {
5586 isLoading .value = true
87+ errorMessage .value = ' '
88+
89+ try {
90+ // Create AbortController for timeout handling
91+ const controller = new AbortController ()
92+ const timeoutId = setTimeout (() => controller .abort (), 10000 ) // 10 second timeout
93+
94+ const response = await fetch (` ${apiUrl }/auth/login ` , {
95+ method: ' POST' ,
96+ headers: {
97+ ' Content-Type' : ' application/json' ,
98+ },
99+ body: JSON .stringify ({
100+ email: values .email ,
101+ password: values .password ,
102+ }),
103+ signal: controller .signal ,
104+ })
105+
106+ clearTimeout (timeoutId )
107+
108+ if (! response .ok ) {
109+ const errorData = await response .json ().catch (() => ({}))
110+ const error = new Error (errorData .message || ' Login failed' ) as Error & { status: number }
111+ error .status = response .status
112+ throw error
113+ }
56114
57- // Simulate API call
58- setTimeout (() => {
59- console .log (' Login submitted!' , values )
115+ const data = await response .json ()
116+ console .log (' Login successful!' , data )
117+
118+ // Handle successful login - redirect to dashboard or home
119+ router .push (' /dashboard' )
120+
121+ } catch (error ) {
122+ console .error (' Login error:' , error )
123+ handleError (error )
124+ } finally {
60125 isLoading .value = false
61- // In a real app, you would handle authentication here
62- }, 1000 )
126+ }
63127})
64128
65129import { getEnv , getAllEnv } from ' @/utils/env' ;
@@ -87,6 +151,15 @@ const navigateToRegister = () => {
87151 </div >
88152
89153 <div class =" mt-10 sm:mx-auto sm:w-full sm:max-w-sm" >
154+ <!-- Error Alert -->
155+ <Alert v-if =" errorMessage" variant =" destructive" class =" mb-6" >
156+ <AlertTriangle class =" h-4 w-4" />
157+ <AlertTitle >{{ $t('login.errors.title') }}</AlertTitle >
158+ <AlertDescription >
159+ {{ errorMessage }}
160+ </AlertDescription >
161+ </Alert >
162+
90163 <Card >
91164 <CardContent class =" pt-6" >
92165 <form @submit =" onSubmit" class =" space-y-6" >
@@ -102,6 +175,7 @@ const navigateToRegister = () => {
102175 v-bind =" componentField"
103176 class =" pl-10"
104177 autocomplete =" email"
178+ @input =" clearError"
105179 />
106180 </div >
107181 </FormControl >
@@ -128,6 +202,7 @@ const navigateToRegister = () => {
128202 v-bind =" componentField"
129203 class =" pl-10"
130204 autocomplete =" current-password"
205+ @input =" clearError"
131206 />
132207 </div >
133208 </FormControl >
0 commit comments