From f9980b1f2c5fae7b6a9c102ed7a59564abacac1c Mon Sep 17 00:00:00 2001 From: Alyssa Marielle Harding <44278215+alyssaraq98@users.noreply.github.com> Date: Fri, 1 Aug 2025 20:45:18 -0700 Subject: [PATCH] Fix file upload Fix file upload in file management --- .../lib/widgets/file_upload_widget.dart | 317 +++++++++--------- 1 file changed, 165 insertions(+), 152 deletions(-) diff --git a/careconnect2025/frontend/lib/widgets/file_upload_widget.dart b/careconnect2025/frontend/lib/widgets/file_upload_widget.dart index 67fad6e0..ec1560b4 100644 --- a/careconnect2025/frontend/lib/widgets/file_upload_widget.dart +++ b/careconnect2025/frontend/lib/widgets/file_upload_widget.dart @@ -1,6 +1,9 @@ import 'dart:io'; +import 'package:file_picker/file_picker.dart'; +import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; +import '../services/auth_token_manager.dart'; import '../services/comprehensive_file_service.dart'; import '../services/enhanced_file_service.dart'; import '../providers/user_provider.dart'; @@ -35,6 +38,8 @@ class _FileUploadWidgetState extends State { FileCategory? _selectedCategory; bool _isUploading = false; File? _selectedFile; + Uint8List? _selectedFileBytes; + String? _selectedFileName; final TextEditingController _descriptionController = TextEditingController(); @override @@ -43,6 +48,7 @@ class _FileUploadWidgetState extends State { final categories = _availableCategories; } + @override void dispose() { _descriptionController.dispose(); @@ -50,8 +56,7 @@ class _FileUploadWidgetState extends State { } List get _availableCategories1 { - if (widget.allowedCategories != null && - widget.allowedCategories!.isNotEmpty) { + if (widget.allowedCategories != null && widget.allowedCategories!.isNotEmpty) { return widget.allowedCategories!; } @@ -60,45 +65,44 @@ class _FileUploadWidgetState extends State { } List get _availableCategories { - if (widget.allowedCategories != null && - widget.allowedCategories!.isNotEmpty) { + if (widget.allowedCategories != null && widget.allowedCategories!.isNotEmpty) { return widget.allowedCategories!; } else { return FileCategory.values; } } + @override Widget build(BuildContext context) { return Card( - color: AppTheme.primary, child: Padding( padding: const EdgeInsets.all(16), - child: SingleChildScrollView( - child: Column( - crossAxisAlignment: CrossAxisAlignment.stretch, - children: [ - _buildHeader(), + child: Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + _buildHeader(), + const SizedBox(height: 16), + if (widget.showCategorySelector) ...[ + _buildCategorySelector(), const SizedBox(height: 16), - if (widget.showCategorySelector) ...[ - _buildCategorySelector(), - const SizedBox(height: 16), - ], - _buildFileSelector(), - if (_selectedFile != null) ...[ - const SizedBox(height: 16), - _buildFilePreview(), - const SizedBox(height: 16), - _buildDescriptionField(), - const SizedBox(height: 16), - ], - _buildUploadButton(), - if (_isUploading) ...[ - const SizedBox(height: 16), - const LinearProgressIndicator(), - ], ], - ), + _buildFileSelector(), + if (_selectedFile != null) ...[ + const SizedBox(height: 16), + /// Remove build file preview as it is not currently supported + /// _buildFilePreview(), + /// const SizedBox(height: 16), + _buildDescriptionField(), + const SizedBox(height: 16), + ], + const SizedBox(height: 16), + _buildUploadButton(), + if (_isUploading) ...[ + const SizedBox(height: 16), + const LinearProgressIndicator(), + ], + ], ), ), ); @@ -107,16 +111,12 @@ class _FileUploadWidgetState extends State { Widget _buildHeader() { return Row( children: [ - const Icon(Icons.cloud_upload, color: Colors.white), + Icon(Icons.cloud_upload, color: Theme.of(context).colorScheme.primary), const SizedBox(width: 8), Expanded( child: Text( widget.customTitle ?? 'Upload File', - style: const TextStyle( - color: Colors.white, - fontWeight: FontWeight.bold, - fontSize: 20, - ), + style: Theme.of(context).textTheme.headlineSmall, ), ), ], @@ -148,14 +148,11 @@ class _FileUploadWidgetState extends State { } return null; }, - value: _selectedCategory, // Starts as null! - hint: const Text('Select Category'), // This shows when value is null + value: _selectedCategory, // Starts as null! + hint: const Text('Select Category'), // This shows when value is null decoration: InputDecoration( border: OutlineInputBorder(borderRadius: BorderRadius.circular(8)), - contentPadding: const EdgeInsets.symmetric( - horizontal: 16, - vertical: 12, - ), + contentPadding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12), ), ); } @@ -203,8 +200,9 @@ class _FileUploadWidgetState extends State { const SizedBox(height: 8), Text( _selectedFile != null - ? 'File Selected: ${_selectedFile!.path.split('/').last}' - : _getFileInstructions(), + ? 'File Selected: ${_selectedFile!.path}' + : _selectedFileName != null ? 'File Selected: $_selectedFileName' : + _getFileInstructions(), textAlign: TextAlign.center, style: TextStyle( color: _selectedFile != null @@ -307,24 +305,27 @@ class _FileUploadWidgetState extends State { Widget _buildUploadButton() { final canUpload = - _selectedCategory != null && _selectedFile != null && !_isUploading; + _selectedCategory != null && + (_selectedFile != null || + (_selectedFileBytes != null && _selectedFileName != null)) + && !_isUploading; return ElevatedButton.icon( - onPressed: canUpload ? _uploadFile : null, - style: ElevatedButton.styleFrom( - backgroundColor: Colors.white, - foregroundColor: AppTheme.primary, - textStyle: const TextStyle(fontWeight: FontWeight.bold), + onPressed: canUpload ? _uploadFileWeb : null, + style: canUpload + ? Theme.of(context).elevatedButtonTheme.style + : ElevatedButton.styleFrom( + backgroundColor: Theme.of(context).disabledColor, + foregroundColor: Theme.of( + context, + ).colorScheme.onSurface.withOpacity(0.38), ), icon: _isUploading ? const SizedBox( - width: 20, - height: 20, - child: CircularProgressIndicator( - strokeWidth: 2, - color: AppTheme.primary, - ), - ) + width: 20, + height: 20, + child: CircularProgressIndicator(strokeWidth: 2), + ) : const Icon(Icons.cloud_upload), label: Text(_isUploading ? 'Uploading...' : 'Upload File'), ); @@ -394,29 +395,26 @@ class _FileUploadWidgetState extends State { } try { - final file = await ComprehensiveFileService.pickFileForCategory( - _selectedCategory!, - ); - if (file != null) { - // Validate file - if (!ComprehensiveFileService.validateFileForCategory( - file, + if (kIsWeb) { + final (Uint8List, String)? webSelectedFile = await ComprehensiveFileService.pickFileForCategoryWeb( _selectedCategory!, - )) { - ScaffoldMessenger.of(context).showSnackBar( - SnackBar( - content: Text( - 'Invalid file type for ${_selectedCategory!.displayName}', - ), - backgroundColor: AppTheme.error, - ), - ); - return; + ); + if (webSelectedFile != null) { + setState(() { + _selectedFileBytes = webSelectedFile.$1; + _selectedFileName = webSelectedFile.$2; + }); + } + } + else { + final File? file = await ComprehensiveFileService.pickFileForCategory( + _selectedCategory!, + ); + if (file != null) { + setState(() { + _selectedFile = file; + }); } - - setState(() { - _selectedFile = file; - }); } } catch (e) { ScaffoldMessenger.of(context).showSnackBar( @@ -447,76 +445,13 @@ class _FileUploadWidgetState extends State { ? null : _descriptionController.text.trim(); - // Always pass category.value (string) to upload methods - switch (_selectedCategory!) { - case FileCategory.profilePicture: - response = await ComprehensiveFileService.uploadProfileImage( - userId: user.id, - imageFile: _selectedFile!, - ); - break; - - case FileCategory.medicalReport: - case FileCategory.labResult: - response = await ComprehensiveFileService.uploadMedicalDocument( - patientId: widget.patientId ?? user.id, - documentFile: _selectedFile!, - category: _selectedCategory!, // Pass enum, not value - description: description, - ); - break; - - case FileCategory.prescription: - response = await ComprehensiveFileService.uploadPrescription( - patientId: widget.patientId ?? user.id, - prescriptionFile: _selectedFile!, - description: description, - ); - break; - - case FileCategory.clinicalNotes: - response = - await ComprehensiveFileService.uploadClinicalNotesAttachment( - patientId: widget.patientId ?? user.id, - attachmentFile: _selectedFile!, - description: description, - ); - break; - - case FileCategory.aiChatUpload: - response = await ComprehensiveFileService.uploadChatFile( - chatFile: _selectedFile!, - description: description, - ); - break; - - case FileCategory.insuranceDoc: - response = await ComprehensiveFileService.uploadInsuranceDocument( - patientId: widget.patientId ?? user.id, - insuranceFile: _selectedFile!, - description: description, - ); - break; - - case FileCategory.emergencyContact: - response = - await ComprehensiveFileService.uploadEmergencyContactDocument( - userId: user.id, - documentFile: _selectedFile!, - description: description, - ); - break; - - default: - // Use the existing enhanced file service for other categories - response = await EnhancedFileService.uploadFile( - file: _selectedFile!, - category: _selectedCategory!.value, // pass value, not enum - description: description, - patientId: widget.patientId, - ); - break; - } + // Use the existing enhanced file service for other categories + response = await EnhancedFileService.uploadFile( + file: _selectedFile!, + category: _selectedCategory!.value, + description: description, + patientId: widget.patientId, + ); if (response != null) { ScaffoldMessenger.of(context).showSnackBar( @@ -556,8 +491,86 @@ class _FileUploadWidgetState extends State { }); } } + + Future _uploadFileWeb() async { + + if (_selectedCategory == null || + _selectedFileBytes == null || + _selectedFileName == null) { + return; + } + + setState(() { + _isUploading = true; + }); + + try { + final userProvider = Provider.of(context, listen: false); + final user = userProvider.user; + if (user == null) { + throw Exception('User not logged in'); + } + + FileUploadResponse? response; + final description = _descriptionController.text.trim().isEmpty + ? null + : _descriptionController.text.trim(); + + // Use the existing enhanced file service for other categories + response = await EnhancedFileService.uploadFileWeb( + fileBytes: _selectedFileBytes!, + fileName: _selectedFileName!, + category: _selectedCategory!.value, + description: description, + patientId: widget.patientId, + ); + + if (response != null) { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Text( + 'File uploaded successfully: ${response.fileName}', + ), + backgroundColor: AppTheme.success, + ), + ); + + // Reset form + setState(() { + _selectedFile = null; + _selectedFileName = null; + _selectedFileBytes = null; + _descriptionController.clear(); + }); + + // Callback + if (widget.onUploadSuccess != null) { + widget.onUploadSuccess!(response); + } + } else { + throw Exception('Upload failed - no response received'); + } + } catch (e, stacktrace) { + print('Upload Exception: $e'); + print('Stacktrace: $stacktrace'); + final errorMessage = 'Upload failed: $e'; + ScaffoldMessenger.of(context).showSnackBar( + SnackBar(content: Text(errorMessage), backgroundColor: AppTheme.error), + ); + + if (widget.onUploadError != null) { + widget.onUploadError!(errorMessage); + } + } finally { + setState(() { + _isUploading = false; + }); + } + } } + + /// Quick upload buttons for common file types class QuickUploadButtons extends StatelessWidget { final int? patientId; @@ -619,11 +632,11 @@ class QuickUploadButtons extends StatelessWidget { } Widget _buildQuickButton( - BuildContext context, { - required IconData icon, - required String label, - required FileCategory category, - }) { + BuildContext context, { + required IconData icon, + required String label, + required FileCategory category, + }) { return ElevatedButton.icon( onPressed: () => _showUploadDialog(context, category), style: AppTheme.secondaryButtonStyle, @@ -662,4 +675,4 @@ class QuickUploadButtons extends StatelessWidget { }, ); } -} +} \ No newline at end of file