From d4de5576622b4c9b33978bff45713c569f0b902b Mon Sep 17 00:00:00 2001 From: Phuoc Nguyen Date: Thu, 4 Dec 2025 10:39:47 +0700 Subject: [PATCH] fix update page --- ios/Runner.xcodeproj/project.pbxproj | 3 + .../pages/submission_create_page.dart | 144 +++++++++++------- pubspec.yaml | 2 +- 3 files changed, 90 insertions(+), 59 deletions(-) diff --git a/ios/Runner.xcodeproj/project.pbxproj b/ios/Runner.xcodeproj/project.pbxproj index c3a8e5c..a1f4921 100644 --- a/ios/Runner.xcodeproj/project.pbxproj +++ b/ios/Runner.xcodeproj/project.pbxproj @@ -632,6 +632,7 @@ ENABLE_BITCODE = NO; INFOPLIST_FILE = Runner/Info.plist; INFOPLIST_KEY_CFBundleDisplayName = "DBIZ Partner"; + IPHONEOS_DEPLOYMENT_TARGET = 15; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -934,6 +935,7 @@ ENABLE_BITCODE = NO; INFOPLIST_FILE = Runner/Info.plist; INFOPLIST_KEY_CFBundleDisplayName = "DBIZ Partner"; + IPHONEOS_DEPLOYMENT_TARGET = 15; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -959,6 +961,7 @@ ENABLE_BITCODE = NO; INFOPLIST_FILE = Runner/Info.plist; INFOPLIST_KEY_CFBundleDisplayName = "DBIZ Partner"; + IPHONEOS_DEPLOYMENT_TARGET = 15; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", diff --git a/lib/features/projects/presentation/pages/submission_create_page.dart b/lib/features/projects/presentation/pages/submission_create_page.dart index cc3e661..b0195a2 100644 --- a/lib/features/projects/presentation/pages/submission_create_page.dart +++ b/lib/features/projects/presentation/pages/submission_create_page.dart @@ -53,10 +53,14 @@ class _SubmissionCreatePageState extends ConsumerState { bool _isSubmitting = false; bool _isLoadingDetail = false; String? _deletingFileId; // Track which file is being deleted + bool _isAllowModify = true; // From API detail response /// Whether we're editing an existing submission bool get isEditing => widget.submission != null; + /// Whether the form is read-only (editing but not allowed to modify) + bool get isReadOnly => isEditing && !_isAllowModify; + @override void initState() { super.initState(); @@ -101,6 +105,9 @@ class _SubmissionCreatePageState extends ConsumerState { // Set existing files from API _existingFiles = detail.filesList; + + // Set modify permission from API + _isAllowModify = detail.isAllowModify; } catch (e) { if (mounted) { ScaffoldMessenger.of(context).showSnackBar( @@ -146,7 +153,11 @@ class _SubmissionCreatePageState extends ConsumerState { onPressed: () => Navigator.of(context).pop(), ), title: Text( - isEditing ? 'Chỉnh sửa Dự án' : 'Đăng ký Công trình', + isReadOnly + ? 'Chi tiết Dự án' + : isEditing + ? 'Chỉnh sửa Dự án' + : 'Đăng ký Công trình', style: TextStyle(color: colorScheme.onSurface), ), actions: [ @@ -197,10 +208,12 @@ class _SubmissionCreatePageState extends ConsumerState { // File Upload _buildFileUploadCard(colorScheme), - const SizedBox(height: 24), - // Submit Button - _buildSubmitButton(colorScheme), + // Submit Button - only show when not read-only + if (!isReadOnly) ...[ + const SizedBox(height: 24), + _buildSubmitButton(colorScheme), + ], const SizedBox(height: 40), ], ), @@ -376,49 +389,50 @@ class _SubmissionCreatePageState extends ConsumerState { ), const SizedBox(height: 16), - // Upload Area - InkWell( - onTap: _pickFiles, - borderRadius: BorderRadius.circular(8), - child: Container( - padding: const EdgeInsets.symmetric(vertical: 40, horizontal: 20), - width: double.infinity, - decoration: BoxDecoration( - border: Border.all( - color: colorScheme.surfaceContainerHighest, - width: 2, - style: BorderStyle.solid, + // Upload Area - only show when not read-only + if (!isReadOnly) + InkWell( + onTap: _pickFiles, + borderRadius: BorderRadius.circular(8), + child: Container( + padding: const EdgeInsets.symmetric(vertical: 40, horizontal: 20), + width: double.infinity, + decoration: BoxDecoration( + border: Border.all( + color: colorScheme.surfaceContainerHighest, + width: 2, + style: BorderStyle.solid, + ), + borderRadius: BorderRadius.circular(8), ), - borderRadius: BorderRadius.circular(8), - ), - child: Column( - children: [ - FaIcon( - FontAwesomeIcons.cloudArrowUp, - size: 48, - color: colorScheme.onSurfaceVariant, - ), - const SizedBox(height: 12), - Text( - 'Kéo thả ảnh vào đây', - style: TextStyle( - fontSize: 16, - fontWeight: FontWeight.w500, - color: colorScheme.onSurface, - ), - ), - const SizedBox(height: 4), - Text( - 'hoặc nhấn để chọn file', - style: TextStyle( - fontSize: 14, + child: Column( + children: [ + FaIcon( + FontAwesomeIcons.cloudArrowUp, + size: 48, color: colorScheme.onSurfaceVariant, ), - ), - ], + const SizedBox(height: 12), + Text( + 'Kéo thả ảnh vào đây', + style: TextStyle( + fontSize: 16, + fontWeight: FontWeight.w500, + color: colorScheme.onSurface, + ), + ), + const SizedBox(height: 4), + Text( + 'hoặc nhấn để chọn file', + style: TextStyle( + fontSize: 14, + color: colorScheme.onSurfaceVariant, + ), + ), + ], + ), ), ), - ), // Existing files from API if (_existingFiles.isNotEmpty) ...[ @@ -488,7 +502,9 @@ class _SubmissionCreatePageState extends ConsumerState { int maxLines = 1, TextInputType? keyboardType, String? helperText, + bool? readOnly, }) { + final isFieldReadOnly = readOnly ?? isReadOnly; return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ @@ -517,11 +533,12 @@ class _SubmissionCreatePageState extends ConsumerState { controller: controller, maxLines: maxLines, keyboardType: keyboardType, + readOnly: isFieldReadOnly, decoration: InputDecoration( hintText: hint, hintStyle: TextStyle(color: colorScheme.onSurfaceVariant), filled: true, - fillColor: colorScheme.surface, + fillColor: isFieldReadOnly ? colorScheme.surfaceContainerHighest : colorScheme.surface, border: OutlineInputBorder( borderRadius: BorderRadius.circular(8), borderSide: BorderSide(color: colorScheme.surfaceContainerHighest), @@ -594,10 +611,10 @@ class _SubmissionCreatePageState extends ConsumerState { const SizedBox(height: 8), progressListAsync.when( data: (progressList) => DropdownButtonFormField( - initialValue: _selectedProgress, + value: _selectedProgress, decoration: InputDecoration( filled: true, - fillColor: colorScheme.surface, + fillColor: isReadOnly ? colorScheme.surfaceContainerHighest : colorScheme.surface, border: OutlineInputBorder( borderRadius: BorderRadius.circular(8), borderSide: BorderSide(color: colorScheme.surfaceContainerHighest), @@ -618,11 +635,13 @@ class _SubmissionCreatePageState extends ConsumerState { child: Text(progress.status), )) .toList(), - onChanged: (value) { - setState(() { - _selectedProgress = value; - }); - }, + onChanged: isReadOnly + ? null + : (value) { + setState(() { + _selectedProgress = value; + }); + }, validator: (value) { if (value == null) { return 'Vui lòng chọn tiến độ công trình'; @@ -692,11 +711,11 @@ class _SubmissionCreatePageState extends ConsumerState { ), const SizedBox(height: 8), InkWell( - onTap: _pickExpectedDate, + onTap: isReadOnly ? null : _pickExpectedDate, child: Container( padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12), decoration: BoxDecoration( - color: colorScheme.surface, + color: isReadOnly ? colorScheme.surfaceContainerHighest : colorScheme.surface, border: Border.all(color: colorScheme.surfaceContainerHighest), borderRadius: BorderRadius.circular(8), ), @@ -847,8 +866,8 @@ class _SubmissionCreatePageState extends ConsumerState { ], ), ), - // Only show remove button when not uploading - if (!_isSubmitting) + // Only show remove button when not uploading and not read-only + if (!_isSubmitting && !isReadOnly) IconButton( icon: const FaIcon( FontAwesomeIcons.xmark, @@ -961,8 +980,8 @@ class _SubmissionCreatePageState extends ConsumerState { ], ), ), - // Delete button or checkmark - if (!_isSubmitting && !isDeleting) + // Delete button or checkmark - only show delete when not read-only + if (!_isSubmitting && !isDeleting && !isReadOnly) IconButton( icon: const FaIcon( FontAwesomeIcons.trash, @@ -1166,10 +1185,19 @@ class _SubmissionCreatePageState extends ConsumerState { } Future _pickExpectedDate() async { + final now = DateTime.now(); + final today = DateTime(now.year, now.month, now.day); + + // For editing mode with past date, allow selecting from that date + // Otherwise, start from today + final firstDate = (_expectedStartDate != null && _expectedStartDate!.isBefore(today)) + ? _expectedStartDate! + : today; + final date = await showDatePicker( context: context, - initialDate: _expectedStartDate ?? DateTime.now(), - firstDate: DateTime.now(), + initialDate: _expectedStartDate ?? today, + firstDate: firstDate, lastDate: DateTime.now().add(const Duration(days: 365 * 3)), ); diff --git a/pubspec.yaml b/pubspec.yaml index 0e28655..9bb2dcb 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -16,7 +16,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html # In Windows, build-name is used as the major, minor, and patch parts # of the product and file versions while build-number is used as the build suffix. -version: 1.0.1+23 +version: 1.0.1+24 environment: sdk: ^3.10.0