Skip to content

Commit 5e09ae4

Browse files
Made Frontend. (Still subject to changes)
1 parent f15c389 commit 5e09ae4

25 files changed

+1726
-112
lines changed

frontend/lib/core/constants/colors.dart

Whitespace-only changes.

frontend/lib/core/constants/strings.dart

Whitespace-only changes.
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import 'package:flutter/material.dart';
2+
3+
class CustomTextField extends StatelessWidget {
4+
final TextEditingController controller;
5+
final String labelText;
6+
final bool obscureText;
7+
8+
const CustomTextField({
9+
super.key,
10+
required this.controller,
11+
required this.labelText,
12+
this.obscureText = false,
13+
});
14+
15+
@override
16+
Widget build(BuildContext context) {
17+
return TextField(
18+
controller: controller,
19+
obscureText: obscureText,
20+
decoration: InputDecoration(
21+
labelText: labelText,
22+
border: const OutlineInputBorder(),
23+
),
24+
);
25+
}
26+
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import 'package:flutter/material.dart';
2+
3+
class PrimaryButton extends StatelessWidget {
4+
final String text;
5+
final VoidCallback? onPressed;
6+
7+
const PrimaryButton({
8+
super.key,
9+
required this.text,
10+
required this.onPressed,
11+
});
12+
13+
@override
14+
Widget build(BuildContext context) {
15+
return SizedBox(
16+
width: double.infinity,
17+
child: ElevatedButton(
18+
style: ElevatedButton.styleFrom(
19+
backgroundColor: const Color(0xFF4E88FF),
20+
padding: const EdgeInsets.symmetric(vertical: 16),
21+
),
22+
onPressed: onPressed,
23+
child: Text(
24+
text,
25+
style: const TextStyle(fontSize: 16, fontWeight: FontWeight.bold),
26+
),
27+
),
28+
);
29+
}
30+
}

frontend/lib/core/widgets/section_card.dart

Whitespace-only changes.
Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
import 'package:flutter/foundation.dart';
2+
3+
// Simple User model
4+
class User {
5+
final String? uid;
6+
final String? displayName;
7+
final String? email;
8+
final String? photoURL;
9+
10+
User({this.uid, this.displayName, this.email, this.photoURL});
11+
}
12+
13+
// This is a simplified auth service. In a real app, you would integrate
14+
// with Firebase Auth, your own backend, or another auth provider.
15+
class AuthService extends ChangeNotifier {
16+
User? _currentUser;
17+
18+
User? get currentUser => _currentUser;
19+
20+
// Check if user is logged in
21+
bool get isLoggedIn => _currentUser != null;
22+
23+
// Constructor - initialize with a debug user in debug mode
24+
AuthService() {
25+
// Simulamos un usuario autenticado para desarrollo
26+
if (kDebugMode) {
27+
_currentUser = User(
28+
uid: 'user123',
29+
displayName: 'Usuario de Prueba',
30+
email: 'usuario@example.com',
31+
photoURL: null,
32+
);
33+
notifyListeners();
34+
}
35+
}
36+
37+
// Initialize the auth service and check for existing session
38+
Future<void> initialize() async {
39+
// Here you would check for existing auth tokens in secure storage
40+
// and validate them with your backend
41+
try {
42+
// Skip if we already have a debug user
43+
if (_currentUser != null) return;
44+
45+
// Simulate loading user data
46+
await Future.delayed(const Duration(milliseconds: 500));
47+
48+
// For demo purposes, we'll assume no user is logged in initially
49+
_currentUser = null;
50+
notifyListeners();
51+
} catch (e) {
52+
// Handle initialization error
53+
_currentUser = null;
54+
notifyListeners();
55+
}
56+
}
57+
58+
// Sign in with email and password
59+
Future<User?> signIn(String email, String password) async {
60+
// Here you would make an API call to your auth endpoint
61+
try {
62+
// Simulate API call
63+
await Future.delayed(const Duration(seconds: 1));
64+
65+
// For demo purposes, we'll create a mock user
66+
_currentUser = User(
67+
uid: 'user123',
68+
email: email,
69+
displayName: 'Usuario Autenticado',
70+
photoURL: null,
71+
);
72+
73+
notifyListeners();
74+
return _currentUser;
75+
} catch (e) {
76+
rethrow;
77+
}
78+
}
79+
80+
// Sign up with name, email and password
81+
Future<User?> signUp(String name, String email, String password) async {
82+
try {
83+
// Simulate API call
84+
await Future.delayed(const Duration(seconds: 1));
85+
86+
// For demo purposes, we'll create a mock user
87+
_currentUser = User(
88+
uid: 'newuser456',
89+
email: email,
90+
displayName: name,
91+
photoURL: null,
92+
);
93+
94+
notifyListeners();
95+
return _currentUser;
96+
} catch (e) {
97+
rethrow;
98+
}
99+
}
100+
101+
// Sign out
102+
Future<void> signOut() async {
103+
// Here you would invalidate tokens on your backend
104+
try {
105+
// Simulate API call
106+
await Future.delayed(const Duration(seconds: 1));
107+
108+
_currentUser = null;
109+
notifyListeners();
110+
} catch (e) {
111+
rethrow;
112+
}
113+
}
114+
115+
// Update user profile
116+
Future<void> updateProfile({String? displayName, String? photoURL}) async {
117+
if (_currentUser == null) {
118+
throw Exception('No user is logged in');
119+
}
120+
121+
try {
122+
// Simulate API call
123+
await Future.delayed(const Duration(milliseconds: 500));
124+
125+
_currentUser = User(
126+
uid: _currentUser!.uid,
127+
email: _currentUser!.email,
128+
displayName: displayName ?? _currentUser!.displayName,
129+
photoURL: photoURL ?? _currentUser!.photoURL,
130+
);
131+
132+
notifyListeners();
133+
} catch (e) {
134+
rethrow;
135+
}
136+
}
137+
}
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
import 'package:flutter/material.dart';
2+
import 'package:go_router/go_router.dart';
3+
import '../../../core/widgets/custom_textfield.dart';
4+
import '../../../core/widgets/primary_button.dart';
5+
6+
class LoginScreen extends StatefulWidget {
7+
const LoginScreen({super.key});
8+
9+
@override
10+
State<LoginScreen> createState() => _LoginScreenState();
11+
}
12+
13+
class _LoginScreenState extends State<LoginScreen> {
14+
final _emailController = TextEditingController();
15+
final _passwordController = TextEditingController();
16+
bool _isLoading = false;
17+
String? _error;
18+
19+
void _login() async {
20+
setState(() => _isLoading = true);
21+
// Simulación de login. Aquí va llamada a AuthService
22+
await Future.delayed(const Duration(seconds: 1));
23+
setState(() => _isLoading = false);
24+
25+
if (_emailController.text == 'admin@taskhub.com' &&
26+
_passwordController.text == '123456') {
27+
// Redirigir a Home usando go_router
28+
if (!mounted) return;
29+
context.go('/home');
30+
} else {
31+
setState(() => _error = 'Credenciales incorrectas');
32+
}
33+
}
34+
35+
@override
36+
Widget build(BuildContext context) {
37+
return Scaffold(
38+
body: SafeArea(
39+
child: Padding(
40+
padding: const EdgeInsets.all(24.0),
41+
child: Center(
42+
child: SingleChildScrollView(
43+
child: Column(
44+
mainAxisAlignment: MainAxisAlignment.center,
45+
children: [
46+
const Icon(
47+
Icons.task_alt_rounded,
48+
size: 72,
49+
color: Color(0xFF4E88FF),
50+
),
51+
const SizedBox(height: 24),
52+
CustomTextField(
53+
controller: _emailController,
54+
labelText: 'Correo electrónico',
55+
),
56+
const SizedBox(height: 16),
57+
CustomTextField(
58+
controller: _passwordController,
59+
labelText: 'Contraseña',
60+
obscureText: true,
61+
),
62+
if (_error != null) ...[
63+
const SizedBox(height: 12),
64+
Text(_error!, style: const TextStyle(color: Colors.red)),
65+
],
66+
const SizedBox(height: 24),
67+
PrimaryButton(
68+
text: _isLoading ? 'Cargando...' : 'Iniciar sesión',
69+
onPressed: _isLoading ? null : _login,
70+
),
71+
const SizedBox(height: 16),
72+
TextButton(
73+
onPressed: () => context.go('/register'),
74+
child: const Text('¿No tienes cuenta? Regístrate'),
75+
),
76+
],
77+
),
78+
),
79+
),
80+
),
81+
),
82+
);
83+
}
84+
}
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
import 'package:flutter/material.dart';
2+
import 'package:go_router/go_router.dart';
3+
import '../../../core/widgets/custom_textfield.dart';
4+
import '../../../core/widgets/primary_button.dart';
5+
6+
class RegisterScreen extends StatefulWidget {
7+
const RegisterScreen({super.key});
8+
9+
@override
10+
State<RegisterScreen> createState() => _RegisterScreenState();
11+
}
12+
13+
class _RegisterScreenState extends State<RegisterScreen> {
14+
final _nameController = TextEditingController();
15+
final _emailController = TextEditingController();
16+
final _passwordController = TextEditingController();
17+
final _confirmPasswordController = TextEditingController();
18+
String? _error;
19+
20+
void _register() {
21+
setState(() => _error = null);
22+
if (_passwordController.text != _confirmPasswordController.text) {
23+
setState(() => _error = 'Las contraseñas no coinciden');
24+
return;
25+
}
26+
context.go('/home');
27+
}
28+
29+
@override
30+
Widget build(BuildContext context) {
31+
return Scaffold(
32+
body: SafeArea(
33+
child: Padding(
34+
padding: const EdgeInsets.all(24.0),
35+
child: Center(
36+
child: SingleChildScrollView(
37+
child: Column(
38+
mainAxisAlignment: MainAxisAlignment.center,
39+
children: [
40+
const Icon(
41+
Icons.task_alt_rounded,
42+
size: 72,
43+
color: Color(0xFF4E88FF),
44+
),
45+
const SizedBox(height: 24),
46+
CustomTextField(
47+
controller: _nameController,
48+
labelText: 'Nombre completo',
49+
),
50+
const SizedBox(height: 16),
51+
CustomTextField(
52+
controller: _emailController,
53+
labelText: 'Correo electrónico',
54+
),
55+
const SizedBox(height: 16),
56+
CustomTextField(
57+
controller: _passwordController,
58+
labelText: 'Contraseña',
59+
obscureText: true,
60+
),
61+
const SizedBox(height: 16),
62+
CustomTextField(
63+
controller: _confirmPasswordController,
64+
labelText: 'Confirmar contraseña',
65+
obscureText: true,
66+
),
67+
if (_error != null) ...[
68+
const SizedBox(height: 12),
69+
Text(_error!, style: const TextStyle(color: Colors.red)),
70+
],
71+
const SizedBox(height: 24),
72+
PrimaryButton(text: 'Crear cuenta', onPressed: _register),
73+
const SizedBox(height: 16),
74+
TextButton(
75+
onPressed: () => context.go('/login'),
76+
child: const Text('¿Ya tienes cuenta? Inicia sesión'),
77+
),
78+
],
79+
),
80+
),
81+
),
82+
),
83+
),
84+
);
85+
}
86+
}

0 commit comments

Comments
 (0)