fix update page

This commit is contained in:
Phuoc Nguyen
2025-12-04 10:39:47 +07:00
parent 8ff7b3b505
commit d4de557662
3 changed files with 90 additions and 59 deletions

View File

@@ -632,6 +632,7 @@
ENABLE_BITCODE = NO; ENABLE_BITCODE = NO;
INFOPLIST_FILE = Runner/Info.plist; INFOPLIST_FILE = Runner/Info.plist;
INFOPLIST_KEY_CFBundleDisplayName = "DBIZ Partner"; INFOPLIST_KEY_CFBundleDisplayName = "DBIZ Partner";
IPHONEOS_DEPLOYMENT_TARGET = 15;
LD_RUNPATH_SEARCH_PATHS = ( LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)", "$(inherited)",
"@executable_path/Frameworks", "@executable_path/Frameworks",
@@ -934,6 +935,7 @@
ENABLE_BITCODE = NO; ENABLE_BITCODE = NO;
INFOPLIST_FILE = Runner/Info.plist; INFOPLIST_FILE = Runner/Info.plist;
INFOPLIST_KEY_CFBundleDisplayName = "DBIZ Partner"; INFOPLIST_KEY_CFBundleDisplayName = "DBIZ Partner";
IPHONEOS_DEPLOYMENT_TARGET = 15;
LD_RUNPATH_SEARCH_PATHS = ( LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)", "$(inherited)",
"@executable_path/Frameworks", "@executable_path/Frameworks",
@@ -959,6 +961,7 @@
ENABLE_BITCODE = NO; ENABLE_BITCODE = NO;
INFOPLIST_FILE = Runner/Info.plist; INFOPLIST_FILE = Runner/Info.plist;
INFOPLIST_KEY_CFBundleDisplayName = "DBIZ Partner"; INFOPLIST_KEY_CFBundleDisplayName = "DBIZ Partner";
IPHONEOS_DEPLOYMENT_TARGET = 15;
LD_RUNPATH_SEARCH_PATHS = ( LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)", "$(inherited)",
"@executable_path/Frameworks", "@executable_path/Frameworks",

View File

