diff --git a/lib/core/routes/app_router.dart b/lib/core/routes/app_router.dart index 2008f95..2a942e8 100644 --- a/lib/core/routes/app_router.dart +++ b/lib/core/routes/app_router.dart @@ -12,6 +12,7 @@ import 'package:flux/features/customers/models/customer_model.dart'; import 'package:flux/features/customers/ui/customer_detail_screen.dart'; import 'package:flux/features/customers/ui/customer_mobile_upload_screen.dart'; import 'package:flux/features/customers/ui/customers_content.dart'; +import 'package:flux/features/home/latest_store_services/bloc/latest_store_services_bloc.dart'; import 'package:flux/features/home/ui/home_screen.dart'; import 'package:flux/features/master_data/master_data_hub_content.dart'; import 'package:flux/features/master_data/products/ui/products_screen.dart'; @@ -24,6 +25,7 @@ import 'package:flux/features/services/blocs/service_files_bloc.dart'; import 'package:flux/features/services/models/service_model.dart'; import 'package:flux/features/services/ui/service_form_screen/service_form_screen.dart'; import 'package:flux/features/services/ui/service_form_screen/service_mobile_upload_screen.dart'; +import 'package:flux/features/services/ui/services_screen.dart'; import 'package:get_it/get_it.dart'; import 'package:go_router/go_router.dart'; @@ -128,15 +130,19 @@ class AppRouter { ), ), ), + GoRoute( + path: '/services', + builder: (context, state) => const ServicesScreen(), + ), + GoRoute( + path: '/customers', + builder: (context, state) => + const CustomersContent(), // O come si chiama il tuo widget della lista! + ), ], ), // --- DETTAGLI E OPERATIVITÀ (FUORI DALLA SHELL - TUTTO SCHERMO) --- - GoRoute( - path: '/customers', - builder: (context, state) => - const CustomersContent(), // O come si chiama il tuo widget della lista! - ), GoRoute( path: '/customer/:id', builder: (context, state) { diff --git a/lib/features/home/latest_store_services/bloc/latest_store_services_bloc.dart b/lib/features/home/latest_store_services/bloc/latest_store_services_bloc.dart index 2593922..9cd5b94 100644 --- a/lib/features/home/latest_store_services/bloc/latest_store_services_bloc.dart +++ b/lib/features/home/latest_store_services/bloc/latest_store_services_bloc.dart @@ -1,3 +1,5 @@ +import 'dart:developer'; + import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:equatable/equatable.dart'; import 'package:flux/features/services/data/services_repository.dart'; @@ -17,19 +19,38 @@ class LatestStoreServicesBloc status: LatestStoreServicesStatus.initial, ), ) { - on((event, emit) async { + on((event, emit) async { emit(state.copyWith(status: LatestStoreServicesStatus.loading)); - try { + // 1. Creiamo uno stream "intermedio" che idrata i dati + final hydratedStream = _repository + .getLastStoreServicesStream(storeId: event.storeId, limit: 5) + .asyncMap((List rawServices) async { + // Questo gira ad ogni "scatto" dello stream di Supabase + List fullyHydratedServices = []; + + for (ServiceModel service in rawServices) { + // Peschiamo i dati completi (incluso il cliente) + ServiceModel fullService = await _repository.fetchServiceById( + service.id!, + ); + fullyHydratedServices.add(fullService); + } + + // Passiamo la lista completa allo step successivo + return fullyHydratedServices; + }); + + // 2. Ora passiamo lo stream idratato all'emit.forEach await emit.forEach( - _repository.getLastStoreServicesStream( - storeId: event.storeId, - limit: 5, - ), - onData: (List data) => state.copyWith( - status: LatestStoreServicesStatus.success, - services: data, - ), + hydratedStream, // Usiamo lo stream modificato! + onData: (List fullyHydratedServices) { + // Qui ora è tutto sincrono e bellissimo + return state.copyWith( + services: fullyHydratedServices, + status: LatestStoreServicesStatus.success, + ); + }, onError: (error, stackTrace) => state.copyWith( status: LatestStoreServicesStatus.failure, error: error.toString(), diff --git a/lib/features/home/latest_store_services/bloc/latest_store_services_events.dart b/lib/features/home/latest_store_services/bloc/latest_store_services_events.dart index 9550022..b66128d 100644 --- a/lib/features/home/latest_store_services/bloc/latest_store_services_events.dart +++ b/lib/features/home/latest_store_services/bloc/latest_store_services_events.dart @@ -7,10 +7,10 @@ sealed class LatestStoreServicesEvent extends Equatable { List get props => []; } -class InitLastServicesEvent extends LatestStoreServicesEvent { +class InitLastStoreServicesEvent extends LatestStoreServicesEvent { final String storeId; - const InitLastServicesEvent(this.storeId); + const InitLastStoreServicesEvent(this.storeId); @override List get props => [storeId]; diff --git a/lib/features/home/latest_store_services/ui/latest_store_services_card.dart b/lib/features/home/latest_store_services/ui/latest_store_services_card.dart index 3fe63b2..368b7dc 100644 --- a/lib/features/home/latest_store_services/ui/latest_store_services_card.dart +++ b/lib/features/home/latest_store_services/ui/latest_store_services_card.dart @@ -2,7 +2,9 @@ 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/utils/extensions.dart'; import 'package:flux/features/home/latest_store_services/bloc/latest_store_services_bloc.dart'; +import 'package:go_router/go_router.dart'; class LatestStoreServicesCard extends StatelessWidget { const LatestStoreServicesCard({super.key}); @@ -15,7 +17,7 @@ class LatestStoreServicesCard extends StatelessWidget { // 1. Creiamo il Bloc e facciamo partire subito la query create: (context) => LatestStoreServicesBloc() - ..add(InitLastServicesEvent(currentStoreId ?? '')), + ..add(InitLastStoreServicesEvent(currentStoreId ?? '')), child: BlocListener( // 2. MAGIA: Se l'utente cambia negozio dalla barra in alto, riavviamo lo stream! listenWhen: (previous, current) => @@ -23,7 +25,7 @@ class LatestStoreServicesCard extends StatelessWidget { listener: (context, state) { if (state.currentStore?.id != null) { context.read().add( - InitLastServicesEvent(state.currentStore!.id!), + InitLastStoreServicesEvent(state.currentStore!.id!), ); } }, @@ -67,15 +69,18 @@ class _LatestServicesCardContent extends StatelessWidget { ), const SizedBox(width: 12), Expanded( - child: Text( - "Ultimi Servizi", - style: TextStyle( - fontWeight: FontWeight.bold, - fontSize: 16, - color: context.primaryText, + child: TextButton( + onPressed: () => context.push('/services'), + child: Text( + context.l10n.homeLatestServices, + style: TextStyle( + fontWeight: FontWeight.bold, + fontSize: 16, + color: context.primaryText, + ), + maxLines: 1, + overflow: TextOverflow.ellipsis, ), - maxLines: 1, - overflow: TextOverflow.ellipsis, ), ), ], @@ -120,31 +125,46 @@ class _LatestServicesCardContent extends StatelessWidget { ), itemBuilder: (context, index) { final service = state.services[index]; - return Padding( - padding: const EdgeInsets.symmetric(vertical: 8.0), - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Expanded( - child: Text( - service.number, - style: TextStyle( - fontWeight: FontWeight.w600, - color: context.primaryText, + return InkWell( + onTap: () => + context.push('/service-form', extra: service), + child: Padding( + padding: const EdgeInsets.symmetric(vertical: 8.0), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Expanded( + flex: 5, + child: Text( + service.customerDisplayName ?? + 'Cliente sconosciuto', + style: TextStyle( + fontWeight: FontWeight.w700, + color: context.primaryText, + ), ), - maxLines: 1, - overflow: TextOverflow.ellipsis, ), - ), - // Se hai formattato la data, stampala qui (es. 12/04/2026) - Text( - "${service.createdAt?.day}/${service.createdAt?.month}", - style: TextStyle( - color: context.secondaryText, - fontSize: 12, + Expanded( + flex: 5, + child: Text( + service.number, + style: TextStyle( + fontWeight: FontWeight.w600, + color: context.primaryText, + ), + maxLines: 1, + overflow: TextOverflow.ellipsis, + ), ), - ), - ], + Text( + "${service.createdAt?.day}/${service.createdAt?.month}", + style: TextStyle( + color: context.secondaryText, + fontSize: 12, + ), + ), + ], + ), ), ); }, diff --git a/lib/features/home/ui/home_screen.dart b/lib/features/home/ui/home_screen.dart index 0d73cfa..91ab08c 100644 --- a/lib/features/home/ui/home_screen.dart +++ b/lib/features/home/ui/home_screen.dart @@ -3,6 +3,7 @@ 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/utils/extensions.dart'; +import 'package:flux/features/home/latest_store_services/ui/latest_store_services_card.dart'; import 'package:flux/features/home/ui/quick_actions_widget.dart'; import 'package:flux/features/master_data/staff/blocs/staff_cubit.dart'; import 'package:go_router/go_router.dart'; @@ -75,12 +76,8 @@ class HomeScreen extends StatelessWidget { color: Colors.green, context: context, ), - _buildDashboardWidget( - title: context.l10n.homeLatestServices, - icon: Icons.design_services_outlined, - color: Colors.blue, - context: context, - ), + LatestStoreServicesCard(), + _buildDashboardWidget( title: context.l10n.homeLatestServiceTickets, icon: Icons.support_agent_outlined, diff --git a/lib/l10n/app_en.arb b/lib/l10n/app_en.arb new file mode 100644 index 0000000..84c90f5 --- /dev/null +++ b/lib/l10n/app_en.arb @@ -0,0 +1,13 @@ +{ + "@@locale": "en", + "welcomeBack": "Welcome back, {name}! 👋", + "latestServices": "Latest Services", + "masterData": "Master Data", + "settings": "Settings", + "newService": "Service", + "expiring_contracts": "Expiring Contracts", + "sticky_notes": "Sticky Notes", + "my_tasks": "My Tasks", + "latest_service_tickets": "Latest service tickets" + +} \ No newline at end of file diff --git a/lib/l10n/app_localizations_en.dart b/lib/l10n/app_localizations_en.dart new file mode 100644 index 0000000..eeebff4 --- /dev/null +++ b/lib/l10n/app_localizations_en.dart @@ -0,0 +1,57 @@ +// ignore: unused_import +import 'package:intl/intl.dart' as intl; +import 'app_localizations.dart'; + +// ignore_for_file: type=lint + +/// The translations for English (`en`). +class AppLocalizationsEn extends AppLocalizations { + AppLocalizationsEn([String locale = 'en']) : super(locale); + + @override + String get homeExpiringContracts => 'Contratti in scadenza'; + + @override + String get homeLatestServiceTickets => 'Ultime assistenze'; + + @override + String get homeLatestServices => 'Ultimi Servizi'; + + @override + String get homeMasterData => 'Anagrafiche'; + + @override + String get homeMyTasks => 'Mie Attività'; + + @override + String get homeNewService => 'Servizio'; + + @override + String get homeSettings => 'Impostazioni'; + + @override + String get homeStickyNotes => 'Sticky Notes'; + + @override + String homeWelcomeBack(String name) { + return 'Bentornato, $name! 👋'; + } + + @override + String get homeNoStoreFound => 'Nessun negozio trovato'; + + @override + String get homeNewServiceTicket => 'Nuova assistenza'; + + @override + String get homeNewNote => 'Nota'; + + @override + String get homeNewTask => 'Attività'; + + @override + String get commonComingSoon => 'Coming soon'; + + @override + String get commonDashboard => 'Panoramica'; +} diff --git a/lib/l10n/intl_en.arb b/lib/l10n/intl_en.arb new file mode 100644 index 0000000..9e26dfe --- /dev/null +++ b/lib/l10n/intl_en.arb @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/lib/main.dart b/lib/main.dart index d8b2451..4130cd2 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -5,6 +5,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_dotenv/flutter_dotenv.dart'; import 'package:flux/features/auth/bloc/auth_cubit.dart'; +import 'package:flux/features/home/latest_store_services/bloc/latest_store_services_bloc.dart'; import 'package:flux/l10n/app_localizations.dart'; import 'package:get_it/get_it.dart'; import 'package:go_router/go_router.dart';