feat: Add camera functionality to expense attachment; update logger configuration for improved performance
This commit is contained in:
parent
61acbb019b
commit
fd7f108a20
@ -16,6 +16,7 @@ import 'package:marco/model/employees/employee_model.dart';
|
||||
import 'package:marco/model/expense/expense_type_model.dart';
|
||||
import 'package:marco/model/expense/payment_types_model.dart';
|
||||
import 'package:mime/mime.dart';
|
||||
import 'package:image_picker/image_picker.dart';
|
||||
|
||||
class AddExpenseController extends GetxController {
|
||||
// --- Text Controllers ---
|
||||
@ -57,7 +58,7 @@ class AddExpenseController extends GetxController {
|
||||
String? editingExpenseId;
|
||||
|
||||
final expenseController = Get.find<ExpenseController>();
|
||||
|
||||
final ImagePicker _picker = ImagePicker();
|
||||
@override
|
||||
void onInit() {
|
||||
super.onInit();
|
||||
@ -308,6 +309,17 @@ class AddExpenseController extends GetxController {
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> pickFromCamera() async {
|
||||
try {
|
||||
final pickedFile = await _picker.pickImage(source: ImageSource.camera);
|
||||
if (pickedFile != null) {
|
||||
attachments.add(File(pickedFile.path));
|
||||
}
|
||||
} catch (e) {
|
||||
_errorSnackbar("Camera error: $e");
|
||||
}
|
||||
}
|
||||
|
||||
// --- Submission ---
|
||||
Future<void> submitOrUpdateExpense() async {
|
||||
if (isSubmitting.value) return;
|
||||
@ -361,13 +373,11 @@ class AddExpenseController extends GetxController {
|
||||
Future<Map<String, dynamic>> _buildExpensePayload() async {
|
||||
final now = DateTime.now();
|
||||
|
||||
// Determine if attachments were changed
|
||||
bool attachmentsChanged =
|
||||
attachments.isNotEmpty || existingAttachments.any((e) => e['isActive'] == false);
|
||||
|
||||
// Existing attachments payload
|
||||
final existingAttachmentPayloads = attachmentsChanged
|
||||
? existingAttachments.map((e) => {
|
||||
// --- Existing Attachments Payload (for edit mode only) ---
|
||||
final List<Map<String, dynamic>> existingAttachmentPayloads =
|
||||
isEditMode.value
|
||||
? existingAttachments
|
||||
.map<Map<String, dynamic>>((e) => {
|
||||
"documentId": e['documentId'],
|
||||
"fileName": e['fileName'],
|
||||
"contentType": e['contentType'],
|
||||
@ -375,28 +385,39 @@ Future<Map<String, dynamic>> _buildExpensePayload() async {
|
||||
"description": "",
|
||||
"url": e['url'],
|
||||
"isActive": e['isActive'] ?? true,
|
||||
// If attachment removed, base64Data should be empty array
|
||||
"base64Data": e['isActive'] == false ? "" : e['base64Data'],
|
||||
}).toList()
|
||||
: [];
|
||||
"base64Data": "", // <-- always empty now
|
||||
})
|
||||
.toList()
|
||||
: <Map<String, dynamic>>[];
|
||||
|
||||
// New attachments payload
|
||||
final newAttachmentPayloads = attachmentsChanged
|
||||
// --- New Attachments Payload (always include if attachments exist) ---
|
||||
final List<Map<String, dynamic>> newAttachmentPayloads =
|
||||
attachments.isNotEmpty
|
||||
? await Future.wait(attachments.map((file) async {
|
||||
final bytes = await file.readAsBytes();
|
||||
return {
|
||||
final length = await file.length();
|
||||
return <String, dynamic>{
|
||||
"fileName": file.path.split('/').last,
|
||||
"base64Data": base64Encode(bytes),
|
||||
"contentType": lookupMimeType(file.path) ?? 'application/octet-stream',
|
||||
"fileSize": await file.length(),
|
||||
"contentType":
|
||||
lookupMimeType(file.path) ?? 'application/octet-stream',
|
||||
"fileSize": length,
|
||||
"description": "",
|
||||
};
|
||||
}))
|
||||
: [];
|
||||
: <Map<String, dynamic>>[];
|
||||
|
||||
// --- Selected Expense Type ---
|
||||
final type = selectedExpenseType.value!;
|
||||
|
||||
return {
|
||||
// --- Combine all attachments ---
|
||||
final List<Map<String, dynamic>> combinedAttachments = [
|
||||
...existingAttachmentPayloads,
|
||||
...newAttachmentPayloads
|
||||
];
|
||||
|
||||
// --- Build Payload ---
|
||||
final payload = <String, dynamic>{
|
||||
if (isEditMode.value && editingExpenseId != null) "id": editingExpenseId,
|
||||
"projectId": projectsMap[selectedProject.value]!,
|
||||
"expensesTypeId": type.id,
|
||||
@ -412,11 +433,11 @@ Future<Map<String, dynamic>> _buildExpensePayload() async {
|
||||
"noOfPersons": type.noOfPersonsRequired == true
|
||||
? int.tryParse(noOfPersonsController.text.trim()) ?? 0
|
||||
: 0,
|
||||
// Attachments logic
|
||||
"billAttachments": isEditMode.value && !attachmentsChanged
|
||||
? null
|
||||
: [...existingAttachmentPayloads, ...newAttachmentPayloads],
|
||||
"billAttachments":
|
||||
combinedAttachments.isEmpty ? null : combinedAttachments,
|
||||
};
|
||||
|
||||
return payload;
|
||||
}
|
||||
|
||||
String validateForm() {
|
||||
|
@ -18,10 +18,10 @@ bool _isPosting = false;
|
||||
bool _canPostLogs = false;
|
||||
|
||||
/// Maximum number of logs before triggering API post
|
||||
const int _maxLogsBeforePost = 50;
|
||||
const int _maxLogsBeforePost = 100;
|
||||
|
||||
/// Maximum logs in memory buffer
|
||||
const int _maxBufferSize = 50;
|
||||
const int _maxBufferSize = 500;
|
||||
|
||||
/// Enum → logger level mapping
|
||||
const _levelMap = {
|
||||
|
@ -791,6 +791,8 @@ class _AttachmentsSection extends StatelessWidget {
|
||||
),
|
||||
);
|
||||
}),
|
||||
|
||||
// 📎 File Picker Button
|
||||
GestureDetector(
|
||||
onTap: onAdd,
|
||||
child: Container(
|
||||
@ -801,7 +803,24 @@ class _AttachmentsSection extends StatelessWidget {
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
color: Colors.grey.shade100,
|
||||
),
|
||||
child: const Icon(Icons.add, size: 30, color: Colors.grey),
|
||||
child: const Icon(Icons.attach_file,
|
||||
size: 30, color: Colors.grey),
|
||||
),
|
||||
),
|
||||
|
||||
// 📷 Camera Button
|
||||
GestureDetector(
|
||||
onTap: () => Get.find<AddExpenseController>().pickFromCamera(),
|
||||
child: Container(
|
||||
width: 80,
|
||||
height: 80,
|
||||
decoration: BoxDecoration(
|
||||
border: Border.all(color: Colors.grey.shade400),
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
color: Colors.grey.shade100,
|
||||
),
|
||||
child: const Icon(Icons.camera_alt,
|
||||
size: 30, color: Colors.grey),
|
||||
),
|
||||
),
|
||||
],
|
||||
|
Loading…
x
Reference in New Issue
Block a user