@@ -53,10 +53,14 @@ class _SubmissionCreatePageState extends ConsumerState<SubmissionCreatePage> {
bool _isSubmitting = false; bool _isSubmitting = false;
bool _isLoadingDetail = false; bool _isLoadingDetail = false;
String? _deletingFileId; // Track which file is being deleted String? _deletingFileId; // Track which file is being deleted
bool _isAllowModify = true; // From API detail response
/// Whether we're editing an existing submission /// Whether we're editing an existing submission
bool get isEditing => widget.submission != null; bool get isEditing => widget.submission != null;
/// Whether the form is read-only (editing but not allowed to modify)
bool get isReadOnly => isEditing && !_isAllowModify;
@override @override
void initState() { void initState() {
super.initState(); super.initState();
@@ -101,6 +105,9 @@ class _SubmissionCreatePageState extends ConsumerState<SubmissionCreatePage> {
// Set existing files from API // Set existing files from API
_existingFiles = detail.filesList; _existingFiles = detail.filesList;
// Set modify permission from API
_isAllowModify = detail.isAllowModify;
} catch (e) { } catch (e) {
if (mounted) { if (mounted) {
ScaffoldMessenger.of(context).showSnackBar( ScaffoldMessenger.of(context).showSnackBar(
@@ -146,7 +153,11 @@ class _SubmissionCreatePageState extends ConsumerState<SubmissionCreatePage> {
onPressed: () => Navigator.of(context).pop(), onPressed: () => Navigator.of(context).pop(),
), ),
title: Text( 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), style: TextStyle(color: colorScheme.onSurface),
), ),
actions: [ actions: [
@@ -197,10 +208,12 @@ class _SubmissionCreatePageState extends ConsumerState<SubmissionCreatePage> {
// File Upload // File Upload
_buildFileUploadCard(colorScheme), _buildFileUploadCard(colorScheme),
const SizedBox(height: 24),
// Submit Button // Submit Button - only show when not read-only
_buildSubmitButton(colorScheme), if (!isReadOnly) ...[
const SizedBox(height: 24),
_buildSubmitButton(colorScheme),
],
const SizedBox(height: 40), const SizedBox(height: 40),
], ],
), ),
@@ -376,49 +389,50 @@ class _SubmissionCreatePageState extends ConsumerState<SubmissionCreatePage> {
), ),
const SizedBox(height: 16), const SizedBox(height: 16),
// Upload Area // Upload Area - only show when not read-only
InkWell( if (!isReadOnly)
onTap: _pickFiles, InkWell(
borderRadius: BorderRadius.circular(8), onTap: _pickFiles,
child: Container( borderRadius: BorderRadius.circular(8),
padding: const EdgeInsets.symmetric(vertical: 40, horizontal: 20), child: Container(
width: double.infinity, padding: const EdgeInsets.symmetric(vertical: 40, horizontal: 20),
decoration: BoxDecoration( width: double.infinity,
border: Border.all( decoration: BoxDecoration(
color: colorScheme.surfaceContainerHighest, border: Border.all(
width: 2, color: colorScheme.surfaceContainerHighest,
style: BorderStyle.solid, width: 2,
style: BorderStyle.solid,
),
borderRadius: BorderRadius.circular(8),
), ),
borderRadius: BorderRadius.circular(8), child: Column(
), children: [
child: Column( FaIcon(
children: [ FontAwesomeIcons.cloudArrowUp,
FaIcon( size: 48,
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, 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 // Existing files from API
if (_existingFiles.isNotEmpty) ...[ if (_existingFiles.isNotEmpty) ...[
@@ -488,7 +502,9 @@ class _SubmissionCreatePageState extends ConsumerState<SubmissionCreatePage> {
int maxLines = 1, int maxLines = 1,
TextInputType? keyboardType, TextInputType? keyboardType,
String? helperText, String? helperText,
bool? readOnly,
}) { }) {
final isFieldReadOnly = readOnly ?? isReadOnly;
return Column( return Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
@@ -517,11 +533,12 @@ class _SubmissionCreatePageState extends ConsumerState<SubmissionCreatePage> {
controller: controller, controller: controller,
maxLines: maxLines, maxLines: maxLines,
keyboardType: keyboardType, keyboardType: keyboardType,
readOnly: isFieldReadOnly,
decoration: InputDecoration( decoration: InputDecoration(
hintText: hint, hintText: hint,
hintStyle: TextStyle(color: colorScheme.onSurfaceVariant), hintStyle: TextStyle(color: colorScheme.onSurfaceVariant),
filled: true, filled: true,
fillColor: colorScheme.surface, fillColor: isFieldReadOnly ? colorScheme.surfaceContainerHighest : colorScheme.surface,
border: OutlineInputBorder( border: OutlineInputBorder(
borderRadius: BorderRadius.circular(8), borderRadius: BorderRadius.circular(8),
borderSide: BorderSide(color: colorScheme.surfaceContainerHighest), borderSide: BorderSide(color: colorScheme.surfaceContainerHighest),
@@ -594,10 +611,10 @@ class _SubmissionCreatePageState extends ConsumerState<SubmissionCreatePage> {
const SizedBox(height: 8), const SizedBox(height: 8),
progressListAsync.when( progressListAsync.when(
data: (progressList) => DropdownButtonFormField<ProjectProgress>( data: (progressList) => DropdownButtonFormField<ProjectProgress>(
initialValue: _selectedProgress, value: _selectedProgress,
decoration: InputDecoration( decoration: InputDecoration(
filled: true, filled: true,
fillColor: colorScheme.surface, fillColor: isReadOnly ? colorScheme.surfaceContainerHighest : colorScheme.surface,
border: OutlineInputBorder( border: OutlineInputBorder(
borderRadius: BorderRadius.circular(8), borderRadius: BorderRadius.circular(8),
borderSide: BorderSide(color: colorScheme.surfaceContainerHighest), borderSide: BorderSide(color: colorScheme.surfaceContainerHighest),
@@ -618,11 +635,13 @@ class _SubmissionCreatePageState extends ConsumerState<SubmissionCreatePage> {
child: Text(progress.status), child: Text(progress.status),
)) ))
.toList(), .toList(),
onChanged: (value) { onChanged: isReadOnly
setState(() { ? null
_selectedProgress = value; : (value) {
}); setState(() {
}, _selectedProgress = value;
});
},
validator: (value) { validator: (value) {
if (value == null) { if (value == null) {
return 'Vui lòng chọn tiến độ công trình'; return 'Vui lòng chọn tiến độ công trình';
@@ -692,11 +711,11 @@ class _SubmissionCreatePageState extends ConsumerState<SubmissionCreatePage> {
), ),
const SizedBox(height: 8), const SizedBox(height: 8),
InkWell( InkWell(
onTap: _pickExpectedDate, onTap: isReadOnly ? null : _pickExpectedDate,
child: Container( child: Container(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12), padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
decoration: BoxDecoration( decoration: BoxDecoration(
color: colorScheme.surface, color: isReadOnly ? colorScheme.surfaceContainerHighest : colorScheme.surface,
border: Border.all(color: colorScheme.surfaceContainerHighest), border: Border.all(color: colorScheme.surfaceContainerHighest),
borderRadius: BorderRadius.circular(8), borderRadius: BorderRadius.circular(8),
), ),
@@ -847,8 +866,8 @@ class _SubmissionCreatePageState extends ConsumerState<SubmissionCreatePage> {
], ],
), ),
), ),
// Only show remove button when not uploading // Only show remove button when not uploading and not read-only
if (!_isSubmitting) if (!_isSubmitting && !isReadOnly)
IconButton( IconButton(
icon: const FaIcon( icon: const FaIcon(
FontAwesomeIcons.xmark, FontAwesomeIcons.xmark,
@@ -961,8 +980,8 @@ class _SubmissionCreatePageState extends ConsumerState<SubmissionCreatePage> {
], ],
), ),
), ),
// Delete button or checkmark // Delete button or checkmark - only show delete when not read-only
if (!_isSubmitting && !isDeleting) if (!_isSubmitting && !isDeleting && !isReadOnly)
IconButton( IconButton(
icon: const FaIcon( icon: const FaIcon(
FontAwesomeIcons.trash, FontAwesomeIcons.trash,
@@ -1166,10 +1185,19 @@ class _SubmissionCreatePageState extends ConsumerState<SubmissionCreatePage> {
} }
Future<void> _pickExpectedDate() async { Future<void> _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( final date = await showDatePicker(
context: context, context: context,
initialDate: _expectedStartDate ?? DateTime.now(), initialDate: _expectedStartDate ?? today,
firstDate: DateTime.now(), firstDate: firstDate,
lastDate: DateTime.now().add(const Duration(days: 365 * 3)), lastDate: DateTime.now().add(const Duration(days: 365 * 3)),
); );

View File

@@ -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 # 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 # 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. # 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: environment:
sdk: ^3.10.0 sdk: ^3.10.0