refactor power

This commit is contained in:
2026-04-07 11:55:01 +02:00
parent 130780cbb8
commit f4d3ec4bca
12 changed files with 157 additions and 74 deletions

View File

@@ -1,9 +1,8 @@
// lib/ui/auth/auth_screen.dart
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flux/features/auth/bloc/auth_bloc.dart';
import 'package:flux/core/theme/theme.dart';
import 'package:flux/core/widgets/flux_text_field.dart';
import 'package:flux/features/auth/bloc/auth_bloc.dart';
class AuthScreen extends StatefulWidget {
const AuthScreen({super.key});
@@ -15,86 +14,166 @@ class AuthScreen extends StatefulWidget {
class _AuthScreenState extends State<AuthScreen> {
final _emailController = TextEditingController();
final _passwordController = TextEditingController();
final _storeController = TextEditingController();
@override
Widget build(BuildContext context) {
return BlocConsumer<AuthBloc, AuthState>(
listener: (context, state) {
if (state.status == AuthStatus.failure) {
// Mostra l'errore che arriva da Supabase (es. "Invalid login credentials")
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text(state.error!), backgroundColor: Colors.red),
);
}
},
builder: (context, state) {
return Column(
children: [
FluxTextField(
label: 'Email',
icon: Icons.email,
controller: _emailController,
),
FluxTextField(
label: 'Password',
icon: Icons.lock,
isPassword: true,
controller: _passwordController,
),
if (!state.isLoginMode)
FluxTextField(
label: 'Codice Negozio',
icon: Icons.store,
controller: _storeController,
),
ElevatedButton(
onPressed: state.status == AuthStatus.loading
? null
: () {
context.read<AuthBloc>().add(
LoginRequested(
email: _emailController.text.trim(),
password: _passwordController.text.trim(),
storeCode: _storeController.text.trim(),
),
);
},
child: state.status == AuthStatus.loading
? const CircularProgressIndicator()
: Text(state.isLoginMode ? 'ACCEDI' : 'REGISTRATI'),
),
],
);
},
);
}
@override
void dispose() {
_emailController.dispose();
_passwordController.dispose();
_storeController.dispose();
super.dispose();
}
}
class _FluxLogo extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
body: BlocConsumer<AuthBloc, AuthState>(
listener: (context, state) {
if (state.status == AuthStatus.failure) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(state.error ?? 'Errore di autenticazione'),
backgroundColor: Colors.redAccent,
),
);
}
},
builder: (context, state) {
final isLoading = state.status == AuthStatus.loading;
return SafeArea(
child: Center(
child: SingleChildScrollView(
padding: const EdgeInsets.symmetric(horizontal: 32),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
// --- LOGO FLUX ---
_buildLogo(context),
const SizedBox(height: 60),
// --- TITOLO DINAMICO ---
Text(
state.isLoginMode ? 'BENTORNATO' : 'CREA ACCOUNT',
style: TextStyle(
color: context.primaryText,
fontSize: 24,
fontWeight: FontWeight.w900,
letterSpacing: 1.5,
),
),
const SizedBox(height: 8),
Text(
state.isLoginMode
? 'Accedi per gestire il tuo business'
: 'Inizia oggi a digitalizzare il tuo negozio',
textAlign: TextAlign.center,
style: TextStyle(color: context.secondaryText),
),
const SizedBox(height: 40),
// --- CAMPI INPUT ---
FluxTextField(
label: 'Email Aziendale',
icon: Icons.email_outlined,
controller: _emailController,
keyboardType: TextInputType.emailAddress,
),
const SizedBox(height: 20),
FluxTextField(
label: 'Password',
icon: Icons.lock_outline,
isPassword: true,
controller: _passwordController,
),
const SizedBox(height: 40),
// --- BOTTONE PRINCIPALE ---
SizedBox(
width: double.infinity,
height: 56,
child: ElevatedButton(
onPressed: isLoading
? null
: () {
context.read<AuthBloc>().add(
LoginRequested(
email: _emailController.text.trim(),
password: _passwordController.text.trim(),
),
);
},
child: isLoading
? const SizedBox(
height: 24,
width: 24,
child: CircularProgressIndicator(
strokeWidth: 2,
color: Colors.white,
),
)
: Text(state.isLoginMode ? 'ACCEDI' : 'REGISTRATI'),
),
),
// --- SWITCH LOGIN/SIGNUP ---
const SizedBox(height: 24),
TextButton(
onPressed: isLoading
? null
: () {
context.read<AuthBloc>().add(ToggleAuthMode());
},
child: RichText(
text: TextSpan(
text: state.isLoginMode
? "Non hai un account? "
: "Hai già un account? ",
style: TextStyle(color: context.secondaryText),
children: [
TextSpan(
text: state.isLoginMode ? "Registrati" : "Accedi",
style: TextStyle(
color: context.accent,
fontWeight: FontWeight.bold,
),
),
],
),
),
),
],
),
),
),
);
},
),
);
}
Widget _buildLogo(BuildContext context) {
return Column(
children: [
Icon(
Icons.all_inclusive,
size: 80,
color: context.accent,
), // Simbolo Flux/Infinito
Container(
padding: const EdgeInsets.all(20),
decoration: BoxDecoration(
color: context.accent.withValues(alpha: 0.1),
shape: BoxShape.circle,
),
child: Icon(
Icons.all_inclusive_rounded, // Simbolo dell'infinito/Flux
size: 60,
color: context.accent,
),
),
const SizedBox(height: 16),
Text(
'FLUX',
style: Theme.of(context).textTheme.headlineMedium?.copyWith(
style: TextStyle(
color: context.primaryText,
fontSize: 32,
fontWeight: FontWeight.w900,
letterSpacing: 8,
letterSpacing: 10,
),
),
],

View File

@@ -0,0 +1,43 @@
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:equatable/equatable.dart';
import 'package:get_it/get_it.dart';
import 'package:supabase_flutter/supabase_flutter.dart';
part 'company_events.dart';
part 'company_state.dart';
class CompanyBloc extends Bloc<CompanyEvent, CompanyState> {
final _supabase = GetIt.instance<SupabaseClient>();
CompanyBloc() : super(const CompanyState(status: CompanyStatus.initial)) {
on<SaveCompanyRequested>((event, emit) async {
emit(const CompanyState(status: CompanyStatus.loading));
try {
// Recuperiamo l'ID utente corrente da Supabase Auth
final userId = _supabase.auth.currentUser!.id;
await _supabase.from('company').insert({
'user_id': userId,
'ragione_sociale': event.ragioneSociale,
'partita_iva': event.partitaIva,
// Se il CF è vuoto, usa la P.IVA (logica salva-tempo per ditte individuali)
'codice_fiscale': event.codiceFiscale.isEmpty
? event.partitaIva
: event.codiceFiscale,
'codice_univoco': event.codiceUnivoco,
'indirizzo': event.indirizzo,
'cap': event.cap,
'citta': event.citta,
'provincia': event.provincia,
'company_logo': event.companyLogo,
'is_paid': false, // Di default partono con trial/non pagato
});
emit(const CompanyState(status: CompanyStatus.success));
} catch (e) {
emit(CompanyState(status: CompanyStatus.failure, error: e.toString()));
}
});
}
}

View File

@@ -0,0 +1,47 @@
part of 'company_bloc.dart';
// lib/blocs/company/company_event.dart
abstract class CompanyEvent extends Equatable {
const CompanyEvent();
@override
List<Object?> get props => [];
}
class SaveCompanyRequested extends CompanyEvent {
final String ragioneSociale;
final String partitaIva;
final String codiceFiscale;
final String codiceUnivoco;
final String indirizzo;
final String cap;
final String citta;
final String provincia;
final String companyLogo;
const SaveCompanyRequested({
required this.ragioneSociale,
required this.partitaIva,
required this.codiceFiscale,
required this.codiceUnivoco,
required this.indirizzo,
required this.cap,
required this.citta,
required this.provincia,
this.companyLogo = '', // Default vuoto come da schema SQL
});
@override
List<Object?> get props => [
ragioneSociale,
partitaIva,
codiceFiscale,
codiceUnivoco,
indirizzo,
cap,
citta,
provincia,
companyLogo,
];
}

View File

@@ -0,0 +1,13 @@
part of 'company_bloc.dart';
enum CompanyStatus { initial, loading, success, failure }
class CompanyState extends Equatable {
final CompanyStatus status;
final String? error;
const CompanyState({required this.status, this.error});
@override
List<Object?> get props => [status, error];
}

View File

@@ -1,7 +1,7 @@
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flux/blocs/company/company_bloc.dart';
import 'package:flux/blocs/session/session_bloc.dart';
import 'package:flux/features/company/bloc/company_bloc.dart';
import 'package:flux/core/blocs/session/session_bloc.dart';
import 'package:flux/core/theme/theme.dart';
import 'package:flux/core/widgets/flux_text_field.dart';