autoreplace service operation
This commit is contained in:
@@ -2,4 +2,4 @@ distributionBase=GRADLE_USER_HOME
|
|||||||
distributionPath=wrapper/dists
|
distributionPath=wrapper/dists
|
||||||
zipStoreBase=GRADLE_USER_HOME
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
zipStorePath=wrapper/dists
|
zipStorePath=wrapper/dists
|
||||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.14-all.zip
|
distributionUrl=https\://operations.gradle.org/distributions/gradle-8.14-all.zip
|
||||||
|
|||||||
@@ -131,7 +131,7 @@ class AppRouter {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
GoRoute(
|
GoRoute(
|
||||||
path: '/services',
|
path: '/operations',
|
||||||
builder: (context, state) => const ServicesScreen(),
|
builder: (context, state) => const ServicesScreen(),
|
||||||
),
|
),
|
||||||
GoRoute(
|
GoRoute(
|
||||||
@@ -168,8 +168,8 @@ class AppRouter {
|
|||||||
},
|
},
|
||||||
),
|
),
|
||||||
GoRoute(
|
GoRoute(
|
||||||
path: '/service-form',
|
path: '/operation-form',
|
||||||
name: 'service-form',
|
name: 'operation-form',
|
||||||
builder: (context, state) {
|
builder: (context, state) {
|
||||||
final existingService = state.extra as ServiceModel?;
|
final existingService = state.extra as ServiceModel?;
|
||||||
final serviceId = state.uri.queryParameters['serviceId'];
|
final serviceId = state.uri.queryParameters['serviceId'];
|
||||||
@@ -184,7 +184,7 @@ class AppRouter {
|
|||||||
},
|
},
|
||||||
),
|
),
|
||||||
GoRoute(
|
GoRoute(
|
||||||
path: '/service/:id/upload',
|
path: '/operation/:id/upload',
|
||||||
builder: (context, state) {
|
builder: (context, state) {
|
||||||
final serviceId = state.pathParameters['id']!;
|
final serviceId = state.pathParameters['id']!;
|
||||||
final serviceName = state.uri.queryParameters['name'] ?? 'Pratica';
|
final serviceName = state.uri.queryParameters['name'] ?? 'Pratica';
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
// lib/ui/common/flux_text_field.dart
|
// lib/ui/common/flux_text_field.dart
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/operations.dart';
|
||||||
import 'package:flux/core/theme/theme.dart';
|
import 'package:flux/core/theme/theme.dart';
|
||||||
|
|
||||||
class FluxTextField extends StatefulWidget {
|
class FluxTextField extends StatefulWidget {
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import 'package:flutter_bloc/flutter_bloc.dart';
|
|||||||
import 'package:flux/features/customers/blocs/customer_cubit.dart';
|
import 'package:flux/features/customers/blocs/customer_cubit.dart';
|
||||||
import 'package:flux/features/customers/models/customer_model.dart';
|
import 'package:flux/features/customers/models/customer_model.dart';
|
||||||
import 'package:flux/features/customers/ui/quick_customer_dialog.dart';
|
import 'package:flux/features/customers/ui/quick_customer_dialog.dart';
|
||||||
import 'package:flux/features/operations/blocs/services_cubit.dart';
|
import 'package:flux/features/operations/blocs/operations_cubit.dart';
|
||||||
|
|
||||||
class CustomerSearchSheet extends StatefulWidget {
|
class CustomerSearchSheet extends StatefulWidget {
|
||||||
const CustomerSearchSheet({super.key});
|
const CustomerSearchSheet({super.key});
|
||||||
|
|||||||
@@ -29,10 +29,10 @@ class LatestStoreServicesBloc
|
|||||||
// Questo gira ad ogni "scatto" dello stream di Supabase
|
// Questo gira ad ogni "scatto" dello stream di Supabase
|
||||||
List<ServiceModel> fullyHydratedServices = [];
|
List<ServiceModel> fullyHydratedServices = [];
|
||||||
|
|
||||||
for (ServiceModel service in rawServices) {
|
for (ServiceModel operation in rawServices) {
|
||||||
// Peschiamo i dati completi (incluso il cliente)
|
// Peschiamo i dati completi (incluso il cliente)
|
||||||
ServiceModel fullService = await _repository.fetchServiceById(
|
ServiceModel fullService = await _repository.fetchServiceById(
|
||||||
service.id!,
|
operation.id!,
|
||||||
);
|
);
|
||||||
fullyHydratedServices.add(fullService);
|
fullyHydratedServices.add(fullService);
|
||||||
}
|
}
|
||||||
@@ -47,7 +47,7 @@ class LatestStoreServicesBloc
|
|||||||
onData: (List<ServiceModel> fullyHydratedServices) {
|
onData: (List<ServiceModel> fullyHydratedServices) {
|
||||||
// Qui ora è tutto sincrono e bellissimo
|
// Qui ora è tutto sincrono e bellissimo
|
||||||
return state.copyWith(
|
return state.copyWith(
|
||||||
services: fullyHydratedServices,
|
operations: fullyHydratedServices,
|
||||||
status: LatestStoreServicesStatus.success,
|
status: LatestStoreServicesStatus.success,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -5,26 +5,26 @@ enum LatestStoreServicesStatus { initial, loading, success, failure }
|
|||||||
class LatestStoreServicesState extends Equatable {
|
class LatestStoreServicesState extends Equatable {
|
||||||
final LatestStoreServicesStatus status;
|
final LatestStoreServicesStatus status;
|
||||||
final String? error;
|
final String? error;
|
||||||
final List<ServiceModel> services;
|
final List<ServiceModel> operations;
|
||||||
|
|
||||||
const LatestStoreServicesState({
|
const LatestStoreServicesState({
|
||||||
required this.status,
|
required this.status,
|
||||||
this.error,
|
this.error,
|
||||||
this.services = const [],
|
this.operations = const [],
|
||||||
});
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
List<Object?> get props => [status, error, services];
|
List<Object?> get props => [status, error, operations];
|
||||||
|
|
||||||
LatestStoreServicesState copyWith({
|
LatestStoreServicesState copyWith({
|
||||||
LatestStoreServicesStatus? status,
|
LatestStoreServicesStatus? status,
|
||||||
String? error,
|
String? error,
|
||||||
List<ServiceModel>? services,
|
List<ServiceModel>? operations,
|
||||||
}) {
|
}) {
|
||||||
return LatestStoreServicesState(
|
return LatestStoreServicesState(
|
||||||
status: status ?? this.status,
|
status: status ?? this.status,
|
||||||
error: error,
|
error: error,
|
||||||
services: services ?? this.services,
|
operations: operations ?? this.operations,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -70,7 +70,7 @@ class _LatestServicesCardContent extends StatelessWidget {
|
|||||||
const SizedBox(width: 12),
|
const SizedBox(width: 12),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: TextButton(
|
child: TextButton(
|
||||||
onPressed: () => context.push('/services'),
|
onPressed: () => context.push('/operations'),
|
||||||
child: Text(
|
child: Text(
|
||||||
context.l10n.homeLatestServices,
|
context.l10n.homeLatestServices,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
@@ -105,7 +105,7 @@ class _LatestServicesCardContent extends StatelessWidget {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (state.services.isEmpty) {
|
if (state.operations.isEmpty) {
|
||||||
return Center(
|
return Center(
|
||||||
child: Text(
|
child: Text(
|
||||||
"Nessun servizio recente.",
|
"Nessun servizio recente.",
|
||||||
@@ -118,16 +118,16 @@ class _LatestServicesCardContent extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return ListView.separated(
|
return ListView.separated(
|
||||||
itemCount: state.services.length,
|
itemCount: state.operations.length,
|
||||||
separatorBuilder: (context, index) => Divider(
|
separatorBuilder: (context, index) => Divider(
|
||||||
height: 1,
|
height: 1,
|
||||||
color: theme.dividerColor.withValues(alpha: 0.3),
|
color: theme.dividerColor.withValues(alpha: 0.3),
|
||||||
),
|
),
|
||||||
itemBuilder: (context, index) {
|
itemBuilder: (context, index) {
|
||||||
final service = state.services[index];
|
final operation = state.operations[index];
|
||||||
return InkWell(
|
return InkWell(
|
||||||
onTap: () =>
|
onTap: () =>
|
||||||
context.push('/service-form', extra: service),
|
context.push('/operation-form', extra: operation),
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: const EdgeInsets.symmetric(vertical: 8.0),
|
padding: const EdgeInsets.symmetric(vertical: 8.0),
|
||||||
child: Row(
|
child: Row(
|
||||||
@@ -136,7 +136,7 @@ class _LatestServicesCardContent extends StatelessWidget {
|
|||||||
Expanded(
|
Expanded(
|
||||||
flex: 5,
|
flex: 5,
|
||||||
child: Text(
|
child: Text(
|
||||||
service.customerDisplayName ??
|
operation.customerDisplayName ??
|
||||||
'Cliente sconosciuto',
|
'Cliente sconosciuto',
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontWeight: FontWeight.w700,
|
fontWeight: FontWeight.w700,
|
||||||
@@ -147,7 +147,7 @@ class _LatestServicesCardContent extends StatelessWidget {
|
|||||||
Expanded(
|
Expanded(
|
||||||
flex: 5,
|
flex: 5,
|
||||||
child: Text(
|
child: Text(
|
||||||
service.number,
|
operation.number,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontWeight: FontWeight.w600,
|
fontWeight: FontWeight.w600,
|
||||||
color: context.primaryText,
|
color: context.primaryText,
|
||||||
@@ -157,7 +157,7 @@ class _LatestServicesCardContent extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
Text(
|
Text(
|
||||||
"${service.createdAt?.day}/${service.createdAt?.month}",
|
"${operation.createdAt?.day}/${operation.createdAt?.month}",
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
color: context.secondaryText,
|
color: context.secondaryText,
|
||||||
fontSize: 12,
|
fontSize: 12,
|
||||||
|
|||||||
@@ -185,7 +185,7 @@ class HomeScreen extends StatelessWidget {
|
|||||||
color: Colors.blue,
|
color: Colors.blue,
|
||||||
onTap: () {
|
onTap: () {
|
||||||
// Entriamo nel form! Nessun parametro extra = Nuovo Servizio
|
// Entriamo nel form! Nessun parametro extra = Nuovo Servizio
|
||||||
context.push('/service-form');
|
context.push('/operation-form');
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
const SizedBox(width: 12),
|
const SizedBox(width: 12),
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/services.dart'; // <-- IMPORTANTE per i formatter
|
import 'package:flutter/operations.dart'; // <-- IMPORTANTE per i formatter
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
import 'package:flux/core/widgets/flux_text_field.dart';
|
import 'package:flux/core/widgets/flux_text_field.dart';
|
||||||
import 'package:flux/features/master_data/store/models/store_model.dart';
|
import 'package:flux/features/master_data/store/models/store_model.dart';
|
||||||
|
|||||||
@@ -17,11 +17,11 @@ class ServiceSavedEvent extends ServiceFilesEvent {
|
|||||||
|
|
||||||
class LoadServiceFilesEvent extends ServiceFilesEvent {
|
class LoadServiceFilesEvent extends ServiceFilesEvent {
|
||||||
final String? serviceId;
|
final String? serviceId;
|
||||||
final ServiceModel? service;
|
final ServiceModel? operation;
|
||||||
const LoadServiceFilesEvent({this.serviceId, this.service});
|
const LoadServiceFilesEvent({this.serviceId, this.operation});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
List<Object?> get props => [serviceId, service];
|
List<Object?> get props => [serviceId, operation];
|
||||||
}
|
}
|
||||||
|
|
||||||
class AddServiceFilesEvent extends ServiceFilesEvent {
|
class AddServiceFilesEvent extends ServiceFilesEvent {
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
part of 'services_cubit.dart';
|
part of 'operations_cubit.dart';
|
||||||
|
|
||||||
enum ServicesStatus {
|
enum ServicesStatus {
|
||||||
initial,
|
initial,
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ class ServicesRepository {
|
|||||||
Future<ServiceModel> fetchServiceById(String id) async {
|
Future<ServiceModel> fetchServiceById(String id) async {
|
||||||
try {
|
try {
|
||||||
final response = await _supabase
|
final response = await _supabase
|
||||||
.from('service')
|
.from('operation')
|
||||||
.select('''
|
.select('''
|
||||||
*,
|
*,
|
||||||
customer(nome),
|
customer(nome),
|
||||||
@@ -47,7 +47,7 @@ class ServicesRepository {
|
|||||||
try {
|
try {
|
||||||
// Nota: 'customer(name, surname)' serve per il display name nella card
|
// Nota: 'customer(name, surname)' serve per il display name nella card
|
||||||
var query = _supabase
|
var query = _supabase
|
||||||
.from('service')
|
.from('operation')
|
||||||
.select('''
|
.select('''
|
||||||
*,
|
*,
|
||||||
customer(nome),
|
customer(nome),
|
||||||
@@ -89,7 +89,7 @@ class ServicesRepository {
|
|||||||
required int limit,
|
required int limit,
|
||||||
}) {
|
}) {
|
||||||
return _supabase
|
return _supabase
|
||||||
.from('service')
|
.from('operation')
|
||||||
.stream(primaryKey: ['id'])
|
.stream(primaryKey: ['id'])
|
||||||
.eq('store_id', storeId)
|
.eq('store_id', storeId)
|
||||||
.order('created_at', ascending: false)
|
.order('created_at', ascending: false)
|
||||||
@@ -101,12 +101,12 @@ class ServicesRepository {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// --- SALVATAGGIO COMPLETO (PRIMA PADRE, POI FIGLI) ---
|
// --- SALVATAGGIO COMPLETO (PRIMA PADRE, POI FIGLI) ---
|
||||||
Future<ServiceModel> saveFullService(ServiceModel service) async {
|
Future<ServiceModel> saveFullService(ServiceModel operation) async {
|
||||||
try {
|
try {
|
||||||
// 1. Upsert del record principale
|
// 1. Upsert del record principale
|
||||||
final serviceData = await _supabase
|
final serviceData = await _supabase
|
||||||
.from('service')
|
.from('operation')
|
||||||
.upsert(service.toMap())
|
.upsert(operation.toMap())
|
||||||
.select()
|
.select()
|
||||||
.single();
|
.single();
|
||||||
|
|
||||||
@@ -114,7 +114,7 @@ class ServicesRepository {
|
|||||||
|
|
||||||
// 2. MODIFICA: Pulizia atomica dei figli
|
// 2. MODIFICA: Pulizia atomica dei figli
|
||||||
// Se stiamo modificando (id != null), resettiamo le tabelle collegate
|
// Se stiamo modificando (id != null), resettiamo le tabelle collegate
|
||||||
if (service.id != null) {
|
if (operation.id != null) {
|
||||||
await Future.wait([
|
await Future.wait([
|
||||||
_supabase.from('energy_service').delete().eq('service_id', newId),
|
_supabase.from('energy_service').delete().eq('service_id', newId),
|
||||||
_supabase.from('fin_service').delete().eq('service_id', newId),
|
_supabase.from('fin_service').delete().eq('service_id', newId),
|
||||||
@@ -129,36 +129,36 @@ class ServicesRepository {
|
|||||||
// 3. Inserimento dei moduli in parallelo per velocità
|
// 3. Inserimento dei moduli in parallelo per velocità
|
||||||
final List<Future> insertTasks = [];
|
final List<Future> insertTasks = [];
|
||||||
|
|
||||||
if (service.energyServices.isNotEmpty) {
|
if (operation.energyServices.isNotEmpty) {
|
||||||
insertTasks.add(
|
insertTasks.add(
|
||||||
_supabase
|
_supabase
|
||||||
.from('energy_service')
|
.from('energy_service')
|
||||||
.insert(
|
.insert(
|
||||||
service.energyServices
|
operation.energyServices
|
||||||
.map((item) => item.copyWith(serviceId: newId).toMap())
|
.map((item) => item.copyWith(serviceId: newId).toMap())
|
||||||
.toList(),
|
.toList(),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (service.finServices.isNotEmpty) {
|
if (operation.finServices.isNotEmpty) {
|
||||||
insertTasks.add(
|
insertTasks.add(
|
||||||
_supabase
|
_supabase
|
||||||
.from('fin_service')
|
.from('fin_service')
|
||||||
.insert(
|
.insert(
|
||||||
service.finServices
|
operation.finServices
|
||||||
.map((item) => item.copyWith(serviceId: newId).toMap())
|
.map((item) => item.copyWith(serviceId: newId).toMap())
|
||||||
.toList(),
|
.toList(),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (service.entertainmentServices.isNotEmpty) {
|
if (operation.entertainmentServices.isNotEmpty) {
|
||||||
insertTasks.add(
|
insertTasks.add(
|
||||||
_supabase
|
_supabase
|
||||||
.from('entertainment_service')
|
.from('entertainment_service')
|
||||||
.insert(
|
.insert(
|
||||||
service.entertainmentServices
|
operation.entertainmentServices
|
||||||
.map((item) => item.copyWith(serviceId: newId).toMap())
|
.map((item) => item.copyWith(serviceId: newId).toMap())
|
||||||
.toList(),
|
.toList(),
|
||||||
),
|
),
|
||||||
@@ -171,7 +171,7 @@ class ServicesRepository {
|
|||||||
|
|
||||||
// 4. UPLOAD DEI FILE LOCALI (Nuovi)
|
// 4. UPLOAD DEI FILE LOCALI (Nuovi)
|
||||||
// Filtriamo solo i file che non hanno ancora un ID (quindi sono locali)
|
// Filtriamo solo i file che non hanno ancora un ID (quindi sono locali)
|
||||||
final localFilesToUpload = service.files
|
final localFilesToUpload = operation.files
|
||||||
.where((f) => f.id == null)
|
.where((f) => f.id == null)
|
||||||
.toList();
|
.toList();
|
||||||
|
|
||||||
@@ -180,7 +180,7 @@ class ServicesRepository {
|
|||||||
|
|
||||||
for (var file in localFilesToUpload) {
|
for (var file in localFilesToUpload) {
|
||||||
final storagePath =
|
final storagePath =
|
||||||
'$companyId/services/$newId/${DateTime.now().millisecondsSinceEpoch}_${file.name}.${file.extension}';
|
'$companyId/operations/$newId/${DateTime.now().millisecondsSinceEpoch}_${file.name}.${file.extension}';
|
||||||
final String mimeType = file.extension.toLowerCase() == 'pdf'
|
final String mimeType = file.extension.toLowerCase() == 'pdf'
|
||||||
? 'application/pdf'
|
? 'application/pdf'
|
||||||
: 'image/${file.extension}';
|
: 'image/${file.extension}';
|
||||||
@@ -216,7 +216,7 @@ class ServicesRepository {
|
|||||||
// Interroghiamo Supabase per farci restituire la pratica con TUTTI gli ID generati
|
// Interroghiamo Supabase per farci restituire la pratica con TUTTI gli ID generati
|
||||||
// (inclusi quelli della tabella service_file appena inseriti)
|
// (inclusi quelli della tabella service_file appena inseriti)
|
||||||
final updatedServiceData = await _supabase
|
final updatedServiceData = await _supabase
|
||||||
.from('service')
|
.from('operation')
|
||||||
.select('''
|
.select('''
|
||||||
*,
|
*,
|
||||||
energy_service(*),
|
energy_service(*),
|
||||||
@@ -237,7 +237,7 @@ class ServicesRepository {
|
|||||||
// --- ELIMINAZIONE ---
|
// --- ELIMINAZIONE ---
|
||||||
Future<void> deleteService(String id) async {
|
Future<void> deleteService(String id) async {
|
||||||
try {
|
try {
|
||||||
await _supabase.from('service').delete().eq('id', id);
|
await _supabase.from('operation').delete().eq('id', id);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
throw Exception('Errore durante l\'eliminazione: $e');
|
throw Exception('Errore durante l\'eliminazione: $e');
|
||||||
}
|
}
|
||||||
@@ -247,11 +247,11 @@ class ServicesRepository {
|
|||||||
Future<List<String>> fetchTopEntertainmentTypes(String companyId) async {
|
Future<List<String>> fetchTopEntertainmentTypes(String companyId) async {
|
||||||
try {
|
try {
|
||||||
// Cerchiamo i tipi più frequenti associati ai servizi di questa company
|
// Cerchiamo i tipi più frequenti associati ai servizi di questa company
|
||||||
// Nota: dobbiamo passare attraverso la tabella 'service' per filtrare per company_id
|
// Nota: dobbiamo passare attraverso la tabella 'operation' per filtrare per company_id
|
||||||
final response = await _supabase
|
final response = await _supabase
|
||||||
.from('entertainment_service')
|
.from('entertainment_service')
|
||||||
.select('type, service!inner(store!inner(company_id))')
|
.select('type, operation!inner(store!inner(company_id))')
|
||||||
.eq('service.store.company_id', companyId)
|
.eq('operation.store.company_id', companyId)
|
||||||
.limit(100); // Prendiamo un campione
|
.limit(100); // Prendiamo un campione
|
||||||
|
|
||||||
// Logica rapida per contare le occorrenze e prendere i primi 5
|
// Logica rapida per contare le occorrenze e prendere i primi 5
|
||||||
@@ -297,7 +297,7 @@ class ServicesRepository {
|
|||||||
'_',
|
'_',
|
||||||
);
|
);
|
||||||
final storagePath =
|
final storagePath =
|
||||||
'$companyId/services/$serviceId/${DateTime.now().millisecondsSinceEpoch}_$cleanFileName';
|
'$companyId/operations/$serviceId/${DateTime.now().millisecondsSinceEpoch}_$cleanFileName';
|
||||||
final int fileSize = pickedFile.size;
|
final int fileSize = pickedFile.size;
|
||||||
final fileToSave = ServiceFileModel(
|
final fileToSave = ServiceFileModel(
|
||||||
serviceId: serviceId,
|
serviceId: serviceId,
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import 'package:flux/core/widgets/image_viewer_widget.dart';
|
|||||||
import 'package:flux/core/widgets/pdf_viewer_widget.dart';
|
import 'package:flux/core/widgets/pdf_viewer_widget.dart';
|
||||||
import 'package:flux/core/widgets/qr_upload_dialog.dart';
|
import 'package:flux/core/widgets/qr_upload_dialog.dart';
|
||||||
import 'package:flux/features/operations/blocs/service_files_bloc.dart';
|
import 'package:flux/features/operations/blocs/service_files_bloc.dart';
|
||||||
import 'package:flux/features/operations/blocs/services_cubit.dart';
|
import 'package:flux/features/operations/blocs/operations_cubit.dart';
|
||||||
import 'package:flux/features/operations/models/service_file_model.dart';
|
import 'package:flux/features/operations/models/service_file_model.dart';
|
||||||
|
|
||||||
class AttachmentsSection extends StatelessWidget {
|
class AttachmentsSection extends StatelessWidget {
|
||||||
@@ -310,7 +310,7 @@ class AttachmentsSection extends StatelessWidget {
|
|||||||
},
|
},
|
||||||
child: QrUploadDialog(
|
child: QrUploadDialog(
|
||||||
deepLinkUrl:
|
deepLinkUrl:
|
||||||
'fluxapp:///service/${currentService!.id}/upload?name=${Uri.encodeComponent(nomePratica)}',
|
'fluxapp:///operation/${currentService!.id}/upload?name=${Uri.encodeComponent(nomePratica)}',
|
||||||
title: 'Scatta per\n$nomePratica',
|
title: 'Scatta per\n$nomePratica',
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -3,9 +3,9 @@ import 'package:flux/features/customers/ui/customer_search_sheet.dart';
|
|||||||
import 'package:flux/features/operations/models/service_model.dart';
|
import 'package:flux/features/operations/models/service_model.dart';
|
||||||
|
|
||||||
class CustomerSection extends StatelessWidget {
|
class CustomerSection extends StatelessWidget {
|
||||||
final ServiceModel service;
|
final ServiceModel operation;
|
||||||
|
|
||||||
const CustomerSection({super.key, required this.service});
|
const CustomerSection({super.key, required this.operation});
|
||||||
|
|
||||||
void _openCustomerSearch(BuildContext context) {
|
void _openCustomerSearch(BuildContext context) {
|
||||||
showModalBottomSheet(
|
showModalBottomSheet(
|
||||||
@@ -28,8 +28,8 @@ class CustomerSection extends StatelessWidget {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
// Niente BlocBuilder qui! Leggiamo solo la variabile 'service'
|
// Niente BlocBuilder qui! Leggiamo solo la variabile 'operation'
|
||||||
final hasCustomer = service.customerId != null;
|
final hasCustomer = operation.customerId != null;
|
||||||
|
|
||||||
return Card(
|
return Card(
|
||||||
elevation: 2,
|
elevation: 2,
|
||||||
@@ -74,7 +74,7 @@ class CustomerSection extends StatelessWidget {
|
|||||||
children: [
|
children: [
|
||||||
Expanded(
|
Expanded(
|
||||||
child: Text(
|
child: Text(
|
||||||
service.customerDisplayName ?? "Cliente Selezionato",
|
operation.customerDisplayName ?? "Cliente Selezionato",
|
||||||
style: const TextStyle(
|
style: const TextStyle(
|
||||||
fontSize: 16,
|
fontSize: 16,
|
||||||
fontWeight: FontWeight.bold,
|
fontWeight: FontWeight.bold,
|
||||||
|
|||||||
@@ -63,7 +63,7 @@ class _EnergyServiceDialogState extends State<EnergyServiceDialog> {
|
|||||||
},
|
},
|
||||||
)
|
)
|
||||||
: _EnergyList(
|
: _EnergyList(
|
||||||
services: _tempList,
|
operations: _tempList,
|
||||||
onDelete: (index) {
|
onDelete: (index) {
|
||||||
setState(() => _tempList.removeAt(index));
|
setState(() => _tempList.removeAt(index));
|
||||||
},
|
},
|
||||||
@@ -101,14 +101,14 @@ class _EnergyServiceDialogState extends State<EnergyServiceDialog> {
|
|||||||
// VISTA 1: LA LISTA DEI CONTRATTI
|
// VISTA 1: LA LISTA DEI CONTRATTI
|
||||||
// ==========================================
|
// ==========================================
|
||||||
class _EnergyList extends StatelessWidget {
|
class _EnergyList extends StatelessWidget {
|
||||||
final List<EnergyServiceModel> services;
|
final List<EnergyServiceModel> operations;
|
||||||
final List<ProviderModel>
|
final List<ProviderModel>
|
||||||
activeProviders; // <--- NUOVO: La lista vera dal Cubit
|
activeProviders; // <--- NUOVO: La lista vera dal Cubit
|
||||||
final Function(int) onDelete;
|
final Function(int) onDelete;
|
||||||
final VoidCallback onAddTap;
|
final VoidCallback onAddTap;
|
||||||
|
|
||||||
const _EnergyList({
|
const _EnergyList({
|
||||||
required this.services,
|
required this.operations,
|
||||||
required this.activeProviders, // <--- Richiesto
|
required this.activeProviders, // <--- Richiesto
|
||||||
required this.onDelete,
|
required this.onDelete,
|
||||||
required this.onAddTap,
|
required this.onAddTap,
|
||||||
@@ -120,7 +120,7 @@ class _EnergyList extends StatelessWidget {
|
|||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||||
children: [
|
children: [
|
||||||
if (services.isEmpty)
|
if (operations.isEmpty)
|
||||||
const Padding(
|
const Padding(
|
||||||
padding: EdgeInsets.symmetric(vertical: 32.0),
|
padding: EdgeInsets.symmetric(vertical: 32.0),
|
||||||
child: Text(
|
child: Text(
|
||||||
@@ -133,10 +133,10 @@ class _EnergyList extends StatelessWidget {
|
|||||||
Flexible(
|
Flexible(
|
||||||
child: ListView.separated(
|
child: ListView.separated(
|
||||||
shrinkWrap: true,
|
shrinkWrap: true,
|
||||||
itemCount: services.length,
|
itemCount: operations.length,
|
||||||
separatorBuilder: (_, _) => const Divider(height: 1),
|
separatorBuilder: (_, _) => const Divider(height: 1),
|
||||||
itemBuilder: (context, index) {
|
itemBuilder: (context, index) {
|
||||||
final s = services[index];
|
final s = operations[index];
|
||||||
final isLuce = s.type == EnergyType.luce;
|
final isLuce = s.type == EnergyType.luce;
|
||||||
|
|
||||||
// LA MAGIA: Troviamo il nome partendo dall'ID salvato nel servizio
|
// LA MAGIA: Troviamo il nome partendo dall'ID salvato nel servizio
|
||||||
|
|||||||
@@ -67,7 +67,7 @@ class _EntertainmentServiceDialogState
|
|||||||
builder: (context, state) {
|
builder: (context, state) {
|
||||||
// Passiamo allProviders per garantire la visione dello storico
|
// Passiamo allProviders per garantire la visione dello storico
|
||||||
return _EntertainmentList(
|
return _EntertainmentList(
|
||||||
services: _tempList,
|
operations: _tempList,
|
||||||
allProviders: state.allProviders,
|
allProviders: state.allProviders,
|
||||||
onDelete: (index) =>
|
onDelete: (index) =>
|
||||||
setState(() => _tempList.removeAt(index)),
|
setState(() => _tempList.removeAt(index)),
|
||||||
@@ -94,13 +94,13 @@ class _EntertainmentServiceDialogState
|
|||||||
}
|
}
|
||||||
|
|
||||||
class _EntertainmentList extends StatelessWidget {
|
class _EntertainmentList extends StatelessWidget {
|
||||||
final List<EntertainmentServiceModel> services;
|
final List<EntertainmentServiceModel> operations;
|
||||||
final List<ProviderModel> allProviders;
|
final List<ProviderModel> allProviders;
|
||||||
final Function(int) onDelete;
|
final Function(int) onDelete;
|
||||||
final VoidCallback onAddTap;
|
final VoidCallback onAddTap;
|
||||||
|
|
||||||
const _EntertainmentList({
|
const _EntertainmentList({
|
||||||
required this.services,
|
required this.operations,
|
||||||
required this.allProviders,
|
required this.allProviders,
|
||||||
required this.onDelete,
|
required this.onDelete,
|
||||||
required this.onAddTap,
|
required this.onAddTap,
|
||||||
@@ -112,7 +112,7 @@ class _EntertainmentList extends StatelessWidget {
|
|||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||||
children: [
|
children: [
|
||||||
if (services.isEmpty)
|
if (operations.isEmpty)
|
||||||
const Padding(
|
const Padding(
|
||||||
padding: EdgeInsets.symmetric(vertical: 32.0),
|
padding: EdgeInsets.symmetric(vertical: 32.0),
|
||||||
child: Text(
|
child: Text(
|
||||||
@@ -125,10 +125,10 @@ class _EntertainmentList extends StatelessWidget {
|
|||||||
Flexible(
|
Flexible(
|
||||||
child: ListView.separated(
|
child: ListView.separated(
|
||||||
shrinkWrap: true,
|
shrinkWrap: true,
|
||||||
itemCount: services.length,
|
itemCount: operations.length,
|
||||||
separatorBuilder: (_, _) => const Divider(height: 1),
|
separatorBuilder: (_, _) => const Divider(height: 1),
|
||||||
itemBuilder: (context, index) {
|
itemBuilder: (context, index) {
|
||||||
final s = services[index];
|
final s = operations[index];
|
||||||
|
|
||||||
final providerName = allProviders
|
final providerName = allProviders
|
||||||
.firstWhere(
|
.firstWhere(
|
||||||
|
|||||||
@@ -74,7 +74,7 @@ class _FinanceServiceDialogState extends State<FinanceServiceDialog> {
|
|||||||
return BlocBuilder<ProductCubit, ProductState>(
|
return BlocBuilder<ProductCubit, ProductState>(
|
||||||
builder: (context, prodState) {
|
builder: (context, prodState) {
|
||||||
return _FinanceList(
|
return _FinanceList(
|
||||||
services: _tempList,
|
operations: _tempList,
|
||||||
allProviders:
|
allProviders:
|
||||||
provState.allProviders, // Per vedere lo storico
|
provState.allProviders, // Per vedere lo storico
|
||||||
allModels: prodState.models,
|
allModels: prodState.models,
|
||||||
@@ -109,14 +109,14 @@ class _FinanceServiceDialogState extends State<FinanceServiceDialog> {
|
|||||||
// VISTA LISTA (STORICA)
|
// VISTA LISTA (STORICA)
|
||||||
// ===========================================================================
|
// ===========================================================================
|
||||||
class _FinanceList extends StatelessWidget {
|
class _FinanceList extends StatelessWidget {
|
||||||
final List<FinServiceModel> services;
|
final List<FinServiceModel> operations;
|
||||||
final List<ProviderModel> allProviders;
|
final List<ProviderModel> allProviders;
|
||||||
final List<ModelModel> allModels;
|
final List<ModelModel> allModels;
|
||||||
final Function(int) onDelete;
|
final Function(int) onDelete;
|
||||||
final VoidCallback onAddTap;
|
final VoidCallback onAddTap;
|
||||||
|
|
||||||
const _FinanceList({
|
const _FinanceList({
|
||||||
required this.services,
|
required this.operations,
|
||||||
required this.allProviders,
|
required this.allProviders,
|
||||||
required this.allModels,
|
required this.allModels,
|
||||||
required this.onDelete,
|
required this.onDelete,
|
||||||
@@ -125,7 +125,7 @@ class _FinanceList extends StatelessWidget {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
if (services.isEmpty) {
|
if (operations.isEmpty) {
|
||||||
return Column(
|
return Column(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
children: [
|
||||||
@@ -151,10 +151,10 @@ class _FinanceList extends StatelessWidget {
|
|||||||
Flexible(
|
Flexible(
|
||||||
child: ListView.separated(
|
child: ListView.separated(
|
||||||
shrinkWrap: true,
|
shrinkWrap: true,
|
||||||
itemCount: services.length,
|
itemCount: operations.length,
|
||||||
separatorBuilder: (_, _) => const Divider(),
|
separatorBuilder: (_, _) => const Divider(),
|
||||||
itemBuilder: (context, index) {
|
itemBuilder: (context, index) {
|
||||||
final s = services[index];
|
final s = operations[index];
|
||||||
|
|
||||||
// Cerchiamo il nome del provider in TUTTI quelli caricati (storico)
|
// Cerchiamo il nome del provider in TUTTI quelli caricati (storico)
|
||||||
final providerName = allProviders
|
final providerName = allProviders
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
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/operations/blocs/services_cubit.dart';
|
import 'package:flux/features/operations/blocs/operations_cubit.dart';
|
||||||
import 'package:flux/features/operations/models/service_model.dart';
|
import 'package:flux/features/operations/models/service_model.dart';
|
||||||
|
|
||||||
class GeneralInfoSection extends StatelessWidget {
|
class GeneralInfoSection extends StatelessWidget {
|
||||||
final ServiceModel service;
|
final ServiceModel operation;
|
||||||
const GeneralInfoSection({super.key, required this.service});
|
const GeneralInfoSection({super.key, required this.operation});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
@@ -34,7 +34,7 @@ class GeneralInfoSection extends StatelessWidget {
|
|||||||
|
|
||||||
// Numero di Riferimento / Telefono
|
// Numero di Riferimento / Telefono
|
||||||
TextFormField(
|
TextFormField(
|
||||||
initialValue: service.number,
|
initialValue: operation.number,
|
||||||
keyboardType: TextInputType
|
keyboardType: TextInputType
|
||||||
.phone, // Fa aprire il tastierino numerico su mobile
|
.phone, // Fa aprire il tastierino numerico su mobile
|
||||||
decoration: const InputDecoration(
|
decoration: const InputDecoration(
|
||||||
@@ -59,7 +59,7 @@ class GeneralInfoSection extends StatelessWidget {
|
|||||||
"Pratica in lavorazione",
|
"Pratica in lavorazione",
|
||||||
style: TextStyle(fontSize: 12),
|
style: TextStyle(fontSize: 12),
|
||||||
),
|
),
|
||||||
value: service.isBozza,
|
value: operation.isBozza,
|
||||||
activeThumbColor: Colors.orange,
|
activeThumbColor: Colors.orange,
|
||||||
contentPadding: EdgeInsets.zero,
|
contentPadding: EdgeInsets.zero,
|
||||||
onChanged: (val) {
|
onChanged: (val) {
|
||||||
@@ -75,7 +75,7 @@ class GeneralInfoSection extends StatelessWidget {
|
|||||||
"Esito positivo",
|
"Esito positivo",
|
||||||
style: TextStyle(fontSize: 12),
|
style: TextStyle(fontSize: 12),
|
||||||
),
|
),
|
||||||
value: service.resultOk,
|
value: operation.resultOk,
|
||||||
activeThumbColor: Colors.green,
|
activeThumbColor: Colors.green,
|
||||||
contentPadding: EdgeInsets.zero,
|
contentPadding: EdgeInsets.zero,
|
||||||
onChanged: (val) {
|
onChanged: (val) {
|
||||||
@@ -89,7 +89,7 @@ class GeneralInfoSection extends StatelessWidget {
|
|||||||
|
|
||||||
// Campo Note
|
// Campo Note
|
||||||
TextFormField(
|
TextFormField(
|
||||||
initialValue: service.note,
|
initialValue: operation.note,
|
||||||
maxLines: 4,
|
maxLines: 4,
|
||||||
minLines: 2,
|
minLines: 2,
|
||||||
decoration: const InputDecoration(
|
decoration: const InputDecoration(
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
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/operations/blocs/services_cubit.dart';
|
import 'package:flux/features/operations/blocs/operations_cubit.dart';
|
||||||
import 'package:flux/features/operations/models/service_model.dart';
|
import 'package:flux/features/operations/models/service_model.dart';
|
||||||
import 'package:flux/features/operations/ui/service_form_screen/attachment_section.dart';
|
import 'package:flux/features/operations/ui/service_form_screen/attachment_section.dart';
|
||||||
import 'package:flux/features/operations/ui/service_form_screen/customer_section.dart';
|
import 'package:flux/features/operations/ui/service_form_screen/customer_section.dart';
|
||||||
@@ -70,7 +70,7 @@ class _ServiceFormScreenState extends State<ServiceFormScreen> {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
builder: (context, state) {
|
builder: (context, state) {
|
||||||
final service = state.currentService;
|
final operation = state.currentService;
|
||||||
final isSaving = state.status == ServicesStatus.saving;
|
final isSaving = state.status == ServicesStatus.saving;
|
||||||
final isEditMode = widget.serviceId != null;
|
final isEditMode = widget.serviceId != null;
|
||||||
|
|
||||||
@@ -89,7 +89,7 @@ class _ServiceFormScreenState extends State<ServiceFormScreen> {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
else if (service != null) ...[
|
else if (operation != null) ...[
|
||||||
IconButton(
|
IconButton(
|
||||||
icon: const Icon(Icons.edit_note),
|
icon: const Icon(Icons.edit_note),
|
||||||
tooltip: "Salva come Bozza",
|
tooltip: "Salva come Bozza",
|
||||||
@@ -107,20 +107,20 @@ class _ServiceFormScreenState extends State<ServiceFormScreen> {
|
|||||||
],
|
],
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
body: (service == null)
|
body: (operation == null)
|
||||||
? const Center(child: CircularProgressIndicator())
|
? const Center(child: CircularProgressIndicator())
|
||||||
: SingleChildScrollView(
|
: SingleChildScrollView(
|
||||||
padding: const EdgeInsets.all(16),
|
padding: const EdgeInsets.all(16),
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
CustomerSection(service: service),
|
CustomerSection(operation: operation),
|
||||||
const SizedBox(height: 24),
|
const SizedBox(height: 24),
|
||||||
|
|
||||||
GeneralInfoSection(service: service),
|
GeneralInfoSection(operation: operation),
|
||||||
const SizedBox(height: 24),
|
const SizedBox(height: 24),
|
||||||
|
|
||||||
ServicesGrid(service: service),
|
ServicesGrid(operation: operation),
|
||||||
const SizedBox(height: 32),
|
const SizedBox(height: 32),
|
||||||
|
|
||||||
AttachmentsSection(),
|
AttachmentsSection(),
|
||||||
|
|||||||
@@ -1,7 +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/master_data/products/blocs/product_cubit.dart';
|
import 'package:flux/features/master_data/products/blocs/product_cubit.dart';
|
||||||
import 'package:flux/features/operations/blocs/services_cubit.dart';
|
import 'package:flux/features/operations/blocs/operations_cubit.dart';
|
||||||
import 'package:flux/features/operations/models/energy_service_model.dart';
|
import 'package:flux/features/operations/models/energy_service_model.dart';
|
||||||
import 'package:flux/features/operations/models/entertainment_service_model.dart';
|
import 'package:flux/features/operations/models/entertainment_service_model.dart';
|
||||||
import 'package:flux/features/operations/models/fin_service_model.dart';
|
import 'package:flux/features/operations/models/fin_service_model.dart';
|
||||||
@@ -13,9 +13,9 @@ import 'package:flux/features/operations/ui/service_form_screen/finance_service_
|
|||||||
import 'package:flux/features/operations/ui/service_form_screen/int_dialogs.dart'; // Assicurati di importare il modello
|
import 'package:flux/features/operations/ui/service_form_screen/int_dialogs.dart'; // Assicurati di importare il modello
|
||||||
|
|
||||||
class ServicesGrid extends StatelessWidget {
|
class ServicesGrid extends StatelessWidget {
|
||||||
final ServiceModel service;
|
final ServiceModel operation;
|
||||||
|
|
||||||
const ServicesGrid({super.key, required this.service});
|
const ServicesGrid({super.key, required this.operation});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
@@ -52,65 +52,65 @@ class ServicesGrid extends StatelessWidget {
|
|||||||
// --- CONTATORI SEMPLICI ---
|
// --- CONTATORI SEMPLICI ---
|
||||||
ActionCard(
|
ActionCard(
|
||||||
label: "AL",
|
label: "AL",
|
||||||
count: service.al,
|
count: operation.al,
|
||||||
icon: Icons.sim_card,
|
icon: Icons.sim_card,
|
||||||
color: Colors.blue,
|
color: Colors.blue,
|
||||||
onTap: () => updateCountDialog(
|
onTap: () => updateCountDialog(
|
||||||
context,
|
context,
|
||||||
"AL",
|
"AL",
|
||||||
service.al,
|
operation.al,
|
||||||
(val) =>
|
(val) =>
|
||||||
context.read<ServicesCubit>().updateField(al: val),
|
context.read<ServicesCubit>().updateField(al: val),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
ActionCard(
|
ActionCard(
|
||||||
label: "MNP",
|
label: "MNP",
|
||||||
count: service.mnp,
|
count: operation.mnp,
|
||||||
icon: Icons.phone_android,
|
icon: Icons.phone_android,
|
||||||
color: Colors.indigo,
|
color: Colors.indigo,
|
||||||
onTap: () => updateCountDialog(
|
onTap: () => updateCountDialog(
|
||||||
context,
|
context,
|
||||||
"MNP",
|
"MNP",
|
||||||
service.mnp,
|
operation.mnp,
|
||||||
(val) =>
|
(val) =>
|
||||||
context.read<ServicesCubit>().updateField(mnp: val),
|
context.read<ServicesCubit>().updateField(mnp: val),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
ActionCard(
|
ActionCard(
|
||||||
label: "NIP",
|
label: "NIP",
|
||||||
count: service.nip,
|
count: operation.nip,
|
||||||
icon: Icons.compare_arrows,
|
icon: Icons.compare_arrows,
|
||||||
color: Colors.cyan,
|
color: Colors.cyan,
|
||||||
onTap: () => updateCountDialog(
|
onTap: () => updateCountDialog(
|
||||||
context,
|
context,
|
||||||
"NIP",
|
"NIP",
|
||||||
service.nip,
|
operation.nip,
|
||||||
(val) =>
|
(val) =>
|
||||||
context.read<ServicesCubit>().updateField(nip: val),
|
context.read<ServicesCubit>().updateField(nip: val),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
ActionCard(
|
ActionCard(
|
||||||
label: "Unica",
|
label: "Unica",
|
||||||
count: service.unica,
|
count: operation.unica,
|
||||||
icon: Icons.all_inclusive,
|
icon: Icons.all_inclusive,
|
||||||
color: Colors.purple,
|
color: Colors.purple,
|
||||||
onTap: () => updateCountDialog(
|
onTap: () => updateCountDialog(
|
||||||
context,
|
context,
|
||||||
"Unica",
|
"Unica",
|
||||||
service.unica,
|
operation.unica,
|
||||||
(val) =>
|
(val) =>
|
||||||
context.read<ServicesCubit>().updateField(unica: val),
|
context.read<ServicesCubit>().updateField(unica: val),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
ActionCard(
|
ActionCard(
|
||||||
label: "Telepass",
|
label: "Telepass",
|
||||||
count: service.telepass,
|
count: operation.telepass,
|
||||||
icon: Icons.directions_car,
|
icon: Icons.directions_car,
|
||||||
color: Colors.amber.shade700,
|
color: Colors.amber.shade700,
|
||||||
onTap: () => updateCountDialog(
|
onTap: () => updateCountDialog(
|
||||||
context,
|
context,
|
||||||
"Telepass",
|
"Telepass",
|
||||||
service.telepass,
|
operation.telepass,
|
||||||
(val) => context.read<ServicesCubit>().updateField(
|
(val) => context.read<ServicesCubit>().updateField(
|
||||||
telepass: val,
|
telepass: val,
|
||||||
),
|
),
|
||||||
@@ -120,7 +120,7 @@ class ServicesGrid extends StatelessWidget {
|
|||||||
// --- MODULI COMPLESSI (Le liste) ---
|
// --- MODULI COMPLESSI (Le liste) ---
|
||||||
ActionCard(
|
ActionCard(
|
||||||
label: "Energia",
|
label: "Energia",
|
||||||
count: service.energyServices.length,
|
count: operation.energyServices.length,
|
||||||
icon: Icons.bolt,
|
icon: Icons.bolt,
|
||||||
color: Colors.green,
|
color: Colors.green,
|
||||||
onTap: () async {
|
onTap: () async {
|
||||||
@@ -128,8 +128,8 @@ class ServicesGrid extends StatelessWidget {
|
|||||||
final result = await showDialog<List<EnergyServiceModel>>(
|
final result = await showDialog<List<EnergyServiceModel>>(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (context) => EnergyServiceDialog(
|
builder: (context) => EnergyServiceDialog(
|
||||||
currentStoreId: service.storeId,
|
currentStoreId: operation.storeId,
|
||||||
initialServices: service
|
initialServices: operation
|
||||||
.energyServices, // Passiamo la lista attuale
|
.energyServices, // Passiamo la lista attuale
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
@@ -144,7 +144,7 @@ class ServicesGrid extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
ActionCard(
|
ActionCard(
|
||||||
label: "Finanziam.",
|
label: "Finanziam.",
|
||||||
count: service.finServices.length,
|
count: operation.finServices.length,
|
||||||
icon: Icons.euro_symbol,
|
icon: Icons.euro_symbol,
|
||||||
color: Colors.teal,
|
color: Colors.teal,
|
||||||
onTap: () async {
|
onTap: () async {
|
||||||
@@ -152,9 +152,9 @@ class ServicesGrid extends StatelessWidget {
|
|||||||
context: context,
|
context: context,
|
||||||
builder: (context) => FinanceServiceDialog(
|
builder: (context) => FinanceServiceDialog(
|
||||||
productCubit: context.read<ProductCubit>(),
|
productCubit: context.read<ProductCubit>(),
|
||||||
currentStoreId: service.storeId,
|
currentStoreId: operation.storeId,
|
||||||
initialServices:
|
initialServices: operation
|
||||||
service.finServices, // Passiamo la lista attuale
|
.finServices, // Passiamo la lista attuale
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -165,7 +165,7 @@ class ServicesGrid extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
ActionCard(
|
ActionCard(
|
||||||
label: "Intratten.",
|
label: "Intratten.",
|
||||||
count: service.entertainmentServices.length,
|
count: operation.entertainmentServices.length,
|
||||||
icon: Icons.movie_filter_outlined,
|
icon: Icons.movie_filter_outlined,
|
||||||
color: Colors.purple,
|
color: Colors.purple,
|
||||||
onTap: () async {
|
onTap: () async {
|
||||||
@@ -173,8 +173,8 @@ class ServicesGrid extends StatelessWidget {
|
|||||||
await showDialog<List<EntertainmentServiceModel>>(
|
await showDialog<List<EntertainmentServiceModel>>(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (context) => EntertainmentServiceDialog(
|
builder: (context) => EntertainmentServiceDialog(
|
||||||
initialServices: service.entertainmentServices,
|
initialServices: operation.entertainmentServices,
|
||||||
currentStoreId: service.storeId,
|
currentStoreId: operation.storeId,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
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/operations/blocs/services_cubit.dart';
|
import 'package:flux/features/operations/blocs/operations_cubit.dart';
|
||||||
import 'package:flux/features/operations/models/service_model.dart';
|
import 'package:flux/features/operations/models/service_model.dart';
|
||||||
import 'package:flux/features/operations/utils/service_actions.dart';
|
import 'package:flux/features/operations/utils/service_actions.dart';
|
||||||
import 'package:go_router/go_router.dart';
|
import 'package:go_router/go_router.dart';
|
||||||
@@ -107,8 +107,8 @@ class _ServicesScreenState extends State<ServicesScreen> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
final service = state.allServices[index];
|
final operation = state.allServices[index];
|
||||||
return _buildServiceCard(context, service);
|
return _buildServiceCard(context, operation);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
@@ -121,7 +121,7 @@ class _ServicesScreenState extends State<ServicesScreen> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildServiceCard(BuildContext context, ServiceModel service) {
|
Widget _buildServiceCard(BuildContext context, ServiceModel operation) {
|
||||||
return Card(
|
return Card(
|
||||||
margin: const EdgeInsets.symmetric(horizontal: 12, vertical: 6),
|
margin: const EdgeInsets.symmetric(horizontal: 12, vertical: 6),
|
||||||
elevation: 2,
|
elevation: 2,
|
||||||
@@ -132,14 +132,14 @@ class _ServicesScreenState extends State<ServicesScreen> {
|
|||||||
children: [
|
children: [
|
||||||
Expanded(
|
Expanded(
|
||||||
child: Text(
|
child: Text(
|
||||||
service.customerDisplayName ?? "Cliente sconosciuto",
|
operation.customerDisplayName ?? "Cliente sconosciuto",
|
||||||
style: const TextStyle(
|
style: const TextStyle(
|
||||||
fontWeight: FontWeight.bold,
|
fontWeight: FontWeight.bold,
|
||||||
fontSize: 16,
|
fontSize: 16,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
if (service.isBozza)
|
if (operation.isBozza)
|
||||||
const Chip(
|
const Chip(
|
||||||
label: Text(
|
label: Text(
|
||||||
"BOZZA",
|
"BOZZA",
|
||||||
@@ -155,20 +155,20 @@ class _ServicesScreenState extends State<ServicesScreen> {
|
|||||||
children: [
|
children: [
|
||||||
const SizedBox(height: 4),
|
const SizedBox(height: 4),
|
||||||
Text(
|
Text(
|
||||||
"Pratica: ${service.number} • ${service.createdAt?.day}/${service.createdAt?.month}/${service.createdAt?.year}",
|
"Pratica: ${operation.number} • ${operation.createdAt?.day}/${operation.createdAt?.month}/${operation.createdAt?.year}",
|
||||||
),
|
),
|
||||||
const SizedBox(height: 8),
|
const SizedBox(height: 8),
|
||||||
// I nostri mini-chip per i servizi attivati
|
// I nostri mini-chip per i servizi attivati
|
||||||
Wrap(
|
Wrap(
|
||||||
spacing: 6,
|
spacing: 6,
|
||||||
children: [
|
children: [
|
||||||
if (service.al > 0 || service.mnp > 0)
|
if (operation.al > 0 || operation.mnp > 0)
|
||||||
_miniBadge("📞 Tel", Colors.blue),
|
_miniBadge("📞 Tel", Colors.blue),
|
||||||
if (service.energyServices.isNotEmpty)
|
if (operation.energyServices.isNotEmpty)
|
||||||
_miniBadge("⚡ Energy", Colors.green),
|
_miniBadge("⚡ Energy", Colors.green),
|
||||||
if (service.finServices.isNotEmpty)
|
if (operation.finServices.isNotEmpty)
|
||||||
_miniBadge("💰 Fin", Colors.purple),
|
_miniBadge("💰 Fin", Colors.purple),
|
||||||
if (service.entertainmentServices.isNotEmpty)
|
if (operation.entertainmentServices.isNotEmpty)
|
||||||
_miniBadge("📺 Ent", Colors.red),
|
_miniBadge("📺 Ent", Colors.red),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
@@ -176,10 +176,12 @@ class _ServicesScreenState extends State<ServicesScreen> {
|
|||||||
),
|
),
|
||||||
trailing: const Icon(Icons.chevron_right),
|
trailing: const Icon(Icons.chevron_right),
|
||||||
onTap: () => context.pushNamed(
|
onTap: () => context.pushNamed(
|
||||||
'service-form',
|
'operation-form',
|
||||||
extra: service, // <-- LA MAGIA È QUI: Passa l'oggetto intero!
|
extra: operation, // <-- LA MAGIA È QUI: Passa l'oggetto intero!
|
||||||
// Teniamo anche il parametro URL per coerenza di routing
|
// Teniamo anche il parametro URL per coerenza di routing
|
||||||
queryParameters: service.id != null ? {'serviceId': service.id!} : {},
|
queryParameters: operation.id != null
|
||||||
|
? {'serviceId': operation.id!}
|
||||||
|
: {},
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import 'package:flutter/material.dart';
|
|||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
import 'package:flux/core/blocs/session/session_cubit.dart';
|
import 'package:flux/core/blocs/session/session_cubit.dart';
|
||||||
import 'package:flux/features/master_data/store/bloc/store_cubit.dart';
|
import 'package:flux/features/master_data/store/bloc/store_cubit.dart';
|
||||||
import 'package:flux/features/operations/blocs/services_cubit.dart';
|
import 'package:flux/features/operations/blocs/operations_cubit.dart';
|
||||||
import 'package:flux/features/operations/models/service_model.dart';
|
import 'package:flux/features/operations/models/service_model.dart';
|
||||||
import 'package:go_router/go_router.dart';
|
import 'package:go_router/go_router.dart';
|
||||||
|
|
||||||
@@ -67,7 +67,7 @@ void startNewService(BuildContext context) {
|
|||||||
Navigator.pop(modalContext);
|
Navigator.pop(modalContext);
|
||||||
|
|
||||||
// 3. Naviga verso il form
|
// 3. Naviga verso il form
|
||||||
context.pushNamed('service-form');
|
context.pushNamed('operation-form');
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
{
|
{
|
||||||
"@@locale": "en",
|
"@@locale": "en",
|
||||||
"welcomeBack": "Welcome back, {name}! 👋",
|
"welcomeBack": "Welcome back, {name}! 👋",
|
||||||
"latestServices": "Latest Services",
|
"latestServices": "Latest Operations",
|
||||||
"masterData": "Master Data",
|
"masterData": "Master Data",
|
||||||
"settings": "Settings",
|
"settings": "Settings",
|
||||||
"newService": "Service",
|
"newService": "Operation",
|
||||||
"expiring_contracts": "Expiring Contracts",
|
"expiring_contracts": "Expiring Contracts",
|
||||||
"sticky_notes": "Sticky Notes",
|
"sticky_notes": "Sticky Notes",
|
||||||
"my_tasks": "My Tasks",
|
"my_tasks": "My Tasks",
|
||||||
"latest_service_tickets": "Latest service tickets"
|
"latest_service_tickets": "Latest operation tickets"
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -26,7 +26,7 @@ import 'package:flux/features/master_data/staff/blocs/staff_cubit.dart';
|
|||||||
import 'package:flux/features/master_data/staff/data/staff_repository.dart';
|
import 'package:flux/features/master_data/staff/data/staff_repository.dart';
|
||||||
import 'package:flux/features/master_data/store/bloc/store_cubit.dart';
|
import 'package:flux/features/master_data/store/bloc/store_cubit.dart';
|
||||||
import 'package:flux/features/master_data/store/data/store_repository.dart';
|
import 'package:flux/features/master_data/store/data/store_repository.dart';
|
||||||
import 'package:flux/features/operations/blocs/services_cubit.dart';
|
import 'package:flux/features/operations/blocs/operations_cubit.dart';
|
||||||
import 'package:flux/features/operations/data/services_repository.dart';
|
import 'package:flux/features/operations/data/services_repository.dart';
|
||||||
import 'package:flux/features/settings/settings.dart';
|
import 'package:flux/features/settings/settings.dart';
|
||||||
|
|
||||||
|
|||||||
@@ -35,9 +35,9 @@
|
|||||||
<menuItem isSeparatorItem="YES" id="VOq-y0-SEH"/>
|
<menuItem isSeparatorItem="YES" id="VOq-y0-SEH"/>
|
||||||
<menuItem title="Preferences…" keyEquivalent="," id="BOF-NM-1cW"/>
|
<menuItem title="Preferences…" keyEquivalent="," id="BOF-NM-1cW"/>
|
||||||
<menuItem isSeparatorItem="YES" id="wFC-TO-SCJ"/>
|
<menuItem isSeparatorItem="YES" id="wFC-TO-SCJ"/>
|
||||||
<menuItem title="Services" id="NMo-om-nkz">
|
<menuItem title="Operations" id="NMo-om-nkz">
|
||||||
<modifierMask key="keyEquivalentModifierMask"/>
|
<modifierMask key="keyEquivalentModifierMask"/>
|
||||||
<menu key="submenu" title="Services" systemMenu="services" id="hz9-B4-Xy5"/>
|
<menu key="submenu" title="Operations" systemMenu="operations" id="hz9-B4-Xy5"/>
|
||||||
</menuItem>
|
</menuItem>
|
||||||
<menuItem isSeparatorItem="YES" id="4je-JR-u6R"/>
|
<menuItem isSeparatorItem="YES" id="4je-JR-u6R"/>
|
||||||
<menuItem title="Hide APP_NAME" keyEquivalent="h" id="Olw-nP-bQN">
|
<menuItem title="Hide APP_NAME" keyEquivalent="h" id="Olw-nP-bQN">
|
||||||
|
|||||||
Reference in New Issue
Block a user