Refactor AttendanceScreen to improve project selection and refresh logic

This commit is contained in:
Vaibhav Surve 2025-05-16 16:24:11 +05:30
parent 2421233b9f
commit 501bec819f

View File

@ -40,200 +40,181 @@ class _AttendanceScreenState extends State<AttendanceScreen> with UIMixin {
@override
Widget build(BuildContext context) {
return Layout(
child: MyRefreshableContent(
onRefresh: () async {
if (attendanceController.selectedProjectId != null) {
await attendanceController.fetchEmployeesByProject(
attendanceController.selectedProjectId!);
await attendanceController
.fetchAttendanceLogs(attendanceController.selectedProjectId!);
await attendanceController
.fetchProjectData(attendanceController.selectedProjectId!);
await attendanceController
.fetchProjectData(attendanceController.selectedProjectId!);
attendanceController.update();
} else {
await attendanceController.fetchProjects();
}
},
child: GetBuilder<AttendanceController>(
init: attendanceController,
tag: 'attendance_dashboard_controller',
builder: (controller) {
return LoadingComponent(
isLoading: controller.isLoading.value,
loadingText: 'Loading Attendance...',
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
padding: MySpacing.x(flexSpacing),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
MyText.titleMedium("Attendance",
fontSize: 18, fontWeight: 600),
MyBreadcrumb(
children: [
MyBreadcrumbItem(name: 'Dashboard'),
MyBreadcrumbItem(name: 'Attendance', active: true),
],
),
],
),
child: GetBuilder<AttendanceController>(
init: attendanceController,
tag: 'attendance_dashboard_controller',
builder: (controller) {
return LoadingComponent(
isLoading: controller.isLoading.value,
loadingText: 'Loading Attendance...',
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
padding: MySpacing.x(flexSpacing),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
MyText.titleMedium("Attendance",
fontSize: 18, fontWeight: 600),
MyBreadcrumb(
children: [
MyBreadcrumbItem(name: 'Dashboard'),
MyBreadcrumbItem(name: 'Attendance', active: true),
],
),
],
),
MySpacing.height(flexSpacing),
Padding(
padding: MySpacing.x(flexSpacing / 2),
child: MyFlex(
children: [
// Project Selection Dropdown
MyFlexItem(
sizes: 'lg-12',
child: MyContainer.bordered(
padding: MySpacing.xy(8, 8),
child: PopupMenuButton<String>(
onSelected: (value) async {
attendanceController.selectedProjectId = value;
await attendanceController
.fetchEmployeesByProject(value);
await attendanceController
.fetchAttendanceLogs(value);
await attendanceController
.fetchRegularizationLogs(value);
await attendanceController
.fetchProjectData(value);
attendanceController.update();
},
itemBuilder: (BuildContext context) {
if (attendanceController.projects.isEmpty) {
return [
PopupMenuItem<String>(
value: '',
child: MyText.bodySmall('No Data',
fontWeight: 600),
)
];
}
// Filter projects based on permissions
final accessibleProjects = attendanceController
.projects
.where((project) => permissionController
.isUserAssignedToProject(
project.id.toString()))
.toList();
),
MySpacing.height(flexSpacing),
Padding(
padding: MySpacing.x(flexSpacing / 2),
child: MyFlex(
children: [
// Project Selection Dropdown
MyFlexItem(
sizes: 'lg-12',
child: MyContainer.bordered(
padding: MySpacing.xy(8, 8),
child: PopupMenuButton<String>(
onSelected: (value) async {
attendanceController.selectedProjectId = value;
await attendanceController
.fetchEmployeesByProject(value);
await attendanceController
.fetchAttendanceLogs(value);
await attendanceController
.fetchRegularizationLogs(value);
await attendanceController
.fetchProjectData(value);
attendanceController.update();
},
itemBuilder: (BuildContext context) {
if (attendanceController.projects.isEmpty) {
return [
PopupMenuItem<String>(
value: '',
child: MyText.bodySmall('No Data',
fontWeight: 600),
)
];
}
// Filter projects based on permissions
final accessibleProjects = attendanceController
.projects
.where((project) => permissionController
.isUserAssignedToProject(
project.id.toString()))
.toList();
if (accessibleProjects.isEmpty) {
return [
PopupMenuItem<String>(
value: '',
child: MyText.bodySmall(
'No Projects Assigned',
fontWeight: 600),
)
];
}
return accessibleProjects.map((project) {
return PopupMenuItem<String>(
value: project.id.toString(),
height: 32,
if (accessibleProjects.isEmpty) {
return [
PopupMenuItem<String>(
value: '',
child: MyText.bodySmall(
project.name,
color: theme.colorScheme.onSurface,
fontWeight: 600,
),
);
}).toList();
},
color: theme.cardTheme.color,
child: Row(
mainAxisAlignment:
MainAxisAlignment.spaceBetween,
children: [
MyText.labelSmall(
attendanceController.selectedProjectId !=
null
? attendanceController.projects
.firstWhereOrNull((proj) =>
proj.id ==
attendanceController
.selectedProjectId)
?.name ??
'Select a Project'
: 'Select a Project',
'No Projects Assigned',
fontWeight: 600),
)
];
}
return accessibleProjects.map((project) {
return PopupMenuItem<String>(
value: project.id.toString(),
height: 32,
child: MyText.bodySmall(
project.name,
color: theme.colorScheme.onSurface,
fontWeight: 600,
),
Icon(LucideIcons.chevron_down,
size: 20,
color: theme.colorScheme.onSurface),
],
),
);
}).toList();
},
color: theme.cardTheme.color,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
MyText.labelSmall(
attendanceController.selectedProjectId != null
? attendanceController.projects
.firstWhereOrNull((proj) =>
proj.id ==
attendanceController
.selectedProjectId)
?.name ??
'Select a Project'
: 'Select a Project',
color: theme.colorScheme.onSurface,
),
Icon(LucideIcons.chevron_down,
size: 20,
color: theme.colorScheme.onSurface),
],
),
),
),
),
// Tab Section
MyFlexItem(
sizes: 'lg-12',
child: Obx(() {
bool hasRegularizationPermission =
permissionController.hasPermission(
Permissions.regularizeAttendance);
// Tab Section
MyFlexItem(
sizes: 'lg-12',
child: Obx(() {
bool hasRegularizationPermission =
permissionController.hasPermission(
Permissions.regularizeAttendance);
final tabs = <Tab>[
const Tab(text: 'Employee List'),
const Tab(text: 'Logs'),
if (hasRegularizationPermission)
const Tab(text: 'Regularization'),
];
final tabs = <Tab>[
const Tab(text: 'Employee List'),
const Tab(text: 'Logs'),
if (hasRegularizationPermission)
const Tab(text: 'Regularization'),
];
final views = <Widget>[
employeeListTab(),
reportsTab(context),
if (hasRegularizationPermission)
regularizationTab(context),
];
final views = <Widget>[
employeeListTab(),
reportsTab(context),
if (hasRegularizationPermission)
regularizationTab(context),
];
return DefaultTabController(
length: tabs.length,
child: MyCard.bordered(
borderRadiusAll: 4,
border: Border.all(
color: Colors.grey.withAlpha(50)),
shadow: MyShadow(
elevation: 1,
position: MyShadowPosition.bottom),
paddingAll: 10,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
TabBar(
labelColor: theme.colorScheme.primary,
unselectedLabelColor: theme
.colorScheme.onSurface
.withAlpha(150),
tabs: tabs,
),
MySpacing.height(16),
SizedBox(
height: 550,
child: TabBarView(children: views),
),
],
),
return DefaultTabController(
length: tabs.length,
child: MyCard.bordered(
borderRadiusAll: 4,
border:
Border.all(color: Colors.grey.withAlpha(50)),
shadow: MyShadow(
elevation: 1,
position: MyShadowPosition.bottom),
paddingAll: 10,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
TabBar(
labelColor: theme.colorScheme.primary,
unselectedLabelColor: theme
.colorScheme.onSurface
.withAlpha(150),
tabs: tabs,
),
MySpacing.height(16),
SizedBox(
height: 550,
child: TabBarView(children: views),
),
],
),
);
}),
),
],
),
),
);
}),
),
],
),
],
),
);
},
),
),
],
),
);
},
),
);
}
@ -365,13 +346,25 @@ class _AttendanceScreenState extends State<AttendanceScreen> with UIMixin {
),
]);
}).toList();
return Padding(
padding: const EdgeInsets.all(0.0),
child: SingleChildScrollView(
child: MyPaginatedTable(
columns: columns,
rows: rows,
child: MyRefreshableContent(
onRefresh: () async {
if (attendanceController.selectedProjectId != null) {
await attendanceController.fetchEmployeesByProject(
attendanceController.selectedProjectId!);
await attendanceController
.fetchProjectData(attendanceController.selectedProjectId!);
attendanceController.update();
} else {
await attendanceController.fetchProjects();
}
},
child: MyPaginatedTable(
columns: columns,
rows: rows,
),
),
),
);
@ -832,11 +825,28 @@ class _AttendanceScreenState extends State<AttendanceScreen> with UIMixin {
children: [
Padding(
padding: const EdgeInsets.all(8.0),
child: TextButton.icon(
icon: const Icon(Icons.date_range),
label: const Text("Select Date Range for Attendance"),
onPressed: () => attendanceController
.selectDateRangeForAttendance(context, attendanceController),
child: GetBuilder<AttendanceController>(
id: 'attendance_dashboard_controller',
builder: (controller) {
String labelText;
if (controller.startDateAttendance != null &&
controller.endDateAttendance != null) {
final start = DateFormat('dd MM yyyy')
.format(controller.startDateAttendance!);
final end = DateFormat('dd MM yyyy')
.format(controller.endDateAttendance!);
labelText = "$start - $end";
} else {
labelText = "Select Date Range for Attendance";
}
return TextButton.icon(
icon: const Icon(Icons.date_range),
label: Text(labelText, overflow: TextOverflow.ellipsis),
onPressed: () => controller.selectDateRangeForAttendance(
Get.context!, controller),
);
},
),
),
if (attendanceController.attendanceLogs.isEmpty)
@ -851,12 +861,25 @@ class _AttendanceScreenState extends State<AttendanceScreen> with UIMixin {
),
)
else
SingleChildScrollView(
child: MyPaginatedTable(
columns: columns,
rows: rows,
MyRefreshableContent(
onRefresh: () async {
if (attendanceController.selectedProjectId != null) {
await attendanceController.fetchAttendanceLogs(
attendanceController.selectedProjectId!);
await attendanceController.fetchProjectData(
attendanceController.selectedProjectId!);
attendanceController.update();
} else {
await attendanceController.fetchProjects();
}
},
child: SingleChildScrollView(
child: MyPaginatedTable(
columns: columns,
rows: rows,
),
),
),
)
],
),
);
@ -1120,14 +1143,27 @@ class _AttendanceScreenState extends State<AttendanceScreen> with UIMixin {
)
else
Expanded(
child: SingleChildScrollView(
child: MyPaginatedTable(
columns: columns,
rows: rows,
columnSpacing: 15.0,
child: MyRefreshableContent(
onRefresh: () async {
if (attendanceController.selectedProjectId != null) {
await attendanceController.fetchProjectData(
attendanceController.selectedProjectId!);
await attendanceController.fetchRegularizationLogs(
attendanceController.selectedProjectId!);
attendanceController.update();
} else {
await attendanceController.fetchProjects();
}
},
child: SingleChildScrollView(
child: MyPaginatedTable(
columns: columns,
rows: rows,
columnSpacing: 15.0,
),
),
),
),
)
],
);
}