source: src/app/page.tsx@ 4fe4582

Last change on this file since 4fe4582 was 4fe4582, checked in by Stefan-Saveski <stefansaveski19@…>, 7 months ago

Refactor code structure for improved readability and maintainability

  • Property mode set to 100644
File size: 9.6 KB
Line 
1"use client"
2
3import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
4import {
5 faUser,
6 faLock,
7 faEye,
8 faEyeSlash,
9 faSignInAlt,
10 faGraduationCap
11} from '@fortawesome/free-solid-svg-icons';
12import { useState } from 'react';
13import { login } from '@/lib/auth';
14import { useTranslation } from 'react-i18next';
15import LanguageSwitcher from '@/components/LanguageSwitcher';
16
17export default function LoginPage() {
18 const { t } = useTranslation();
19 const [showPassword, setShowPassword] = useState(false);
20 const [isSubmitting, setIsSubmitting] = useState(false);
21 const [errorMessage, setErrorMessage] = useState<string | null>(null);
22 const [formData, setFormData] = useState({
23 email: '',
24 password: ''
25 });
26
27 const handleInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {
28 const { name, value } = e.target;
29 setFormData(prev => ({
30 ...prev,
31 [name]: value
32 }));
33 };
34
35 const handleSubmit = async (e: React.FormEvent) => {
36 e.preventDefault();
37 setErrorMessage(null);
38 setIsSubmitting(true);
39 try {
40 const session = await login({ email: formData.email, password: formData.password });
41 window.location.href = session.role === 'Professor' ? '/professor' : '/students';
42 } catch (err) {
43 setErrorMessage(err instanceof Error ? err.message : 'Login failed.');
44 } finally {
45 setIsSubmitting(false);
46 }
47 };
48
49 return (
50 <div className="container mx-auto px-4">
51 <div className="min-h-screen ">
52 {/* Login Form */}
53 <div className="flex items-center justify-center min-h-[calc(100vh-160px)] p-4">
54 <div className="w-full max-w-5xl">
55 <div className="grid grid-cols-1 gap-6 lg:grid-cols-[24rem_28rem_24rem] lg:items-start lg:justify-center">
56 {/* Sticky Notes */}
57 <div className="w-full flex flex-col gap-4">
58 <div className="bg-yellow-500/10 border border-yellow-500/20 rounded-xl shadow-sm p-5">
59 <div className="text-sm font-bold text-card-foreground mb-2">{t('login_student_title')}</div>
60 <div className="text-xs text-card-foreground whitespace-pre-wrap">
61 {t('login_student_example')}
62 </div>
63 <div className="mt-3 text-xs text-blue-600 font-medium">
64 {t('login_demo_note')}
65 </div>
66 </div>
67
68 <div className="bg-yellow-500/10 border border-yellow-500/20 rounded-xl shadow-sm p-5">
69 <div className="text-sm font-bold text-card-foreground mb-2">{t('login_professor_title')}</div>
70 <div className="text-xs text-card-foreground whitespace-pre-wrap">
71 {t('login_professor_example')}
72 </div>
73 <div className="mt-3 text-xs text-blue-600 font-medium">
74 {t('login_demo_note')}
75 </div>
76 </div>
77 </div>
78
79 {/* Login Card Column */}
80 <div className="w-full max-w-md lg:max-w-none lg:w-[28rem] mx-auto">
81 {/* Login Card */}
82 <div className="bg-card rounded-2xl shadow-xl border border-border overflow-hidden">
83 {/* Header */}
84 <div className="bg-primary text-white px-8 py-6 text-center">
85 <div className="mb-4">
86 <div className="inline-flex items-center justify-center w-16 h-16 bg-white rounded-full shadow-lg">
87 <FontAwesomeIcon icon={faUser} className="text-2xl text-[#0272D1]" />
88 </div>
89 </div>
90 <h1 className="text-2xl font-bold mb-2">{t('login_welcome')}</h1>
91 <p className="text-blue-100 text-sm">
92 {t('login_subtitle')}
93 </p>
94 </div>
95
96 {/* Form */}
97 <div className="p-8">
98 {/* Language Switcher above email field, centered */}
99 <div className="flex justify-center mb-6">
100 <LanguageSwitcher />
101 </div>
102 <form onSubmit={handleSubmit} className="space-y-6">
103 {/* Email Field */}
104 <div>
105 <label htmlFor="email" className="block text-sm font-medium text-muted-foreground mb-2">
106 {t('login_email_label')}
107 </label>
108 <div className="relative">
109 <div className="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none">
110 <FontAwesomeIcon icon={faUser} className="h-4 w-4 text-muted-foreground" />
111 </div>
112 <input
113 id="email"
114 name="email"
115 type="email"
116 required
117 value={formData.email}
118 onChange={handleInputChange}
119 className="w-full pl-10 pr-4 py-3 border border-input rounded-lg focus:outline-none focus:ring-2 focus:ring-primary focus:border-primary transition-colors"
120 placeholder={t('login_email_placeholder')}
121 />
122 </div>
123 </div>
124
125 {/* Password Field */}
126 <div>
127 <label htmlFor="password" className="block text-sm font-medium text-muted-foreground mb-2">
128 {t('login_password_label')}
129 </label>
130 <div className="relative">
131 <div className="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none">
132 <FontAwesomeIcon icon={faLock} className="h-4 w-4 text-muted-foreground" />
133 </div>
134 <input
135 id="password"
136 name="password"
137 type={showPassword ? "text" : "password"}
138 required
139 value={formData.password}
140 onChange={handleInputChange}
141 className="w-full pl-10 pr-12 py-3 border border-input rounded-lg focus:outline-none focus:ring-2 focus:ring-primary focus:border-primary transition-colors"
142 placeholder={t('login_password_placeholder')}
143 />
144 <button
145 type="button"
146 onClick={() => setShowPassword(!showPassword)}
147 className="absolute inset-y-0 right-0 pr-3 flex items-center text-muted-foreground hover:text-foreground transition-colors"
148 >
149 <FontAwesomeIcon
150 icon={showPassword ? faEyeSlash : faEye}
151 className="h-4 w-4"
152 />
153 </button>
154 </div>
155 </div>
156
157 {/* Remember Me & Forgot Password */}
158 <div className="flex items-center justify-between">
159 <div className="flex items-center">
160 <input
161 id="remember"
162 name="remember"
163 type="checkbox"
164 className="h-4 w-4 text-primary focus:ring-primary border-input rounded"
165 />
166 <label htmlFor="remember" className="ml-2 block text-sm text-muted-foreground">
167 {t('login_remember_me')}
168 </label>
169 </div>
170 <button
171 type="button"
172 className="text-sm text-primary hover:text-blue-700 font-medium transition-colors"
173 >
174 {t('login_forgot_password')}
175 </button>
176 </div>
177
178 {errorMessage && (
179 <div className="rounded-lg border border-destructive/20 bg-destructive/10 px-4 py-3 text-sm text-destructive">
180 {t(errorMessage) || errorMessage}
181 </div>
182 )}
183
184 {/* Submit Button */}
185 <button
186 type="submit"
187 disabled={isSubmitting}
188 className="w-full bg-primary hover:bg-blue-700 disabled:opacity-60 disabled:hover:bg-primary text-white font-medium py-3 px-4 rounded-lg transition-colors duration-200 flex items-center justify-center gap-2 shadow-lg hover:shadow-xl"
189 >
190 <FontAwesomeIcon icon={faSignInAlt} className="h-4 w-4" />
191 {isSubmitting ? t('login_button_loading') : t('login_button')}
192 </button>
193 </form>
194 </div>
195
196 {/* Footer */}
197 <div className="bg-accent px-8 py-4 border-t border-border">
198 <p className="text-center text-sm text-muted-foreground">
199 {t('login_no_account')}{' '}
200 <button className="text-primary hover:text-blue-700 font-medium transition-colors">
201 {t('login_contact_admin')}
202 </button>
203 </p>
204 </div>
205 </div>
206
207 {/* Additional Info */}
208 <div className="mt-6 text-center">
209 <div className="inline-flex items-center gap-2 px-4 py-2 bg-card bg-opacity-80 rounded-lg shadow-sm">
210 <FontAwesomeIcon icon={faGraduationCap} className="text-primary" />
211 <span className="text-sm text-muted-foreground">
212 {t('login_footer')}
213 </span>
214 </div>
215 </div>
216 </div>
217
218 {/* Right spacer column (keeps login centered) */}
219 <div className="hidden lg:block" />
220 </div>
221 </div>
222 </div>
223 </div>
224 </div>
225 );
226}
Note: See TracBrowser for help on using the repository browser.