This commit is contained in:
2026-05-08 18:51:28 +02:00
parent 42a9506f02
commit c6ef798b22
7 changed files with 120 additions and 75 deletions

View File

@@ -9,6 +9,7 @@ import 'package:flux/core/widgets/set_password_screen.dart';
import 'package:flux/core/widgets/shared_forms/mobile_upload_screen.dart'; import 'package:flux/core/widgets/shared_forms/mobile_upload_screen.dart';
import 'package:flux/core/widgets/shared_forms/upload_success_screen.dart'; import 'package:flux/core/widgets/shared_forms/upload_success_screen.dart';
import 'package:flux/features/auth/ui/auth_screen.dart'; import 'package:flux/features/auth/ui/auth_screen.dart';
import 'package:flux/features/company/bloc/company_settings_cubit.dart';
import 'package:flux/features/company/ui/company_settings_screen.dart'; import 'package:flux/features/company/ui/company_settings_screen.dart';
import 'package:flux/features/customers/blocs/customers_cubit.dart'; import 'package:flux/features/customers/blocs/customers_cubit.dart';
import 'package:flux/features/customers/models/customer_model.dart'; import 'package:flux/features/customers/models/customer_model.dart';
@@ -30,6 +31,8 @@ import 'package:flux/features/operations/blocs/operation_form_cubit.dart';
import 'package:flux/features/operations/models/operation_model.dart'; import 'package:flux/features/operations/models/operation_model.dart';
import 'package:flux/features/operations/ui/operation_form_screen.dart'; import 'package:flux/features/operations/ui/operation_form_screen.dart';
import 'package:flux/features/operations/ui/operation_list_screen.dart'; import 'package:flux/features/operations/ui/operation_list_screen.dart';
import 'package:flux/features/settings/settings_view.dart';
import 'package:flux/features/settings/theme_settings_view.dart';
import 'package:flux/features/tickets/blocs/ticket_form_cubit.dart'; import 'package:flux/features/tickets/blocs/ticket_form_cubit.dart';
import 'package:flux/features/tickets/blocs/ticket_list_cubit.dart'; import 'package:flux/features/tickets/blocs/ticket_list_cubit.dart';
import 'package:flux/features/tickets/models/ticket_model.dart'; import 'package:flux/features/tickets/models/ticket_model.dart';
@@ -131,8 +134,11 @@ class AppRouter {
}, },
), ),
GoRoute( GoRoute(
path: 'company_settings', path: 'company-settings',
builder: (context, state) => const CompanySettingsScreen(), builder: (context, state) => BlocProvider(
create: (context) => CompanySettingsCubit(),
child: const CompanySettingsScreen(),
),
), ),
GoRoute( GoRoute(
path: 'staff', // Diventa /master-data/staff path: 'staff', // Diventa /master-data/staff
@@ -153,16 +159,13 @@ class AppRouter {
// 3. IMPOSTAZIONI // 3. IMPOSTAZIONI
GoRoute( GoRoute(
path: '/settings', path: '/settings',
builder: (context, state) => Scaffold( builder: (context, state) => const SettingsView(),
appBar: AppBar(title: Text(context.l10n.commonSettings)), routes: [
body: Center( GoRoute(
child: ElevatedButton.icon( path: 'theme',
onPressed: () => context.read<SessionCubit>().signOut(), builder: (context, state) => const ThemeSettingsView(),
icon: const Icon(Icons.logout),
label: const Text("Esci da FLUX"),
),
),
), ),
],
), ),
GoRoute( GoRoute(
path: '/operations', path: '/operations',

View File

@@ -1,4 +1,3 @@
import 'dart:io';
import 'package:equatable/equatable.dart'; import 'package:equatable/equatable.dart';
import 'package:flutter/foundation.dart'; // Per kIsWeb import 'package:flutter/foundation.dart'; // Per kIsWeb
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
@@ -6,7 +5,6 @@ import 'package:flux/core/blocs/session/session_cubit.dart';
import 'package:flux/features/company/data/company_repository.dart'; import 'package:flux/features/company/data/company_repository.dart';
import 'package:flux/features/company/models/company_model.dart'; import 'package:flux/features/company/models/company_model.dart';
import 'package:get_it/get_it.dart'; import 'package:get_it/get_it.dart';
import 'package:image_picker/image_picker.dart';
part 'company_settings_state.dart'; part 'company_settings_state.dart';
@@ -30,9 +28,12 @@ class CompanySettingsCubit extends Cubit<CompanySettingsState> {
void updateFields({ void updateFields({
String? name, String? name,
String? vatId, String? vatId, // Modificato da vatNumber a vatId
String? fiscalCode, // Aggiunto
String? sdi, // Aggiunto
String? address, String? address,
String? city, String? city,
String? province, // Aggiunto
String? zipCode, String? zipCode,
String? phone, String? phone,
String? email, String? email,
@@ -42,8 +43,11 @@ class CompanySettingsCubit extends Cubit<CompanySettingsState> {
final updated = state.company!.copyWith( final updated = state.company!.copyWith(
name: name ?? state.company!.name, name: name ?? state.company!.name,
vatId: vatId ?? state.company!.vatId, vatId: vatId ?? state.company!.vatId,
fiscalCode: fiscalCode ?? state.company!.fiscalCode,
sdi: sdi ?? state.company!.sdi,
address: address ?? state.company!.address, address: address ?? state.company!.address,
city: city ?? state.company!.city, city: city ?? state.company!.city,
province: province ?? state.company!.province,
zipCode: zipCode ?? state.company!.zipCode, zipCode: zipCode ?? state.company!.zipCode,
phone: phone ?? state.company!.phone, phone: phone ?? state.company!.phone,
email: email ?? state.company!.email, email: email ?? state.company!.email,

View File

@@ -1,6 +1,6 @@
part of 'company_settings_cubit.dart'; part of 'company_settings_cubit.dart';
class CompanySettingsState { class CompanySettingsState extends Equatable {
final CompanySettingsStatus status; final CompanySettingsStatus status;
final CompanyModel? company; final CompanyModel? company;
final String? errorMessage; final String? errorMessage;
@@ -22,6 +22,9 @@ class CompanySettingsState {
errorMessage: errorMessage, errorMessage: errorMessage,
); );
} }
@override
List<Object?> get props => [status, company, errorMessage];
} }
enum CompanySettingsStatus { enum CompanySettingsStatus {

View File

@@ -205,7 +205,7 @@ class CompanyModel extends Equatable {
'sdi': sdi, 'sdi': sdi,
'company_logo': logoUrl, 'company_logo': logoUrl,
'phone': phone, 'phone': phone,
'email': 'email', 'email': email,
'is_paid': isPaid, 'is_paid': isPaid,
if (paymentExpiration != null) if (paymentExpiration != null)
'payment_expiration': paymentExpiration!.toIso8601String(), 'payment_expiration': paymentExpiration!.toIso8601String(),

View File

@@ -1,6 +1,7 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flux/features/company/bloc/company_settings_cubit.dart'; import 'package:flux/features/company/bloc/company_settings_cubit.dart';
import 'package:flux/features/company/models/company_model.dart';
import 'package:image_picker/image_picker.dart'; import 'package:image_picker/image_picker.dart';
class CompanySettingsScreen extends StatefulWidget { class CompanySettingsScreen extends StatefulWidget {
@@ -15,8 +16,11 @@ class _CompanySettingsScreenState extends State<CompanySettingsScreen> {
final _nameCtrl = TextEditingController(); final _nameCtrl = TextEditingController();
final _vatCtrl = TextEditingController(); final _vatCtrl = TextEditingController();
final _fiscalCodeCtrl = TextEditingController(); // Nuovo
final _sdiCtrl = TextEditingController(); // Nuovo
final _addressCtrl = TextEditingController(); final _addressCtrl = TextEditingController();
final _cityCtrl = TextEditingController(); final _cityCtrl = TextEditingController();
final _provinceCtrl = TextEditingController(); // Nuovo
final _zipCtrl = TextEditingController(); final _zipCtrl = TextEditingController();
final _phoneCtrl = TextEditingController(); final _phoneCtrl = TextEditingController();
final _emailCtrl = TextEditingController(); final _emailCtrl = TextEditingController();
@@ -26,27 +30,42 @@ class _CompanySettingsScreenState extends State<CompanySettingsScreen> {
@override @override
void initState() { void initState() {
super.initState(); super.initState();
context.read<CompanySettingsCubit>().initSettings(); final cubit = context.read<CompanySettingsCubit>();
cubit.initSettings();
if (cubit.state.status == CompanySettingsStatus.ready &&
cubit.state.company != null) {
_syncControllers(cubit.state.company!);
}
} }
@override @override
void dispose() { void dispose() {
_nameCtrl.dispose(); _nameCtrl.dispose();
_vatCtrl.dispose(); _vatCtrl.dispose();
_fiscalCodeCtrl.dispose(); // Nuovo
_sdiCtrl.dispose(); // Nuovo
_addressCtrl.dispose(); _addressCtrl.dispose();
_cityCtrl.dispose(); _cityCtrl.dispose();
_provinceCtrl.dispose(); // Nuovo
_zipCtrl.dispose(); _zipCtrl.dispose();
_phoneCtrl.dispose(); _phoneCtrl.dispose();
_emailCtrl.dispose(); _emailCtrl.dispose();
super.dispose(); super.dispose();
} }
void _syncControllers(company) { void _syncControllers(CompanyModel company) {
if (_nameCtrl.text.isEmpty) _nameCtrl.text = company.name ?? ''; if (_nameCtrl.text.isEmpty) _nameCtrl.text = company.name;
if (_vatCtrl.text.isEmpty) _vatCtrl.text = company.vatNumber ?? ''; if (_vatCtrl.text.isEmpty) _vatCtrl.text = company.vatId;
if (_addressCtrl.text.isEmpty) _addressCtrl.text = company.address ?? ''; if (_fiscalCodeCtrl.text.isEmpty) {
if (_cityCtrl.text.isEmpty) _cityCtrl.text = company.city ?? ''; _fiscalCodeCtrl.text = company.fiscalCode; // Nuovo
if (_zipCtrl.text.isEmpty) _zipCtrl.text = company.zipCode ?? ''; }
if (_sdiCtrl.text.isEmpty) _sdiCtrl.text = company.sdi; // Nuovo
if (_provinceCtrl.text.isEmpty) {
_provinceCtrl.text = company.province; // Nuovo
}
if (_addressCtrl.text.isEmpty) _addressCtrl.text = company.address;
if (_cityCtrl.text.isEmpty) _cityCtrl.text = company.city;
if (_zipCtrl.text.isEmpty) _zipCtrl.text = company.zipCode;
if (_phoneCtrl.text.isEmpty) _phoneCtrl.text = company.phone ?? ''; if (_phoneCtrl.text.isEmpty) _phoneCtrl.text = company.phone ?? '';
if (_emailCtrl.text.isEmpty) _emailCtrl.text = company.email ?? ''; if (_emailCtrl.text.isEmpty) _emailCtrl.text = company.email ?? '';
_isInitialized = true; _isInitialized = true;
@@ -56,6 +75,9 @@ class _CompanySettingsScreenState extends State<CompanySettingsScreen> {
context.read<CompanySettingsCubit>().updateFields( context.read<CompanySettingsCubit>().updateFields(
name: _nameCtrl.text, name: _nameCtrl.text,
vatId: _vatCtrl.text, vatId: _vatCtrl.text,
fiscalCode: _fiscalCodeCtrl.text, // Nuovo
sdi: _sdiCtrl.text, // Nuovo
province: _provinceCtrl.text,
address: _addressCtrl.text, address: _addressCtrl.text,
city: _cityCtrl.text, city: _cityCtrl.text,
zipCode: _zipCtrl.text, zipCode: _zipCtrl.text,
@@ -174,11 +196,7 @@ class _CompanySettingsScreenState extends State<CompanySettingsScreen> {
), ),
const Divider(), const Divider(),
const SizedBox(height: 16), const SizedBox(height: 16),
Row( TextFormField(
children: [
Expanded(
flex: 2,
child: TextFormField(
controller: _nameCtrl, controller: _nameCtrl,
decoration: const InputDecoration( decoration: const InputDecoration(
labelText: 'Ragione Sociale', labelText: 'Ragione Sociale',
@@ -188,18 +206,36 @@ class _CompanySettingsScreenState extends State<CompanySettingsScreen> {
? 'Campo obbligatorio' ? 'Campo obbligatorio'
: null, : null,
), ),
), const SizedBox(height: 16),
const SizedBox(width: 16), Row(
children: [
Expanded( Expanded(
flex: 1,
child: TextFormField( child: TextFormField(
controller: _vatCtrl, controller: _vatCtrl,
decoration: const InputDecoration( decoration: const InputDecoration(
labelText: 'Partita IVA / C.F.', labelText: 'Partita IVA',
prefixIcon: Icon(Icons.receipt_long), prefixIcon: Icon(Icons.receipt_long),
), ),
), ),
), ),
const SizedBox(width: 16),
Expanded(
child: TextFormField(
controller: _fiscalCodeCtrl,
decoration: const InputDecoration(
labelText: 'Codice Fiscale',
),
),
),
const SizedBox(width: 16),
Expanded(
child: TextFormField(
controller: _sdiCtrl,
decoration: const InputDecoration(
labelText: 'Codice SDI',
),
),
),
], ],
), ),
const SizedBox(height: 16), const SizedBox(height: 16),
@@ -233,6 +269,16 @@ class _CompanySettingsScreenState extends State<CompanySettingsScreen> {
), ),
), ),
const SizedBox(width: 16), const SizedBox(width: 16),
Expanded(
flex: 1,
child: TextFormField(
controller: _provinceCtrl,
decoration: const InputDecoration(
labelText: 'Provincia (es. MI)',
),
),
),
const SizedBox(width: 16),
Expanded( Expanded(
flex: 1, flex: 1,
child: TextFormField( child: TextFormField(

View File

@@ -1,7 +1,9 @@
// lib/ui/impostazioni/impostazioni_view.dart // lib/ui/impostazioni/impostazioni_view.dart
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flux/core/blocs/session/session_cubit.dart';
import 'package:flux/core/theme/theme.dart'; import 'package:flux/core/theme/theme.dart';
import 'package:flux/features/settings/theme_settings_view.dart'; import 'package:go_router/go_router.dart';
class SettingsView extends StatelessWidget { class SettingsView extends StatelessWidget {
const SettingsView({super.key}); const SettingsView({super.key});
@@ -15,48 +17,33 @@ class SettingsView extends StatelessWidget {
children: [ children: [
_settingsSection('Account', [ _settingsSection('Account', [
_settingsTile( _settingsTile(
Icons.person, icon: Icons.person,
'Profilo Utente', title: 'Profilo Utente',
'Configura i tuoi dati', subtitle: 'Configura i tuoi dati',
context, context: context,
MaterialPageRoute( onTap: () {},
builder: (context) => const ThemeSettingsView(),
),
), ),
_settingsTile( _settingsTile(
Icons.store, title: 'Impostazioni Azienda',
'Mio Negozio', icon: Icons.business,
'Piacenza Centro', subtitle: 'Configura i dati aziendali',
context, context: context,
MaterialPageRoute( onTap: () => context.push('/master-data/company-settings'),
builder: (context) => const ThemeSettingsView(),
),
), ),
]), ]),
const SizedBox(height: 16), const SizedBox(height: 16),
_settingsSection('Applicazione', [ _settingsSection('Applicazione', [
_settingsTile( _settingsTile(
Icons.sync, icon: Icons.dark_mode,
'Sincronizzazione', title: 'Tema (FLUX Dark)',
'Ultima: 5 min fa', subtitle: 'Configurazione visiva',
context, context: context,
MaterialPageRoute( onTap: () => context.push('/settings/theme'),
builder: (context) => const ThemeSettingsView(),
),
),
_settingsTile(
Icons.dark_mode,
'Tema (FLUX Dark)',
'Configurazione visiva',
context,
MaterialPageRoute(
builder: (context) => const ThemeSettingsView(),
),
), ),
]), ]),
const SizedBox(height: 24), const SizedBox(height: 24),
TextButton.icon( TextButton.icon(
onPressed: () {}, onPressed: () => context.read<SessionCubit>().signOut(),
icon: const Icon(Icons.exit_to_app, color: Colors.red), icon: const Icon(Icons.exit_to_app, color: Colors.red),
label: const Text('Logout', style: TextStyle(color: Colors.red)), label: const Text('Logout', style: TextStyle(color: Colors.red)),
), ),
@@ -83,22 +70,22 @@ class SettingsView extends StatelessWidget {
); );
} }
Widget _settingsTile( Widget _settingsTile({
IconData icon, required BuildContext context,
String title, required IconData icon,
String subtitle, required String title,
BuildContext context, String? subtitle,
MaterialPageRoute route, required VoidCallback onTap,
) { }) {
return ListTile( return ListTile(
leading: Icon(icon, color: FluxColors.primaryBlue), leading: Icon(icon, color: FluxColors.primaryBlue),
title: Text(title, style: Theme.of(context).textTheme.titleLarge), title: Text(title, style: Theme.of(context).textTheme.titleLarge),
subtitle: Text(subtitle), subtitle: Text(subtitle ?? ''),
trailing: Icon( trailing: Icon(
Icons.chevron_right, Icons.chevron_right,
color: Theme.of(context).textTheme.bodyMedium?.color, color: Theme.of(context).textTheme.bodyMedium?.color,
), ),
onTap: () => Navigator.of(context).push(route), onTap: onTap,
); );
} }
} }

View File

@@ -6,6 +6,7 @@ import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_dotenv/flutter_dotenv.dart'; import 'package:flutter_dotenv/flutter_dotenv.dart';
import 'package:flux/features/attachments/data/attachments_repository.dart'; import 'package:flux/features/attachments/data/attachments_repository.dart';
import 'package:flux/features/auth/bloc/auth_cubit.dart'; import 'package:flux/features/auth/bloc/auth_cubit.dart';
import 'package:flux/features/company/data/company_repository.dart';
import 'package:flux/features/operations/blocs/operation_list_cubit.dart'; import 'package:flux/features/operations/blocs/operation_list_cubit.dart';
import 'package:flux/features/operations/data/operations_repository.dart'; import 'package:flux/features/operations/data/operations_repository.dart';
import 'package:flux/features/tickets/data/ticket_repository.dart'; import 'package:flux/features/tickets/data/ticket_repository.dart';
@@ -106,6 +107,7 @@ Future<void> setupLocator() async {
getIt.registerSingleton<SessionCubit>( getIt.registerSingleton<SessionCubit>(
SessionCubit(getIt<CoreRepository>(), getIt<SharedPreferences>()), SessionCubit(getIt<CoreRepository>(), getIt<SharedPreferences>()),
); );
getIt.registerLazySingleton<CompanyRepository>(() => CompanyRepository());
} }
class FluxApp extends StatefulWidget { class FluxApp extends StatefulWidget {