fix mobile upload
This commit is contained in:
@@ -4,7 +4,6 @@ import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:flux/core/blocs/session/session_cubit.dart';
|
||||
import 'package:flux/core/data/core_repository.dart';
|
||||
import 'package:flux/core/layout/app_shell.dart';
|
||||
import 'package:flux/core/utils/extensions.dart';
|
||||
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/upload_success_screen.dart';
|
||||
@@ -285,6 +284,7 @@ class AppRouter {
|
||||
builder: (context, state) {
|
||||
final typeString = state.pathParameters['type']!;
|
||||
final id = state.pathParameters['id']!;
|
||||
final companyId = state.uri.queryParameters['companyId']!;
|
||||
|
||||
// Trasformiamo la stringa dell'URL nel nostro amato Enum!
|
||||
final parentType = AttachmentParentType.values.firstWhere(
|
||||
@@ -297,8 +297,9 @@ class AppRouter {
|
||||
return BlocProvider(
|
||||
create: (context) =>
|
||||
AttachmentsBloc(parentId: id, parentType: parentType),
|
||||
child: const SharedMobileUploadScreen(
|
||||
child: SharedMobileUploadScreen(
|
||||
title: 'Caricamento Rapido',
|
||||
companyId: companyId,
|
||||
),
|
||||
);
|
||||
},
|
||||
|
||||
@@ -4,6 +4,8 @@ import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:file_picker/file_picker.dart';
|
||||
import 'package:flux/core/blocs/session/session_cubit.dart';
|
||||
import 'package:flux/core/widgets/qr_upload_dialog.dart';
|
||||
import 'package:flux/features/attachments/data/attachments_repository.dart';
|
||||
import 'package:flux/features/attachments/ui/attachment_viewer_screen.dart';
|
||||
import 'package:flux/features/attachments/ui/quick_rename_dialog.dart';
|
||||
@@ -30,14 +32,16 @@ class _ExportItem {
|
||||
|
||||
class SharedAttachmentsSection extends StatefulWidget {
|
||||
final String? parentId;
|
||||
final String customerDisplayName;
|
||||
final String titleForUpload;
|
||||
final AttachmentParentType parentType;
|
||||
final Future<String?> Function()? onGenerateIdForQr;
|
||||
|
||||
const SharedAttachmentsSection({
|
||||
super.key,
|
||||
this.parentId,
|
||||
this.customerDisplayName = 'Cliente_sconosciuto',
|
||||
this.titleForUpload = 'Cliente_sconosciuto',
|
||||
required this.parentType,
|
||||
this.onGenerateIdForQr,
|
||||
});
|
||||
|
||||
@override
|
||||
@@ -65,7 +69,7 @@ class _SharedAttachmentsSectionState extends State<SharedAttachmentsSection> {
|
||||
|
||||
Future<void> _selectExportDirectory() async {
|
||||
final String? selectedDirectory = await FilePicker.getDirectoryPath(
|
||||
dialogTitle: 'Seleziona la cartella di esportazione per TIM/Citrix',
|
||||
dialogTitle: 'Seleziona la cartella di esportazione',
|
||||
);
|
||||
|
||||
if (selectedDirectory != null) {
|
||||
@@ -189,7 +193,7 @@ class _SharedAttachmentsSectionState extends State<SharedAttachmentsSection> {
|
||||
} else {
|
||||
// Se sono più file uniti
|
||||
|
||||
suggestedName = '${widget.customerDisplayName}_Unito';
|
||||
suggestedName = '${widget.titleForUpload}_Unito';
|
||||
}
|
||||
|
||||
if (!mounted) return;
|
||||
@@ -397,7 +401,6 @@ class _SharedAttachmentsSectionState extends State<SharedAttachmentsSection> {
|
||||
Widget build(BuildContext context) {
|
||||
final theme = Theme.of(context);
|
||||
|
||||
// USIAMO IL TUO BLOC!
|
||||
return BlocBuilder<AttachmentsBloc, AttachmentsState>(
|
||||
builder: (context, state) {
|
||||
final allFiles = state.allFiles;
|
||||
@@ -421,7 +424,7 @@ class _SharedAttachmentsSectionState extends State<SharedAttachmentsSection> {
|
||||
color: theme.colorScheme.primary,
|
||||
),
|
||||
title: const Text(
|
||||
'Cartella Export (Es. Citrix TIM)',
|
||||
'Cartella Export (Es. TIM AttachmentRepository)',
|
||||
style: TextStyle(fontWeight: FontWeight.bold),
|
||||
),
|
||||
subtitle: Text(
|
||||
@@ -451,6 +454,69 @@ class _SharedAttachmentsSectionState extends State<SharedAttachmentsSection> {
|
||||
onPressed: state.status == AttachmentsStatus.uploading
|
||||
? null
|
||||
: _pickFiles,
|
||||
/* : () {
|
||||
final bloc = context.read<AttachmentsBloc>();
|
||||
Navigator.of(context).push(
|
||||
MaterialPageRoute(
|
||||
builder: (_) => BlocProvider.value(
|
||||
value: bloc,
|
||||
child: SharedMobileUploadScreen(
|
||||
title: widget.titleForUpload,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}, */
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
Tooltip(
|
||||
message: 'Carica foto con lo smartphone',
|
||||
child: IconButton(
|
||||
icon: const Icon(Icons.qr_code_scanner),
|
||||
color: theme.colorScheme.primary, // Sempre colorato!
|
||||
onPressed: () async {
|
||||
String? targetId = state.parentId;
|
||||
|
||||
// SE L'ID NON C'È, CHIAMIAMO IL SALVATAGGIO IN BACKGROUND!
|
||||
if (targetId == null) {
|
||||
if (widget.onGenerateIdForQr != null) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
const SnackBar(
|
||||
content: Text(
|
||||
'Salvataggio rapido scheda in corso... ⏳',
|
||||
),
|
||||
duration: Duration(seconds: 1),
|
||||
),
|
||||
);
|
||||
|
||||
// Aspettiamo che il TicketFormCubit faccia il suo lavoro
|
||||
targetId = await widget.onGenerateIdForQr!();
|
||||
}
|
||||
|
||||
// Se fallisce (es. validazione form non passata), ci fermiamo
|
||||
if (targetId == null) return;
|
||||
}
|
||||
|
||||
// GENERAZIONE DEL DEEP LINK AGNOSTICO
|
||||
final companyId = GetIt.I
|
||||
.get<SessionCubit>()
|
||||
.state
|
||||
.company!
|
||||
.id!;
|
||||
final deepLink =
|
||||
'https://flux.catelli.it/upload/${state.parentType.name}/$targetId?companyId=$companyId';
|
||||
|
||||
if (context.mounted) {
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (_) => QrUploadDialog(
|
||||
deepLinkUrl: deepLink,
|
||||
title: 'Carica File: ${widget.titleForUpload}',
|
||||
),
|
||||
);
|
||||
}
|
||||
},
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
|
||||
|
||||
@@ -8,8 +8,13 @@ import 'package:flux/features/attachments/blocs/attachments_bloc.dart';
|
||||
|
||||
class SharedMobileUploadScreen extends StatefulWidget {
|
||||
final String title;
|
||||
final String companyId;
|
||||
|
||||
const SharedMobileUploadScreen({super.key, required this.title});
|
||||
const SharedMobileUploadScreen({
|
||||
super.key,
|
||||
required this.title,
|
||||
required this.companyId,
|
||||
});
|
||||
|
||||
@override
|
||||
State<SharedMobileUploadScreen> createState() =>
|
||||
@@ -290,7 +295,10 @@ class _SharedMobileUploadScreenState extends State<SharedMobileUploadScreen> {
|
||||
}
|
||||
|
||||
Future<void> _handleFilePicker() async {
|
||||
final result = await FilePicker.pickFiles(allowMultiple: true);
|
||||
final result = await FilePicker.pickFiles(
|
||||
allowMultiple: true,
|
||||
withData: true,
|
||||
);
|
||||
if (result != null) {
|
||||
setState(() {
|
||||
_stagedFiles.addAll(result.files);
|
||||
@@ -304,7 +312,10 @@ class _SharedMobileUploadScreenState extends State<SharedMobileUploadScreen> {
|
||||
|
||||
// Lanciamo l'evento del nostro nuovo AttachmentsBloc Agnostico!
|
||||
context.read<AttachmentsBloc>().add(
|
||||
UploadAttachmentsEvent(pickedFiles: _stagedFiles),
|
||||
UploadAttachmentsEvent(
|
||||
pickedFiles: _stagedFiles,
|
||||
companyId: widget.companyId,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
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/widgets/qr_upload_dialog.dart';
|
||||
import 'package:flux/core/widgets/shared_forms/mobile_upload_screen.dart';
|
||||
import 'package:flux/features/attachments/blocs/attachments_bloc.dart';
|
||||
import 'package:get_it/get_it.dart';
|
||||
|
||||
class SharedFilesSection extends StatelessWidget {
|
||||
final String titleNameForUpload;
|
||||
@@ -64,8 +66,13 @@ class SharedFilesSection extends StatelessWidget {
|
||||
}
|
||||
|
||||
// GENERAZIONE DEL DEEP LINK AGNOSTICO
|
||||
final companyId = GetIt.I
|
||||
.get<SessionCubit>()
|
||||
.state
|
||||
.company!
|
||||
.id!;
|
||||
final deepLink =
|
||||
'https://flux.catelli.it/upload/${state.parentType.name}/$targetId';
|
||||
'https://flux.catelli.it/upload/${state.parentType.name}/$targetId?companyId=$companyId';
|
||||
|
||||
if (context.mounted) {
|
||||
showDialog(
|
||||
@@ -93,6 +100,11 @@ class SharedFilesSection extends StatelessWidget {
|
||||
value: bloc,
|
||||
child: SharedMobileUploadScreen(
|
||||
title: titleNameForUpload,
|
||||
companyId: GetIt.I
|
||||
.get<SessionCubit>()
|
||||
.state
|
||||
.company!
|
||||
.id!,
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
@@ -14,6 +14,7 @@ part 'attachments_state.dart';
|
||||
|
||||
class AttachmentsBloc extends Bloc<AttachmentsEvent, AttachmentsState> {
|
||||
final _repository = GetIt.I.get<AttachmentsRepository>();
|
||||
final String? companyId = GetIt.I.get<SessionCubit>().state.company?.id;
|
||||
|
||||
AttachmentsBloc({String? parentId, required AttachmentParentType parentType})
|
||||
: super(
|
||||
@@ -36,7 +37,7 @@ class AttachmentsBloc extends Bloc<AttachmentsEvent, AttachmentsState> {
|
||||
on<ClearAttachmentSelectionEvent>(_onClearAttachmentSelection);
|
||||
|
||||
// Se il BLoC nasce già con un ID, carichiamo i file
|
||||
if (parentId != null) {
|
||||
if (parentId != null && companyId != null) {
|
||||
add(LoadAttachmentsEvent(parentId: parentId));
|
||||
}
|
||||
}
|
||||
@@ -65,6 +66,7 @@ class AttachmentsBloc extends Bloc<AttachmentsEvent, AttachmentsState> {
|
||||
parentId: event.newParentId,
|
||||
parentType: state.parentType,
|
||||
pickedFile: fakePlatformFile,
|
||||
companyId: companyId!,
|
||||
);
|
||||
}).toList();
|
||||
|
||||
@@ -118,12 +120,11 @@ class AttachmentsBloc extends Bloc<AttachmentsEvent, AttachmentsState> {
|
||||
|
||||
// BIVIO 1: PRATICA NUOVA (Salvataggio locale)
|
||||
if (currentId == null) {
|
||||
final companyId = GetIt.I.get<SessionCubit>().state.company!.id!;
|
||||
final newLocalFiles = event.files.map((file) {
|
||||
// Assegniamo i campi dinamicamente in base al parentType!
|
||||
return AttachmentModel(
|
||||
id: null,
|
||||
companyId: companyId,
|
||||
companyId: companyId!,
|
||||
operationId: state.parentType == AttachmentParentType.operation
|
||||
? ''
|
||||
: null,
|
||||
@@ -156,6 +157,7 @@ class AttachmentsBloc extends Bloc<AttachmentsEvent, AttachmentsState> {
|
||||
parentId: currentId,
|
||||
parentType: state.parentType,
|
||||
pickedFile: file,
|
||||
companyId: companyId!,
|
||||
);
|
||||
}).toList();
|
||||
|
||||
@@ -191,6 +193,7 @@ class AttachmentsBloc extends Bloc<AttachmentsEvent, AttachmentsState> {
|
||||
parentId: state.parentId!,
|
||||
parentType: state.parentType,
|
||||
pickedFile: file,
|
||||
companyId: event.companyId,
|
||||
),
|
||||
);
|
||||
}
|
||||
@@ -216,6 +219,7 @@ class AttachmentsBloc extends Bloc<AttachmentsEvent, AttachmentsState> {
|
||||
parentId: state.parentId!,
|
||||
parentType: state.parentType,
|
||||
pickedFile: fakePlatformFile,
|
||||
companyId: companyId!,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -29,7 +29,12 @@ class AddAttachmentsEvent extends AttachmentsEvent {
|
||||
class UploadAttachmentsEvent extends AttachmentsEvent {
|
||||
final List<PlatformFile>? pickedFiles;
|
||||
final List<XFile>? photos;
|
||||
const UploadAttachmentsEvent({this.pickedFiles, this.photos});
|
||||
final String companyId;
|
||||
const UploadAttachmentsEvent({
|
||||
this.pickedFiles,
|
||||
this.photos,
|
||||
required this.companyId,
|
||||
});
|
||||
}
|
||||
|
||||
class DeleteAttachmentsEvent extends AttachmentsEvent {}
|
||||
|
||||
@@ -16,6 +16,7 @@ class AttachmentsState extends Equatable {
|
||||
final AttachmentParentType parentType;
|
||||
final AttachmentsStatus status;
|
||||
final String? error;
|
||||
|
||||
final List<AttachmentModel> localFiles;
|
||||
final List<AttachmentModel> remoteFiles;
|
||||
final List<AttachmentModel> selectedFiles;
|
||||
|
||||
@@ -3,8 +3,6 @@ import 'package:file_picker/file_picker.dart';
|
||||
import 'package:flux/features/attachments/models/attachment_model.dart';
|
||||
import 'package:supabase_flutter/supabase_flutter.dart';
|
||||
import 'package:flux/features/attachments/blocs/attachments_bloc.dart';
|
||||
import 'package:get_it/get_it.dart';
|
||||
import 'package:flux/core/blocs/session/session_cubit.dart';
|
||||
|
||||
class AttachmentsRepository {
|
||||
final _supabase = Supabase.instance.client;
|
||||
@@ -58,9 +56,9 @@ class AttachmentsRepository {
|
||||
required String parentId,
|
||||
required AttachmentParentType parentType,
|
||||
required PlatformFile pickedFile,
|
||||
required String companyId,
|
||||
}) async {
|
||||
try {
|
||||
final companyId = GetIt.I.get<SessionCubit>().state.company!.id!;
|
||||
final extension = pickedFile.extension ?? pickedFile.name.split('.').last;
|
||||
final cleanName = pickedFile.name
|
||||
.replaceAll(RegExp(r'[^\w\s\.-]'), '')
|
||||
|
||||
@@ -9,6 +9,7 @@ import 'package:flux/core/widgets/qr_upload_dialog.dart';
|
||||
import 'package:flux/features/attachments/blocs/attachments_bloc.dart';
|
||||
import 'package:flux/features/attachments/models/attachment_model.dart';
|
||||
import 'package:flux/features/customers/models/customer_model.dart';
|
||||
import 'package:get_it/get_it.dart';
|
||||
|
||||
class CustomerDetailScreen extends StatefulWidget {
|
||||
final CustomerModel customer;
|
||||
@@ -43,7 +44,12 @@ class _CustomerDetailScreenState extends State<CustomerDetailScreen> {
|
||||
|
||||
if (result != null) {
|
||||
try {
|
||||
attachmentsBloc.add(UploadAttachmentsEvent(pickedFiles: result.files));
|
||||
attachmentsBloc.add(
|
||||
UploadAttachmentsEvent(
|
||||
pickedFiles: result.files,
|
||||
companyId: GetIt.I.get<SessionCubit>().state.company!.id!,
|
||||
),
|
||||
);
|
||||
} catch (e) {
|
||||
if (mounted) {
|
||||
ScaffoldMessenger.of(
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:flux/core/widgets/shared_forms/shared_files_section.dart';
|
||||
import 'package:flux/features/attachments/blocs/attachments_bloc.dart';
|
||||
import 'package:flux/features/operations/blocs/operation_form_cubit.dart';
|
||||
import 'package:flux/features/operations/models/operation_model.dart';
|
||||
@@ -246,10 +245,12 @@ class _OperationFormScreenState extends State<OperationFormScreen> {
|
||||
flex: 3,
|
||||
child: SingleChildScrollView(
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
child: SharedFilesSection(
|
||||
titleNameForUpload:
|
||||
child: SharedAttachmentsSection(
|
||||
parentType: AttachmentParentType.operation,
|
||||
parentId: state.operation.id,
|
||||
titleForUpload:
|
||||
state.operation.customerDisplayName ??
|
||||
'Nuova operazione',
|
||||
'Nuova pratica',
|
||||
onGenerateIdForQr: _generateIdForQr,
|
||||
),
|
||||
),
|
||||
@@ -408,9 +409,9 @@ class _OperationFormScreenState extends State<OperationFormScreen> {
|
||||
color: _getStatusColor(displayStatus),
|
||||
),
|
||||
items: OperationStatus.values
|
||||
.where(
|
||||
/* .where(
|
||||
(s) => s != OperationStatus.draft,
|
||||
) // Nascondiamo 'Bozza' dal menu
|
||||
) // Nascondiamo 'Bozza' dal menu */
|
||||
.map(
|
||||
(status) => DropdownMenuItem(
|
||||
value: status,
|
||||
@@ -456,7 +457,7 @@ class _OperationFormScreenState extends State<OperationFormScreen> {
|
||||
),
|
||||
const Divider(height: 32),
|
||||
|
||||
_buildSectionTitle('Cliente & Riferimento'),
|
||||
//_buildSectionTitle('Cliente & Riferimento'),
|
||||
SharedCustomerSection(
|
||||
customerId: currentOp.customerId,
|
||||
customerName: currentOp.customerDisplayName,
|
||||
@@ -539,15 +540,18 @@ class _OperationFormScreenState extends State<OperationFormScreen> {
|
||||
const Divider(height: 32),
|
||||
|
||||
if (showFiles) ...[
|
||||
/* SharedAttachmentsSection(
|
||||
SharedAttachmentsSection(
|
||||
parentType: AttachmentParentType.operation,
|
||||
parentId: currentOp.id,
|
||||
), */
|
||||
SharedFilesSection(
|
||||
titleNameForUpload:
|
||||
titleForUpload:
|
||||
state.operation.customerDisplayName ?? 'Nuova pratica',
|
||||
onGenerateIdForQr: _generateIdForQr,
|
||||
),
|
||||
/* SharedFilesSection(
|
||||
titleNameForUpload:
|
||||
state.operation.customerDisplayName ?? 'Nuova pratica',
|
||||
onGenerateIdForQr: _generateIdForQr,
|
||||
), */
|
||||
],
|
||||
],
|
||||
);
|
||||
|
||||
@@ -45,6 +45,7 @@ class DetailsSection extends StatelessWidget {
|
||||
}
|
||||
|
||||
void _showProviderModal(BuildContext context, String operationType) {
|
||||
final OperationFormCubit cubit = context.read<OperationFormCubit>();
|
||||
showModalBottomSheet(
|
||||
context: context,
|
||||
isScrollControlled: true,
|
||||
@@ -103,28 +104,31 @@ class DetailsSection extends StatelessWidget {
|
||||
);
|
||||
}
|
||||
|
||||
return ListView.builder(
|
||||
controller: scrollController,
|
||||
itemCount: filteredProviders.length,
|
||||
itemBuilder: (context, index) {
|
||||
final provider = filteredProviders[index];
|
||||
return ListTile(
|
||||
leading: const Icon(Icons.business),
|
||||
title: Text(
|
||||
provider.name,
|
||||
style: const TextStyle(
|
||||
fontWeight: FontWeight.bold,
|
||||
return BlocProvider.value(
|
||||
value: cubit,
|
||||
child: ListView.builder(
|
||||
controller: scrollController,
|
||||
itemCount: filteredProviders.length,
|
||||
itemBuilder: (context, index) {
|
||||
final provider = filteredProviders[index];
|
||||
return ListTile(
|
||||
leading: const Icon(Icons.business),
|
||||
title: Text(
|
||||
provider.name,
|
||||
style: const TextStyle(
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
),
|
||||
onTap: () {
|
||||
context.read<OperationFormCubit>().updateFields(
|
||||
providerId: provider.id,
|
||||
providerDisplayName: provider.name,
|
||||
);
|
||||
Navigator.pop(modalContext);
|
||||
},
|
||||
);
|
||||
},
|
||||
onTap: () {
|
||||
context.read<OperationFormCubit>().updateFields(
|
||||
providerId: provider.id,
|
||||
providerDisplayName: provider.name,
|
||||
);
|
||||
Navigator.pop(modalContext);
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
|
||||
Reference in New Issue
Block a user