Co-authored-by: Copilot <copilot@github.com>
This commit is contained in:
2026-04-24 12:39:22 +02:00
parent a52436ea9a
commit a06807cd1f
13 changed files with 703 additions and 79 deletions

View File

@@ -1,5 +1,7 @@
import 'package:file_picker/file_picker.dart';
import 'package:flutter/material.dart';
import 'package:flux/core/blocs/session/session_cubit.dart';
import 'package:flux/core/utils/string_extensions.dart';
import 'package:flux/features/customers/data/customer_repository.dart';
import 'package:flux/features/customers/models/customer_file_model.dart';
import 'package:flux/features/services/models/service_file_model.dart';
@@ -83,7 +85,7 @@ class ServicesRepository {
}
// --- SALVATAGGIO COMPLETO (PRIMA PADRE, POI FIGLI) ---
Future<void> saveFullService(ServiceModel service) async {
Future<ServiceModel> saveFullService(ServiceModel service) async {
try {
// 1. Upsert del record principale
final serviceData = await _supabase
@@ -150,15 +152,23 @@ class ServicesRepository {
if (insertTasks.isNotEmpty) {
await Future.wait(insertTasks);
}
if (service.files.isNotEmpty) {
// 4. UPLOAD DEI FILE LOCALI (Nuovi)
// Filtriamo solo i file che non hanno ancora un ID (quindi sono locali)
final localFilesToUpload = service.files
.where((f) => f.id == null)
.toList();
if (localFilesToUpload.isNotEmpty) {
final List<Future> uploadTasks = [];
for (var file in service.files) {
for (var file in localFilesToUpload) {
final storagePath =
'$companyId/services/$newId/${DateTime.now().millisecondsSinceEpoch}_${file.name}.${file.extension}';
final String mimeType = file.extension.toLowerCase() == 'pdf'
? 'application/pdf'
: 'image/${file.extension}';
final fileToSave = file.copyWith(
serviceId: newId,
storagePath: storagePath,
@@ -166,22 +176,16 @@ class ServicesRepository {
// Creiamo una funzione asincrona per caricare file e scrivere nel DB
Future<void> uploadAndLink() async {
// Determiniamo il MIME type corretto in base all'estensione
// A. Upload nel Bucket Storage (usiamo i bytes così funziona anche su Web!)
await _supabase.storage
.from('documents')
.uploadBinary(
storagePath,
fileToSave.localBytes!,
fileOptions: FileOptions(
contentType:
mimeType, // Diciamo a Supabase esattamente cos'è!
upsert:
true, // Opzionale: sovrascrive se esiste già un file con lo stesso nome
),
fileOptions: FileOptions(contentType: mimeType, upsert: true),
);
// B. Inserimento riga nel DB relazionale
await _supabase.from('service_file').insert(fileToSave.toMap());
}
@@ -191,6 +195,23 @@ class ServicesRepository {
// Eseguiamo tutti gli upload in parallelo per la massima velocità
await Future.wait(uploadTasks);
}
// 5. GRAN FINALE: RECUPERO DEL MODELLO COMPLETO E AGGIORNATO
// Interroghiamo Supabase per farci restituire la pratica con TUTTI gli ID generati
// (inclusi quelli della tabella service_file appena inseriti)
final updatedServiceData = await _supabase
.from('service')
.select('''
*,
energy_service(*),
fin_service(*),
entertainment_service(*),
service_file(*)
''')
.eq('id', newId)
.single();
return ServiceModel.fromMap(updatedServiceData);
} catch (e) {
// Qui potresti aggiungere una logica di "rollback manuale" se necessario
throw Exception('Errore durante il salvataggio corazzato: $e');
@@ -239,12 +260,66 @@ class ServicesRepository {
}
/// Ascolta in tempo reale i file caricati per una pratica
Stream<List<Map<String, dynamic>>> getServiceFilesStream(String serviceId) {
Stream<List<ServiceFileModel>> getServiceFilesStream(String serviceId) {
return _supabase
.from('service_file')
.stream(primaryKey: ['id'])
.eq('service_id', serviceId)
.order('created_at', ascending: false);
.order('created_at', ascending: false)
.map(
(listOfMaps) =>
listOfMaps.map((map) => ServiceFileModel.fromMap(map)).toList(),
);
}
Future<ServiceFileModel> uploadAndRegisterServiceFile({
required String serviceId,
required PlatformFile pickedFile,
}) async {
final cleanFileName = pickedFile.name.replaceAll(
RegExp(r'[^a-zA-Z0-9\.\-]'),
'_',
);
final storagePath =
'$companyId/services/$serviceId/${DateTime.now().millisecondsSinceEpoch}_$cleanFileName';
final int fileSize = pickedFile.size;
final fileToSave = ServiceFileModel(
serviceId: serviceId,
name: cleanFileName.fileNameWithoutExtension(),
extension: cleanFileName.fileExtension(),
storagePath: storagePath,
fileSize: fileSize,
);
final String mimeType = fileToSave.extension.toLowerCase() == 'pdf'
? 'application/pdf'
: 'image/${fileToSave.extension}';
try {
// Usiamo bytes invece del path per massima compatibilità
if (pickedFile.bytes == null && pickedFile.path == null) {
throw 'Impossibile leggere il contenuto del file';
}
// Se siamo su desktop/mobile abbiamo il path, su web abbiamo i bytes
if (pickedFile.bytes != null) {
await _supabase.storage
.from('documents')
.uploadBinary(
storagePath,
pickedFile.bytes!,
fileOptions: FileOptions(contentType: mimeType, upsert: true),
);
}
final response = await _supabase
.from('service_file')
.insert(fileToSave.toMap())
.select()
.single();
return ServiceFileModel.fromMap(response);
} catch (e) {
throw 'Errore durante l\'upload: $e';
}
}
Future<void> copyFileToCustomer({
@@ -258,6 +333,6 @@ class ServicesRepository {
extension: file.extension,
fileSize: file.fileSize,
);
await _customerRepository.saveCustomerFile(fileToCopy);
await _customerRepository.saveFileReference(fileToCopy);
}
}