feat: Enhance organization selection and fetching logic with reactive state management
This commit is contained in:
parent
1d9c416f68
commit
85d3dedbef
@ -4,37 +4,49 @@ import 'package:marco/helpers/services/api_service.dart';
|
|||||||
import 'package:marco/model/attendance/organization_per_project_list_model.dart';
|
import 'package:marco/model/attendance/organization_per_project_list_model.dart';
|
||||||
|
|
||||||
class OrganizationController extends GetxController {
|
class OrganizationController extends GetxController {
|
||||||
|
/// List of organizations assigned to the selected project
|
||||||
List<Organization> organizations = [];
|
List<Organization> organizations = [];
|
||||||
Organization? selectedOrganization;
|
|
||||||
|
/// Currently selected organization (reactive)
|
||||||
|
Rxn<Organization> selectedOrganization = Rxn<Organization>();
|
||||||
|
|
||||||
|
/// Loading state for fetching organizations
|
||||||
final isLoadingOrganizations = false.obs;
|
final isLoadingOrganizations = false.obs;
|
||||||
|
|
||||||
|
/// Fetch organizations assigned to a given project
|
||||||
Future<void> fetchOrganizations(String projectId) async {
|
Future<void> fetchOrganizations(String projectId) async {
|
||||||
try {
|
try {
|
||||||
isLoadingOrganizations.value = true;
|
isLoadingOrganizations.value = true;
|
||||||
|
|
||||||
final response = await ApiService.getAssignedOrganizations(projectId);
|
final response = await ApiService.getAssignedOrganizations(projectId);
|
||||||
if (response != null) {
|
if (response != null && response.data.isNotEmpty) {
|
||||||
organizations = response.data;
|
organizations = response.data;
|
||||||
logSafe("Organizations fetched: ${organizations.length}");
|
logSafe("Organizations fetched: ${organizations.length}");
|
||||||
} else {
|
} else {
|
||||||
logSafe("Failed to fetch organizations for project $projectId",
|
organizations = [];
|
||||||
level: LogLevel.error);
|
logSafe("No organizations found for project $projectId",
|
||||||
|
level: LogLevel.warning);
|
||||||
}
|
}
|
||||||
|
} catch (e, stackTrace) {
|
||||||
|
logSafe("Failed to fetch organizations: $e",
|
||||||
|
level: LogLevel.error, error: e, stackTrace: stackTrace);
|
||||||
|
organizations = [];
|
||||||
} finally {
|
} finally {
|
||||||
isLoadingOrganizations.value = false;
|
isLoadingOrganizations.value = false;
|
||||||
update();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Select an organization
|
||||||
void selectOrganization(Organization? org) {
|
void selectOrganization(Organization? org) {
|
||||||
selectedOrganization = org;
|
selectedOrganization.value = org;
|
||||||
update();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Clear the selection (set to "All Organizations")
|
||||||
void clearSelection() {
|
void clearSelection() {
|
||||||
selectedOrganization = null;
|
selectedOrganization.value = null;
|
||||||
update();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Current selection name for UI
|
||||||
String get currentSelection =>
|
String get currentSelection =>
|
||||||
selectedOrganization?.name ?? "All Organizations";
|
selectedOrganization.value?.name ?? "All Organizations";
|
||||||
}
|
}
|
||||||
|
@ -24,8 +24,8 @@ class AttendanceActionColors {
|
|||||||
ButtonActions.rejected: Colors.orange,
|
ButtonActions.rejected: Colors.orange,
|
||||||
ButtonActions.approved: Colors.green,
|
ButtonActions.approved: Colors.green,
|
||||||
ButtonActions.requested: Colors.yellow,
|
ButtonActions.requested: Colors.yellow,
|
||||||
ButtonActions.approve: Colors.blueAccent,
|
ButtonActions.approve: Colors.green,
|
||||||
ButtonActions.reject: Colors.pink,
|
ButtonActions.reject: Colors.red,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -25,7 +25,7 @@ class OrganizationSelector extends StatelessWidget {
|
|||||||
required List<String> items,
|
required List<String> items,
|
||||||
}) {
|
}) {
|
||||||
return PopupMenuButton<String>(
|
return PopupMenuButton<String>(
|
||||||
color: Colors.white,
|
color: Colors.white,
|
||||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
|
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
|
||||||
onSelected: (name) async {
|
onSelected: (name) async {
|
||||||
Organization? org = name == "All Organizations"
|
Organization? org = name == "All Organizations"
|
||||||
@ -45,8 +45,7 @@ class OrganizationSelector extends StatelessWidget {
|
|||||||
height: height,
|
height: height,
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 12),
|
padding: const EdgeInsets.symmetric(horizontal: 12),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color:
|
color: Colors.white,
|
||||||
Colors.white,
|
|
||||||
border: Border.all(color: Colors.grey.shade300),
|
border: Border.all(color: Colors.grey.shade300),
|
||||||
borderRadius: BorderRadius.circular(12),
|
borderRadius: BorderRadius.circular(12),
|
||||||
),
|
),
|
||||||
@ -97,6 +96,7 @@ class OrganizationSelector extends StatelessWidget {
|
|||||||
...controller.organizations.map((e) => e.name)
|
...controller.organizations.map((e) => e.name)
|
||||||
];
|
];
|
||||||
|
|
||||||
|
// Listen to selectedOrganization.value
|
||||||
return _popupSelector(
|
return _popupSelector(
|
||||||
currentValue: controller.currentSelection,
|
currentValue: controller.currentSelection,
|
||||||
items: orgNames,
|
items: orgNames,
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:intl/intl.dart';
|
|
||||||
import 'package:marco/controller/permission_controller.dart';
|
import 'package:marco/controller/permission_controller.dart';
|
||||||
import 'package:marco/controller/attendance/attendance_screen_controller.dart';
|
import 'package:marco/controller/attendance/attendance_screen_controller.dart';
|
||||||
import 'package:marco/helpers/widgets/my_text.dart';
|
import 'package:marco/helpers/widgets/my_text.dart';
|
||||||
import 'package:marco/helpers/utils/permission_constants.dart';
|
import 'package:marco/helpers/utils/permission_constants.dart';
|
||||||
import 'package:marco/helpers/utils/base_bottom_sheet.dart';
|
import 'package:marco/helpers/utils/base_bottom_sheet.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
|
import 'package:marco/helpers/utils/date_time_utils.dart';
|
||||||
|
|
||||||
class AttendanceFilterBottomSheet extends StatefulWidget {
|
class AttendanceFilterBottomSheet extends StatefulWidget {
|
||||||
final AttendanceController controller;
|
final AttendanceController controller;
|
||||||
@ -37,9 +37,11 @@ class _AttendanceFilterBottomSheetState
|
|||||||
String getLabelText() {
|
String getLabelText() {
|
||||||
final startDate = widget.controller.startDateAttendance;
|
final startDate = widget.controller.startDateAttendance;
|
||||||
final endDate = widget.controller.endDateAttendance;
|
final endDate = widget.controller.endDateAttendance;
|
||||||
|
|
||||||
if (startDate != null && endDate != null) {
|
if (startDate != null && endDate != null) {
|
||||||
final start = DateFormat('dd/MM/yyyy').format(startDate);
|
final start =
|
||||||
final end = DateFormat('dd/MM/yyyy').format(endDate);
|
DateTimeUtils.formatDate(startDate, 'dd MMM yyyy');
|
||||||
|
final end = DateTimeUtils.formatDate(endDate, 'dd MMM yyyy');
|
||||||
return "$start - $end";
|
return "$start - $end";
|
||||||
}
|
}
|
||||||
return "Date Range";
|
return "Date Range";
|
||||||
@ -96,7 +98,7 @@ class _AttendanceFilterBottomSheetState
|
|||||||
onSelected: (name) {
|
onSelected: (name) {
|
||||||
if (name == "All Organizations") {
|
if (name == "All Organizations") {
|
||||||
setState(() {
|
setState(() {
|
||||||
widget.controller.selectedOrganization = null;
|
widget.controller.selectedOrganization = null;
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
final selectedOrg = widget.controller.organizations
|
final selectedOrg = widget.controller.organizations
|
||||||
@ -154,7 +156,7 @@ class _AttendanceFilterBottomSheetState
|
|||||||
padding: const EdgeInsets.only(top: 12, bottom: 12),
|
padding: const EdgeInsets.only(top: 12, bottom: 12),
|
||||||
child: Align(
|
child: Align(
|
||||||
alignment: Alignment.centerLeft,
|
alignment: Alignment.centerLeft,
|
||||||
child: MyText.titleSmall("Select Organization", fontWeight: 600),
|
child: MyText.titleSmall("Choose Organization", fontWeight: 600),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Obx(() {
|
Obx(() {
|
||||||
@ -231,7 +233,7 @@ class _AttendanceFilterBottomSheetState
|
|||||||
borderRadius: const BorderRadius.vertical(top: Radius.circular(24)),
|
borderRadius: const BorderRadius.vertical(top: Radius.circular(24)),
|
||||||
child: BaseBottomSheet(
|
child: BaseBottomSheet(
|
||||||
title: "Attendance Filter",
|
title: "Attendance Filter",
|
||||||
submitText: "Apply",
|
submitText: "Apply",
|
||||||
onCancel: () => Navigator.pop(context),
|
onCancel: () => Navigator.pop(context),
|
||||||
onSubmit: () => Navigator.pop(context, {
|
onSubmit: () => Navigator.pop(context, {
|
||||||
'selectedTab': tempSelectedTab,
|
'selectedTab': tempSelectedTab,
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:url_launcher/url_launcher.dart';
|
import 'package:url_launcher/url_launcher.dart';
|
||||||
import 'package:marco/helpers/widgets/my_text.dart';
|
import 'package:marco/helpers/widgets/my_text.dart';
|
||||||
import 'package:marco/helpers/utils/attendance_actions.dart';
|
|
||||||
import 'package:marco/helpers/utils/base_bottom_sheet.dart';
|
import 'package:marco/helpers/utils/base_bottom_sheet.dart';
|
||||||
|
|
||||||
class AttendanceLogViewButton extends StatelessWidget {
|
class AttendanceLogViewButton extends StatelessWidget {
|
||||||
@ -219,7 +218,7 @@ class AttendanceLogViewButton extends StatelessWidget {
|
|||||||
child: ElevatedButton(
|
child: ElevatedButton(
|
||||||
onPressed: () => _showLogsBottomSheet(context),
|
onPressed: () => _showLogsBottomSheet(context),
|
||||||
style: ElevatedButton.styleFrom(
|
style: ElevatedButton.styleFrom(
|
||||||
backgroundColor: AttendanceActionColors.colors[ButtonActions.checkIn],
|
backgroundColor: Colors.indigo,
|
||||||
textStyle: const TextStyle(fontSize: 12),
|
textStyle: const TextStyle(fontSize: 12),
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 12),
|
padding: const EdgeInsets.symmetric(horizontal: 12),
|
||||||
),
|
),
|
||||||
|
@ -48,7 +48,7 @@ class _EmployeesScreenState extends State<EmployeesScreen> with UIMixin {
|
|||||||
|
|
||||||
Future<void> _initEmployees() async {
|
Future<void> _initEmployees() async {
|
||||||
final projectId = Get.find<ProjectController>().selectedProject?.id;
|
final projectId = Get.find<ProjectController>().selectedProject?.id;
|
||||||
final orgId = _organizationController.selectedOrganization?.id;
|
final orgId = _organizationController.selectedOrganization.value?.id;
|
||||||
|
|
||||||
if (projectId != null) {
|
if (projectId != null) {
|
||||||
await _organizationController.fetchOrganizations(projectId);
|
await _organizationController.fetchOrganizations(projectId);
|
||||||
@ -71,7 +71,7 @@ class _EmployeesScreenState extends State<EmployeesScreen> with UIMixin {
|
|||||||
Future<void> _refreshEmployees() async {
|
Future<void> _refreshEmployees() async {
|
||||||
try {
|
try {
|
||||||
final projectId = Get.find<ProjectController>().selectedProject?.id;
|
final projectId = Get.find<ProjectController>().selectedProject?.id;
|
||||||
final orgId = _organizationController.selectedOrganization?.id;
|
final orgId = _organizationController.selectedOrganization.value?.id;
|
||||||
final allSelected = _employeeController.isAllEmployeeSelected.value;
|
final allSelected = _employeeController.isAllEmployeeSelected.value;
|
||||||
|
|
||||||
_employeeController.selectedProjectId = allSelected ? null : projectId;
|
_employeeController.selectedProjectId = allSelected ? null : projectId;
|
||||||
@ -300,17 +300,23 @@ class _EmployeesScreenState extends State<EmployeesScreen> with UIMixin {
|
|||||||
controller: _organizationController,
|
controller: _organizationController,
|
||||||
height: 36,
|
height: 36,
|
||||||
onSelectionChanged: (org) async {
|
onSelectionChanged: (org) async {
|
||||||
|
// Make sure the selectedOrganization is updated immediately
|
||||||
|
_organizationController.selectOrganization(org);
|
||||||
|
|
||||||
final projectId =
|
final projectId =
|
||||||
Get.find<ProjectController>().selectedProject?.id;
|
Get.find<ProjectController>().selectedProject?.id;
|
||||||
|
|
||||||
if (_employeeController.isAllEmployeeSelected.value) {
|
if (_employeeController.isAllEmployeeSelected.value) {
|
||||||
await _employeeController.fetchAllEmployees(
|
await _employeeController.fetchAllEmployees(
|
||||||
organizationId: org?.id);
|
organizationId: _organizationController
|
||||||
|
.selectedOrganization.value?.id);
|
||||||
} else if (projectId != null) {
|
} else if (projectId != null) {
|
||||||
await _employeeController.fetchEmployeesByProject(
|
await _employeeController.fetchEmployeesByProject(
|
||||||
projectId,
|
projectId,
|
||||||
organizationId: org?.id);
|
organizationId: _organizationController
|
||||||
|
.selectedOrganization.value?.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
_employeeController.update(['employee_screen_controller']);
|
_employeeController.update(['employee_screen_controller']);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
Loading…
x
Reference in New Issue
Block a user