login modificado primera version

This commit is contained in:
Abraham
2025-07-15 22:41:40 -07:00
parent 36b5757041
commit 07395137a5
6 changed files with 404 additions and 264 deletions

BIN
assets/images/favicon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

BIN
assets/images/logo_nh.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 277 KiB

BIN
assets/images/logo_nh_b.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 214 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 980 KiB

View File

@@ -2,8 +2,6 @@ import 'package:flutter/material.dart';
import 'package:nethive_neo/models/users/token.dart'; import 'package:nethive_neo/models/users/token.dart';
import 'package:nethive_neo/pages/login_page/widgets/login_form.dart'; import 'package:nethive_neo/pages/login_page/widgets/login_form.dart';
import 'package:nethive_neo/pages/login_page/widgets/right_image.dart';
import 'package:nethive_neo/theme/theme.dart';
class LoginPage extends StatefulWidget { class LoginPage extends StatefulWidget {
const LoginPage({ const LoginPage({
@@ -23,51 +21,169 @@ class _LoginPageState extends State<LoginPage> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Scaffold( return Scaffold(
backgroundColor: AppTheme.of(context).primaryBackground,
key: scaffoldKey, key: scaffoldKey,
body: GestureDetector( body: GestureDetector(
onTap: () => FocusScope.of(context).unfocus(), onTap: () => FocusScope.of(context).unfocus(),
child: LayoutBuilder( child: LayoutBuilder(
builder: (context, constraints) { builder: (context, constraints) {
if (constraints.maxWidth < 600) { if (constraints.maxWidth < 768) {
return Padding( // Vista móvil - fondo degradado completo
padding: const EdgeInsets.all(20), return Container(
child: Container(
width: double.infinity, width: double.infinity,
height: double.infinity, height: double.infinity,
decoration: BoxDecoration( decoration: const BoxDecoration(
color: AppTheme.of(context).primaryBackground, gradient: LinearGradient(
borderRadius: BorderRadius.circular(19), begin: Alignment.centerLeft,
end: Alignment.centerRight,
colors: [
Color(0xFF22C55E), // Verde izquierda
Color(0xFF3B82F6), // Azul centro
Color(0xFF8B5CF6), // Morado derecha
],
),
),
child: SingleChildScrollView(
child: Container(
constraints: BoxConstraints(
minHeight: MediaQuery.of(context).size.height,
),
padding: const EdgeInsets.all(20),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
// Logo NetHive para móvil
Container(
margin: const EdgeInsets.only(bottom: 30),
child: Column(
children: [
Container(
width: 80,
height: 80,
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(16),
),
child: Padding(
padding: const EdgeInsets.all(16),
child: Image.asset(
'assets/images/favicon.png',
fit: BoxFit.contain,
),
),
),
const SizedBox(height: 16),
const Text(
'NetHive',
style: TextStyle(
fontSize: 32,
fontWeight: FontWeight.bold,
color: Colors.white,
),
),
const Text(
'Infrastructure Management',
style: TextStyle(
fontSize: 16,
color: Colors.white70,
),
),
],
),
),
const LoginForm(),
],
), ),
child: Center(
child: LoginForm(),
), ),
), ),
); );
} else { } else {
return Padding( // Vista desktop - columna izquierda sólida, derecha degradado
padding: const EdgeInsets.all(10), return Row(
child: Stack(
children: [ children: [
Container( // Lado izquierdo - Formulario (fondo sólido)
width: double.infinity, Expanded(
height: double.infinity, flex: 1,
decoration: BoxDecoration( child: Container(
color: AppTheme.of(context).primaryBackground, color:
borderRadius: BorderRadius.circular(19), const Color(0xFF1E293B), // Fondo sólido azul oscuro
padding: const EdgeInsets.symmetric(
horizontal: 60, vertical: 40),
child: Center(
child: SingleChildScrollView(
child: ConstrainedBox(
constraints: const BoxConstraints(maxWidth: 400),
child: const LoginForm(),
), ),
), ),
const Positioned(
top: 101,
left: 109,
child: LoginForm(),
), ),
const Positioned( ),
right: 0, ),
child: RightImageWidget(), // Lado derecho - Logo (con degradado)
) Expanded(
flex: 1,
child: Container(
decoration: const BoxDecoration(
gradient: LinearGradient(
begin: Alignment.centerLeft,
end: Alignment.centerRight,
colors: [
Color(0xFF22C55E), // Verde izquierda
Color(0xFF3B82F6), // Azul centro
Color(0xFF8B5CF6), // Morado derecha
], ],
), ),
),
padding: const EdgeInsets.all(60),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
// Logo NetHive
Container(
width: 120,
height: 120,
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(24),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.1),
blurRadius: 20,
offset: const Offset(0, 10),
),
],
),
child: Padding(
padding: const EdgeInsets.all(24),
child: Image.asset(
'assets/images/favicon.png',
fit: BoxFit.contain,
),
),
),
const SizedBox(height: 32),
const Text(
'NetHive',
style: TextStyle(
fontSize: 48,
fontWeight: FontWeight.bold,
color: Colors.white,
),
),
const SizedBox(height: 8),
const Text(
'Infrastructure Management',
style: TextStyle(
fontSize: 18,
color: Colors.white70,
fontWeight: FontWeight.w300,
),
),
],
),
),
),
],
); );
} }
}, },

View File

@@ -9,7 +9,6 @@ import 'package:provider/provider.dart';
import 'package:nethive_neo/helpers/globals.dart'; import 'package:nethive_neo/helpers/globals.dart';
import 'package:nethive_neo/helpers/supabase/queries.dart'; import 'package:nethive_neo/helpers/supabase/queries.dart';
import 'package:nethive_neo/pages/widgets/custom_button.dart';
import 'package:nethive_neo/providers/providers.dart'; import 'package:nethive_neo/providers/providers.dart';
import 'package:nethive_neo/services/api_error_handler.dart'; import 'package:nethive_neo/services/api_error_handler.dart';
import 'package:nethive_neo/theme/theme.dart'; import 'package:nethive_neo/theme/theme.dart';
@@ -28,8 +27,6 @@ class _LoginFormState extends State<LoginForm> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final UserState userState = Provider.of<UserState>(context); final UserState userState = Provider.of<UserState>(context);
double height = MediaQuery.of(context).size.height / 1024;
double width = MediaQuery.of(context).size.width / 1440;
Future<void> login() async { Future<void> login() async {
//Login //Login
@@ -101,41 +98,74 @@ class _LoginFormState extends State<LoginForm> {
} }
} }
return SizedBox( return Container(
width: 521, width: double.infinity,
child: SingleChildScrollView( constraints: const BoxConstraints(maxWidth: 400),
child: Form( child: Form(
key: formKey, key: formKey,
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [ children: [
Center( // Logo NetHive para formulario (solo en desktop)
MediaQuery.of(context).size.width >= 768
? Container(
margin: const EdgeInsets.only(bottom: 40),
child: Row(
children: [
Container(
width: 40,
height: 40,
decoration: BoxDecoration(
color: const Color(0xFF3B82F6),
borderRadius: BorderRadius.circular(8),
),
child: Padding(
padding: const EdgeInsets.all(8),
child: Image.asset( child: Image.asset(
AppTheme.themeMode == ThemeMode.light 'assets/images/favicon.png',
? 'assets/images/logo_lu.jpeg'
: 'assets/images/logo_lu.jpeg',
filterQuality: FilterQuality.high,
fit: BoxFit.contain, fit: BoxFit.contain,
alignment: Alignment.centerLeft, ),
width: width * 200, ),
height: height * 200, ),
const SizedBox(width: 12),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Bienvenido a NetHive',
style: GoogleFonts.inter(
fontSize: 24,
fontWeight: FontWeight.bold,
color: Colors.white,
), ),
), ),
Text( Text(
'Inicie Sesión', 'Plataforma de Gestión de Infraestructura',
textAlign: TextAlign.start, style: GoogleFonts.inter(
style: AppTheme.of(context).title3.override( fontSize: 14,
fontFamily: AppTheme.of(context).title3Family, color: Colors.white70,
color: AppTheme.of(context).primaryColor,
), ),
), ),
const SizedBox(height: 50.32), ],
),
],
),
)
: const SizedBox(),
// Título
Text( Text(
'Correo', 'CORREO ELECTRÓNICO',
style: AppTheme.of(context).bodyText2, style: GoogleFonts.inter(
fontSize: 12,
fontWeight: FontWeight.w600,
color: Colors.white,
letterSpacing: 1.2,
), ),
const SizedBox(height: 20), ),
const SizedBox(height: 8),
// Campo de email
TextFormField( TextFormField(
controller: userState.emailController, controller: userState.emailController,
onFieldSubmitted: (value) async { onFieldSubmitted: (value) async {
@@ -152,18 +182,44 @@ class _LoginFormState extends State<LoginForm> {
} }
return null; return null;
}, },
decoration: _buildInputDecoration('Nombre de usuario'), style: GoogleFonts.inter(
style: AppTheme.of(context).bodyText3.override( color: Colors.white,
fontFamily: AppTheme.of(context).bodyText3Family, fontSize: 16,
color: AppTheme.of(context).primaryText, ),
decoration: InputDecoration(
hintText: 'admin@nethive.com',
hintStyle: GoogleFonts.inter(
color: Colors.white54,
fontSize: 16,
),
filled: true,
fillColor: Colors.white.withOpacity(0.1),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(8),
borderSide: BorderSide.none,
),
contentPadding: const EdgeInsets.symmetric(
horizontal: 16,
vertical: 16,
), ),
), ),
const SizedBox(height: 16),
Text(
'Contraseña',
style: AppTheme.of(context).bodyText2,
), ),
const SizedBox(height: 20), const SizedBox(height: 20),
// Título contraseña
Text(
'CONTRASEÑA',
style: GoogleFonts.inter(
fontSize: 12,
fontWeight: FontWeight.w600,
color: Colors.white,
letterSpacing: 1.2,
),
),
const SizedBox(height: 8),
// Campo de contraseña
TextFormField( TextFormField(
controller: userState.passwordController, controller: userState.passwordController,
obscureText: !passwordVisibility, obscureText: !passwordVisibility,
@@ -179,179 +235,147 @@ class _LoginFormState extends State<LoginForm> {
} }
return null; return null;
}, },
decoration: style: GoogleFonts.inter(
_buildInputDecoration('Contraseña', isPassword: true), color: Colors.white,
style: AppTheme.of(context).bodyText3.override( fontSize: 16,
fontFamily: AppTheme.of(context).bodyText3Family,
color: AppTheme.of(context).primaryText,
), ),
decoration: InputDecoration(
hintText: '••••••',
hintStyle: GoogleFonts.inter(
color: Colors.white54,
fontSize: 16,
), ),
const SizedBox(height: 53), filled: true,
Row( fillColor: Colors.white.withOpacity(0.1),
mainAxisSize: MainAxisSize.max, border: OutlineInputBorder(
crossAxisAlignment: CrossAxisAlignment.center, borderRadius: BorderRadius.circular(8),
children: [ borderSide: BorderSide.none,
Transform.scale(
scale: 1.25,
child: Checkbox(
value: userState.recuerdame,
activeColor: AppTheme.of(context).primaryColor,
onChanged: (value) async {
await userState.updateRecuerdame();
},
splashRadius: 0,
), ),
contentPadding: const EdgeInsets.symmetric(
horizontal: 16,
vertical: 16,
), ),
const SizedBox(width: 18), suffixIcon: IconButton(
InkWell( icon: Icon(
onTap: () async { passwordVisibility
await userState.updateRecuerdame(); ? Icons.visibility_outlined
}, : Icons.visibility_off_outlined,
child: Text( color: Colors.white54,
'Recuerdame',
style: AppTheme.of(context).bodyText3,
), ),
), onPressed: () => setState(
const Spacer(), () => passwordVisibility = !passwordVisibility,
InkWell(
onTap: () {},
child: Text(
'Olvidaste Contraseña?',
style: AppTheme.of(context).bodyText3.override(
fontFamily: AppTheme.of(context).bodyText3Family,
color: AppTheme.of(context).primaryColor,
), ),
), ),
), ),
],
), ),
const SizedBox(height: 20),
CustomButton( const SizedBox(height: 32),
// Botón de iniciar sesión
SizedBox(
width: double.infinity,
height: 50,
child: ElevatedButton(
onPressed: () async { onPressed: () async {
if (!formKey.currentState!.validate()) { if (!formKey.currentState!.validate()) {
return; return;
} }
await login(); await login();
}, },
text: 'Iniciar Sesión', style: ElevatedButton.styleFrom(
options: ButtonOptions( backgroundColor: const Color(0xFF22C55E),
width: double.infinity, shape: RoundedRectangleBorder(
height: 68, borderRadius: BorderRadius.circular(8),
color: AppTheme.of(context).primaryColor,
textStyle: AppTheme.of(context).bodyText2.override(
fontFamily: AppTheme.of(context).bodyText3Family,
color: AppTheme.of(context).primaryBackground,
), ),
borderRadius: BorderRadius.circular(6), elevation: 0,
), ),
),
const SizedBox(height: 68),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
'No registrasdo aun? ',
style: AppTheme.of(context).bodyText2.override(
fontFamily: AppTheme.of(context).bodyText2Family,
color: AppTheme.of(context).alternate,
),
),
InkWell(
onTap: () {},
child: Text( child: Text(
'Registrate', 'INICIAR SESIÓN',
style: AppTheme.of(context).bodyText2.override( style: GoogleFonts.inter(
fontFamily: AppTheme.of(context).bodyText2Family, fontSize: 14,
color: AppTheme.of(context).primaryColor, fontWeight: FontWeight.w600,
color: Colors.white,
letterSpacing: 1.2,
), ),
), ),
), ),
],
), ),
Row(
mainAxisAlignment: MainAxisAlignment.center, const SizedBox(height: 12),
children: [
const Icon( // Enlace "Conexión insegura"
Icons.copyright, Center(
size: 16, child: TextButton(
color: Color(0xFF99B2C6), onPressed: () {},
child: Text(
'Conexión insegura',
style: GoogleFonts.inter(
color: const Color(0xFFEF4444),
fontSize: 13,
decoration: TextDecoration.underline,
decorationColor: const Color(0xFFEF4444),
), ),
const SizedBox(width: 12), ),
),
),
const SizedBox(height: 40),
// Características principales
Text( Text(
'Copyright CB Luna 2024', 'CARACTERÍSTICAS PRINCIPALES',
style: AppTheme.of(context).bodyText3.override( style: GoogleFonts.inter(
fontFamily: AppTheme.of(context).bodyText3Family, fontSize: 12,
color: AppTheme.of(context).alternate, fontWeight: FontWeight.w600,
color: Colors.white,
letterSpacing: 1.2,
), ),
), ),
const SizedBox(height: 16),
// Lista de características
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
_buildFeatureItem('Gestión completa de infraestructura'),
_buildFeatureItem('Monitoreo en tiempo real'),
_buildFeatureItem('Reportes avanzados'),
_buildFeatureItem('Dashboard intuitivo'),
], ],
), ),
], ],
), ),
), ),
),
); );
} }
InputDecoration _buildInputDecoration(String label, Widget _buildFeatureItem(String text) {
{bool isPassword = false}) { return Padding(
Widget? suffixIcon; padding: const EdgeInsets.only(bottom: 8),
if (isPassword) { child: Row(
suffixIcon = InkWell( crossAxisAlignment: CrossAxisAlignment.start,
onTap: () => setState( children: [
() => passwordVisibility = !passwordVisibility, Container(
), margin: const EdgeInsets.only(top: 6, right: 12),
focusNode: FocusNode(skipTraversal: true), width: 6,
child: Icon( height: 6,
passwordVisibility decoration: const BoxDecoration(
? Icons.visibility_outlined color: Color(0xFF22C55E),
: Icons.visibility_off_outlined, shape: BoxShape.circle,
color: const Color(0xFFB8B8B8),
size: 22,
),
);
}
return InputDecoration(
hintText: label,
filled: true,
// isDense: true,
contentPadding: const EdgeInsets.symmetric(horizontal: 27, vertical: 20),
fillColor: AppTheme.of(context).tertiaryBackground,
hintStyle: GoogleFonts.quicksand(
fontWeight: FontWeight.w600,
fontSize: 15,
color: AppTheme.of(context).hintText,
),
enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(6),
borderSide: const BorderSide(
color: Colors.transparent,
width: 1,
), ),
), ),
focusedBorder: OutlineInputBorder( Expanded(
borderRadius: BorderRadius.circular(6), child: Text(
borderSide: const BorderSide( text,
color: Colors.transparent, style: GoogleFonts.inter(
width: 1, color: Colors.white70,
fontSize: 14,
height: 1.5,
), ),
), ),
errorBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(6),
borderSide: const BorderSide(
color: Colors.red,
width: 1,
), ),
],
), ),
prefixIcon: Padding(
padding: const EdgeInsets.only(left: 27, right: 19),
child: Icon(
isPassword ? Icons.lock : Icons.mail_rounded,
size: 24,
color: AppTheme.of(context).hintText,
),
),
suffixIcon: suffixIcon,
); );
} }
} }