marco.pms.mobileapp/lib/model/directory/edit_bucket_bottom_sheet.dart
Vaibhav Surve 91184b48bb Refactor employee model imports and restructure employee-related files
- Updated import paths for employee model files to reflect new directory structure.
- Deleted obsolete models: JobRecentApplicationModel, LeadReportModel, Product, ProductOrderModal, ProjectSummaryModel, RecentOrderModel, TaskListModel, TimeLineModel, User, VisitorByChannelsModel.
- Introduced new AttendanceLogModel, AttendanceLogViewModel, AttendanceModel, TaskModel, TaskListModel, EmployeeInfo, and EmployeeModel with comprehensive fields and JSON serialization methods.
- Enhanced data handling in attendance and task management features.
2025-08-26 11:53:53 +05:30

234 lines
9.1 KiB
Dart

import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:collection/collection.dart';
import 'package:marco/controller/directory/manage_bucket_controller.dart';
import 'package:marco/controller/directory/directory_controller.dart';
import 'package:marco/helpers/utils/base_bottom_sheet.dart';
import 'package:marco/helpers/widgets/my_text.dart';
import 'package:marco/helpers/widgets/my_spacing.dart';
import 'package:marco/helpers/widgets/my_snackbar.dart';
import 'package:marco/model/employees/employee_model.dart';
import 'package:marco/model/directory/contact_bucket_list_model.dart';
class EditBucketBottomSheet {
static void show(
BuildContext context,
ContactBucket bucket,
List<EmployeeModel> allEmployees, {
required String ownerId,
}) {
final ManageBucketController controller = Get.find();
final nameController = TextEditingController(text: bucket.name);
final descController = TextEditingController(text: bucket.description);
final searchController = TextEditingController();
final selectedIds = RxSet<String>({...bucket.employeeIds});
final searchText = ''.obs;
InputDecoration _inputDecoration(String label) {
return InputDecoration(
labelText: label,
filled: true,
fillColor: Colors.grey.shade100,
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(12),
borderSide: BorderSide(color: Colors.grey.shade300),
),
enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(12),
borderSide: BorderSide(color: Colors.grey.shade300),
),
focusedBorder: const OutlineInputBorder(
borderRadius: BorderRadius.all(Radius.circular(12)),
borderSide: BorderSide(color: Colors.blueAccent, width: 1.5),
),
contentPadding:
const EdgeInsets.symmetric(horizontal: 16, vertical: 14),
isDense: true,
);
}
Future<void> _handleSubmit() async {
final newName = nameController.text.trim();
final newDesc = descController.text.trim();
final newEmployeeIds = selectedIds.toList()..sort();
final originalEmployeeIds = [...bucket.employeeIds]..sort();
final nameChanged = newName != bucket.name;
final descChanged = newDesc != bucket.description;
final employeeChanged =
!(const ListEquality().equals(newEmployeeIds, originalEmployeeIds));
if (!nameChanged && !descChanged && !employeeChanged) {
showAppSnackbar(
title: "No Changes",
message: "No changes were made to update the bucket.",
type: SnackbarType.warning,
);
return;
}
final success = await controller.updateBucket(
id: bucket.id,
name: newName,
description: newDesc,
employeeIds: newEmployeeIds,
originalEmployeeIds: originalEmployeeIds,
);
if (success) {
final directoryController = Get.find<DirectoryController>();
await directoryController.fetchBuckets();
Navigator.of(context).pop();
}
}
Widget _formContent() {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
TextField(
controller: nameController,
decoration: _inputDecoration('Bucket Name'),
),
MySpacing.height(16),
TextField(
controller: descController,
maxLines: 2,
decoration: _inputDecoration('Description'),
),
MySpacing.height(20),
MyText.labelLarge('Shared With', fontWeight: 600),
MySpacing.height(8),
Obx(() => TextField(
controller: searchController,
onChanged: (value) => searchText.value = value.toLowerCase(),
decoration: InputDecoration(
hintText: 'Search employee...',
prefixIcon: const Icon(Icons.search, size: 20),
suffixIcon: searchText.value.isNotEmpty
? IconButton(
icon: const Icon(Icons.clear, size: 18),
onPressed: () {
searchController.clear();
searchText.value = '';
},
)
: null,
isDense: true,
contentPadding:
const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
filled: true,
fillColor: Colors.grey.shade100,
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(10),
borderSide: BorderSide(color: Colors.grey.shade300),
),
),
)),
MySpacing.height(8),
Obx(() {
final filtered = allEmployees.where((emp) {
final fullName = '${emp.firstName} ${emp.lastName}'.toLowerCase();
return fullName.contains(searchText.value);
}).toList();
return SizedBox(
height: 180,
child: ListView.separated(
itemCount: filtered.length,
separatorBuilder: (_, __) => const SizedBox(height: 2),
itemBuilder: (context, index) {
final emp = filtered[index];
final fullName = '${emp.firstName} ${emp.lastName}'.trim();
return Obx(() => Theme(
data: Theme.of(context).copyWith(
unselectedWidgetColor: Colors.grey.shade500,
checkboxTheme: CheckboxThemeData(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(4)),
side: const BorderSide(color: Colors.grey),
fillColor:
MaterialStateProperty.resolveWith((states) {
if (states.contains(MaterialState.selected)) {
return Colors.blueAccent;
}
return Colors.white;
}),
checkColor: MaterialStateProperty.all(Colors.white),
),
),
child: CheckboxListTile(
dense: true,
contentPadding: EdgeInsets.zero,
visualDensity: const VisualDensity(vertical: -4),
controlAffinity: ListTileControlAffinity.leading,
value: selectedIds.contains(emp.id),
onChanged: emp.id == ownerId
? null
: (val) {
if (val == true) {
selectedIds.add(emp.id);
} else {
selectedIds.remove(emp.id);
}
},
title: Row(
children: [
Expanded(
child: MyText.bodyMedium(
fullName.isNotEmpty ? fullName : 'Unnamed',
fontWeight: 600,
),
),
if (emp.id == ownerId)
Container(
margin: const EdgeInsets.only(left: 6),
padding: const EdgeInsets.symmetric(
horizontal: 6, vertical: 2),
decoration: BoxDecoration(
color: Colors.red.shade50,
borderRadius: BorderRadius.circular(4),
),
child: MyText.labelSmall(
"Owner",
fontWeight: 600,
color: Colors.red,
),
),
],
),
subtitle: emp.jobRole.isNotEmpty
? MyText.bodySmall(
emp.jobRole,
color: Colors.grey.shade600,
)
: null,
),
));
},
),
);
}),
],
);
}
showModalBottomSheet(
context: context,
isScrollControlled: true,
backgroundColor: Colors.transparent,
builder: (context) {
return BaseBottomSheet(
title: "Edit Bucket",
onCancel: () => Navigator.pop(context),
onSubmit: _handleSubmit,
child: _formContent(),
);
},
);
}
}