feat: Refactor project selection handling and update UI across various screens
This commit is contained in:
parent
b81ac33b2d
commit
56efbe8869
@ -10,12 +10,17 @@ class ProjectController extends GetxController {
|
||||
RxList<ProjectModel> projects = <ProjectModel>[].obs;
|
||||
RxString? selectedProjectId;
|
||||
RxBool isProjectListExpanded = false.obs;
|
||||
RxBool isProjectSelectionExpanded = false.obs;
|
||||
RxBool isProjectSelectionExpanded = false.obs;
|
||||
|
||||
RxBool isProjectDropdownExpanded = false.obs;
|
||||
RxBool isLoading = true.obs;
|
||||
RxBool isLoadingProjects = true.obs;
|
||||
RxMap<String, RxBool> uploadingStates = <String, RxBool>{}.obs;
|
||||
ProjectModel? get selectedProject {
|
||||
if (selectedProjectId == null || selectedProjectId!.value.isEmpty)
|
||||
return null;
|
||||
return projects.firstWhereOrNull((p) => p.id == selectedProjectId!.value);
|
||||
}
|
||||
|
||||
@override
|
||||
void onInit() {
|
||||
@ -57,6 +62,6 @@ class ProjectController extends GetxController {
|
||||
Future<void> updateSelectedProject(String projectId) async {
|
||||
selectedProjectId?.value = projectId;
|
||||
await LocalStorage.saveString('selectedProjectId', projectId);
|
||||
update();
|
||||
update();
|
||||
}
|
||||
}
|
||||
|
@ -3,15 +3,12 @@ import 'package:get/get.dart';
|
||||
import 'package:marco/helpers/theme/app_theme.dart';
|
||||
import 'package:marco/helpers/utils/mixins/ui_mixin.dart';
|
||||
import 'package:marco/helpers/utils/my_shadow.dart';
|
||||
import 'package:marco/helpers/widgets/my_breadcrumb.dart';
|
||||
import 'package:marco/helpers/widgets/my_breadcrumb_item.dart';
|
||||
import 'package:marco/helpers/widgets/my_card.dart';
|
||||
import 'package:marco/helpers/widgets/my_container.dart';
|
||||
import 'package:marco/helpers/widgets/my_flex.dart';
|
||||
import 'package:marco/helpers/widgets/my_flex_item.dart';
|
||||
import 'package:marco/helpers/widgets/my_spacing.dart';
|
||||
import 'package:marco/helpers/widgets/my_text.dart';
|
||||
import 'package:marco/view/layouts/layout.dart';
|
||||
import 'package:marco/controller/dashboard/attendance_screen_controller.dart';
|
||||
import 'package:marco/controller/permission_controller.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
@ -20,7 +17,7 @@ import 'package:marco/model/attendance/log_details_view.dart';
|
||||
import 'package:marco/model/attendance/attendence_action_button.dart';
|
||||
import 'package:marco/model/attendance/regualrize_action_button.dart';
|
||||
import 'package:marco/model/attendance/attendence_filter_sheet.dart';
|
||||
import 'package:marco/controller/project_controller.dart'; // adjust if needed
|
||||
import 'package:marco/controller/project_controller.dart';
|
||||
|
||||
class AttendanceScreen extends StatefulWidget {
|
||||
AttendanceScreen({super.key});
|
||||
@ -57,169 +54,198 @@ class _AttendanceScreenState extends State<AttendanceScreen> with UIMixin {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Layout(
|
||||
child: GetBuilder<AttendanceController>(
|
||||
init: attendanceController,
|
||||
tag: 'attendance_dashboard_controller',
|
||||
builder: (controller) {
|
||||
return 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),
|
||||
],
|
||||
),
|
||||
],
|
||||
return Scaffold(
|
||||
appBar: PreferredSize(
|
||||
preferredSize: const Size.fromHeight(80),
|
||||
child: AppBar(
|
||||
backgroundColor: const Color(0xFFF5F5F5),
|
||||
elevation: 0.5,
|
||||
foregroundColor: Colors.black,
|
||||
titleSpacing: 0,
|
||||
leading: IconButton(
|
||||
icon: const Icon(Icons.arrow_back_ios_new,
|
||||
color: Colors.black, size: 20),
|
||||
onPressed: () {
|
||||
Get.offNamed('/dashboard');
|
||||
},
|
||||
),
|
||||
title: Padding(
|
||||
padding: const EdgeInsets.only(top: 12.0),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
MyText.titleLarge(
|
||||
'Attendance',
|
||||
fontWeight: 700,
|
||||
color: Colors.black,
|
||||
),
|
||||
),
|
||||
MySpacing.height(flexSpacing),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.end,
|
||||
const SizedBox(height: 4),
|
||||
GetBuilder<ProjectController>(
|
||||
builder: (projectController) {
|
||||
final projectName =
|
||||
projectController.selectedProject?.name ??
|
||||
'Select Project';
|
||||
return MyText.bodySmall(
|
||||
projectName,
|
||||
fontWeight: 600,
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
color: Colors.grey[700],
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
body: SafeArea(
|
||||
child: SingleChildScrollView(
|
||||
padding: MySpacing.x(0),
|
||||
child: GetBuilder<AttendanceController>(
|
||||
init: attendanceController,
|
||||
tag: 'attendance_dashboard_controller',
|
||||
builder: (controller) {
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
MyText.bodyMedium(
|
||||
"Filter",
|
||||
fontWeight: 600,
|
||||
),
|
||||
Tooltip(
|
||||
message: 'Filter Project',
|
||||
child: InkWell(
|
||||
borderRadius: BorderRadius.circular(24),
|
||||
onTap: () async {
|
||||
final result =
|
||||
await showModalBottomSheet<Map<String, dynamic>>(
|
||||
context: context,
|
||||
isScrollControlled: true,
|
||||
backgroundColor: Colors.white,
|
||||
shape: const RoundedRectangleBorder(
|
||||
borderRadius:
|
||||
BorderRadius.vertical(top: Radius.circular(12)),
|
||||
),
|
||||
builder: (context) => AttendanceFilterBottomSheet(
|
||||
controller: attendanceController,
|
||||
permissionController: permissionController,
|
||||
selectedTab: selectedTab,
|
||||
),
|
||||
);
|
||||
MySpacing.height(flexSpacing),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.end,
|
||||
children: [
|
||||
MyText.bodyMedium("Filter", fontWeight: 600),
|
||||
Tooltip(
|
||||
message: 'Filter Project',
|
||||
child: InkWell(
|
||||
borderRadius: BorderRadius.circular(24),
|
||||
onTap: () async {
|
||||
final result = await showModalBottomSheet<
|
||||
Map<String, dynamic>>(
|
||||
context: context,
|
||||
isScrollControlled: true,
|
||||
backgroundColor: Colors.white,
|
||||
shape: const RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.vertical(
|
||||
top: Radius.circular(12)),
|
||||
),
|
||||
builder: (context) => AttendanceFilterBottomSheet(
|
||||
controller: attendanceController,
|
||||
permissionController: permissionController,
|
||||
selectedTab: selectedTab,
|
||||
),
|
||||
);
|
||||
|
||||
if (result != null) {
|
||||
final selectedProjectId =
|
||||
Get.find<ProjectController>()
|
||||
.selectedProjectId
|
||||
?.value;
|
||||
if (result != null) {
|
||||
final selectedProjectId =
|
||||
Get.find<ProjectController>()
|
||||
.selectedProjectId
|
||||
?.value;
|
||||
|
||||
final selectedView = result['selectedTab'] as String?;
|
||||
final selectedView =
|
||||
result['selectedTab'] as String?;
|
||||
|
||||
if (selectedProjectId != null &&
|
||||
selectedProjectId !=
|
||||
attendanceController.selectedProjectId) {
|
||||
attendanceController.selectedProjectId =
|
||||
selectedProjectId;
|
||||
try {
|
||||
await attendanceController
|
||||
.fetchEmployeesByProject(selectedProjectId);
|
||||
await attendanceController
|
||||
.fetchAttendanceLogs(selectedProjectId);
|
||||
await attendanceController
|
||||
.fetchRegularizationLogs(selectedProjectId);
|
||||
await attendanceController
|
||||
.fetchProjectData(selectedProjectId);
|
||||
} catch (_) {}
|
||||
attendanceController
|
||||
.update(['attendance_dashboard_controller']);
|
||||
}
|
||||
if (selectedProjectId != null &&
|
||||
selectedProjectId !=
|
||||
attendanceController.selectedProjectId) {
|
||||
attendanceController.selectedProjectId =
|
||||
selectedProjectId;
|
||||
try {
|
||||
await attendanceController
|
||||
.fetchEmployeesByProject(
|
||||
selectedProjectId);
|
||||
await attendanceController
|
||||
.fetchAttendanceLogs(selectedProjectId);
|
||||
await attendanceController
|
||||
.fetchRegularizationLogs(
|
||||
selectedProjectId);
|
||||
await attendanceController
|
||||
.fetchProjectData(selectedProjectId);
|
||||
} catch (_) {}
|
||||
attendanceController.update(
|
||||
['attendance_dashboard_controller']);
|
||||
}
|
||||
|
||||
if (selectedView != null &&
|
||||
selectedView != selectedTab) {
|
||||
setState(() {
|
||||
selectedTab = selectedView;
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
child: MouseRegion(
|
||||
cursor: SystemMouseCursors.click,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: Icon(
|
||||
Icons.filter_list_alt,
|
||||
color: Colors.blueAccent,
|
||||
size: 28,
|
||||
if (selectedView != null &&
|
||||
selectedView != selectedTab) {
|
||||
setState(() {
|
||||
selectedTab = selectedView;
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
child: MouseRegion(
|
||||
cursor: SystemMouseCursors.click,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: Icon(
|
||||
Icons.filter_list_alt,
|
||||
color: Colors.blueAccent,
|
||||
size: 28,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 4),
|
||||
MyText.bodyMedium(
|
||||
"Refresh",
|
||||
fontWeight: 600,
|
||||
),
|
||||
Tooltip(
|
||||
message: 'Refresh Data',
|
||||
child: InkWell(
|
||||
borderRadius: BorderRadius.circular(24),
|
||||
onTap: () async {
|
||||
final projectId = Get.find<ProjectController>()
|
||||
.selectedProjectId
|
||||
?.value;
|
||||
const SizedBox(width: 4),
|
||||
MyText.bodyMedium("Refresh", fontWeight: 600),
|
||||
Tooltip(
|
||||
message: 'Refresh Data',
|
||||
child: InkWell(
|
||||
borderRadius: BorderRadius.circular(24),
|
||||
onTap: () async {
|
||||
final projectId = Get.find<ProjectController>()
|
||||
.selectedProjectId
|
||||
?.value;
|
||||
|
||||
if (projectId != null && projectId.isNotEmpty) {
|
||||
try {
|
||||
await attendanceController
|
||||
.fetchEmployeesByProject(projectId);
|
||||
await attendanceController
|
||||
.fetchAttendanceLogs(projectId);
|
||||
await attendanceController
|
||||
.fetchRegularizationLogs(projectId);
|
||||
await attendanceController
|
||||
.fetchProjectData(projectId);
|
||||
attendanceController
|
||||
.update(['attendance_dashboard_controller']);
|
||||
} catch (e) {
|
||||
debugPrint("Error refreshing data: $e");
|
||||
}
|
||||
}
|
||||
},
|
||||
child: MouseRegion(
|
||||
cursor: SystemMouseCursors.click,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: Icon(
|
||||
Icons.refresh,
|
||||
color: Colors.green,
|
||||
size: 28,
|
||||
if (projectId != null && projectId.isNotEmpty) {
|
||||
try {
|
||||
await attendanceController
|
||||
.fetchEmployeesByProject(projectId);
|
||||
await attendanceController
|
||||
.fetchAttendanceLogs(projectId);
|
||||
await attendanceController
|
||||
.fetchRegularizationLogs(projectId);
|
||||
await attendanceController
|
||||
.fetchProjectData(projectId);
|
||||
attendanceController.update(
|
||||
['attendance_dashboard_controller']);
|
||||
} catch (e) {
|
||||
debugPrint("Error refreshing data: $e");
|
||||
}
|
||||
}
|
||||
},
|
||||
child: MouseRegion(
|
||||
cursor: SystemMouseCursors.click,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: Icon(
|
||||
Icons.refresh,
|
||||
color: Colors.green,
|
||||
size: 28,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
MySpacing.height(flexSpacing),
|
||||
MyFlex(children: [
|
||||
MyFlexItem(
|
||||
sizes: 'lg-12 md-12 sm-12',
|
||||
child: selectedTab == 'todaysAttendance'
|
||||
? employeeListTab()
|
||||
: selectedTab == 'attendanceLogs'
|
||||
? employeeLog()
|
||||
: regularizationScreen(),
|
||||
),
|
||||
]),
|
||||
],
|
||||
),
|
||||
Padding(
|
||||
padding: MySpacing.x(0),
|
||||
child: MyFlex(children: [
|
||||
MyFlexItem(
|
||||
sizes: 'lg-12 md-12 sm-12',
|
||||
child: selectedTab == 'todaysAttendance'
|
||||
? employeeListTab()
|
||||
: selectedTab == 'attendanceLogs'
|
||||
? employeeLog()
|
||||
: regularizationScreen(),
|
||||
),
|
||||
]),
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
@ -8,14 +8,13 @@ import 'package:marco/helpers/widgets/my_breadcrumb_item.dart';
|
||||
import 'package:marco/helpers/widgets/my_card.dart';
|
||||
import 'package:marco/helpers/widgets/my_spacing.dart';
|
||||
import 'package:marco/helpers/widgets/my_text.dart';
|
||||
import 'package:marco/view/layouts/layout.dart';
|
||||
import 'package:marco/controller/permission_controller.dart';
|
||||
import 'package:marco/model/employees/employees_screen_filter_sheet.dart';
|
||||
import 'package:marco/model/employees/add_employee_bottom_sheet.dart';
|
||||
import 'package:marco/controller/dashboard/employees_screen_controller.dart';
|
||||
import 'package:marco/helpers/widgets/avatar.dart';
|
||||
import 'package:marco/model/employees/employee_detail_bottom_sheet.dart';
|
||||
|
||||
import 'package:marco/controller/project_controller.dart';
|
||||
class EmployeesScreen extends StatefulWidget {
|
||||
const EmployeesScreen({super.key});
|
||||
|
||||
@ -79,7 +78,52 @@ class _EmployeesScreenState extends State<EmployeesScreen> with UIMixin {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Layout(
|
||||
return Scaffold(
|
||||
backgroundColor: const Color(0xFFF5F5F5),
|
||||
appBar: PreferredSize(
|
||||
preferredSize: const Size.fromHeight(80),
|
||||
child: AppBar(
|
||||
backgroundColor: const Color(0xFFF5F5F5),
|
||||
elevation: 0.5,
|
||||
foregroundColor: Colors.black,
|
||||
titleSpacing: 0,
|
||||
leading: IconButton(
|
||||
icon: const Icon(Icons.arrow_back_ios_new),
|
||||
onPressed: () {
|
||||
Get.offNamed('/dashboard');
|
||||
},
|
||||
),
|
||||
title: Padding(
|
||||
padding: const EdgeInsets.only(top: 12.0),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
MyText.titleLarge(
|
||||
'Employees',
|
||||
fontWeight: 700,
|
||||
color: Colors.black,
|
||||
),
|
||||
const SizedBox(height: 4),
|
||||
GetBuilder<ProjectController>(
|
||||
builder: (projectController) {
|
||||
final projectName =
|
||||
projectController.selectedProject?.name ??
|
||||
'Select Project';
|
||||
return MyText.bodySmall(
|
||||
projectName,
|
||||
fontWeight: 600,
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
color: Colors.grey[700],
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
floatingActionButton: InkWell(
|
||||
onTap: () async {
|
||||
final result = await showModalBottomSheet<bool>(
|
||||
@ -120,13 +164,14 @@ class _EmployeesScreenState extends State<EmployeesScreen> with UIMixin {
|
||||
),
|
||||
),
|
||||
),
|
||||
child: GetBuilder<EmployeesScreenController>(
|
||||
init: employeeScreenController,
|
||||
tag: 'employee_screen_controller',
|
||||
builder: (controller) {
|
||||
return Stack(
|
||||
children: [
|
||||
Column(
|
||||
body: SafeArea(
|
||||
child: GetBuilder<EmployeesScreenController>(
|
||||
init: employeeScreenController,
|
||||
tag: 'employee_screen_controller',
|
||||
builder: (controller) {
|
||||
return SingleChildScrollView(
|
||||
padding: const EdgeInsets.only(bottom: 80),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Padding(
|
||||
@ -134,11 +179,6 @@ class _EmployeesScreenState extends State<EmployeesScreen> with UIMixin {
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
MyText.titleMedium(
|
||||
"Employees",
|
||||
fontSize: 18,
|
||||
fontWeight: 600,
|
||||
),
|
||||
MyBreadcrumb(
|
||||
children: [
|
||||
MyBreadcrumbItem(name: 'Dashboard'),
|
||||
@ -154,10 +194,7 @@ class _EmployeesScreenState extends State<EmployeesScreen> with UIMixin {
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.end,
|
||||
children: [
|
||||
MyText.bodyMedium(
|
||||
"Filter",
|
||||
fontWeight: 600,
|
||||
),
|
||||
MyText.bodyMedium("Filter", fontWeight: 600),
|
||||
Tooltip(
|
||||
message: 'Project',
|
||||
child: InkWell(
|
||||
@ -177,10 +214,7 @@ class _EmployeesScreenState extends State<EmployeesScreen> with UIMixin {
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
MyText.bodyMedium(
|
||||
"Refresh",
|
||||
fontWeight: 600,
|
||||
),
|
||||
MyText.bodyMedium("Refresh", fontWeight: 600),
|
||||
Tooltip(
|
||||
message: 'Refresh Data',
|
||||
child: InkWell(
|
||||
@ -208,9 +242,9 @@ class _EmployeesScreenState extends State<EmployeesScreen> with UIMixin {
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
@ -9,6 +9,7 @@ import 'package:marco/helpers/services/api_endpoints.dart';
|
||||
import 'package:marco/images.dart';
|
||||
import 'package:marco/controller/project_controller.dart';
|
||||
import 'package:marco/view/layouts/user_profile_right_bar.dart';
|
||||
|
||||
class Layout extends StatefulWidget {
|
||||
final Widget? child;
|
||||
final Widget? floatingActionButton;
|
||||
@ -42,7 +43,7 @@ class _LayoutState extends State<Layout> {
|
||||
Widget _buildScaffold(BuildContext context, {bool isMobile = false}) {
|
||||
return Scaffold(
|
||||
key: controller.scaffoldKey,
|
||||
endDrawer: UserProfileBar(),
|
||||
endDrawer: UserProfileBar(),
|
||||
floatingActionButton: widget.floatingActionButton,
|
||||
body: SafeArea(
|
||||
child: Column(
|
||||
@ -51,8 +52,8 @@ class _LayoutState extends State<Layout> {
|
||||
Expanded(
|
||||
child: SingleChildScrollView(
|
||||
key: controller.scrollKey,
|
||||
padding: EdgeInsets.symmetric(horizontal: 0, vertical: isMobile ? 16 : 32),
|
||||
|
||||
padding: EdgeInsets.symmetric(
|
||||
horizontal: 0, vertical: isMobile ? 16 : 32),
|
||||
child: widget.child,
|
||||
),
|
||||
),
|
||||
@ -64,7 +65,7 @@ class _LayoutState extends State<Layout> {
|
||||
|
||||
Widget _buildHeader(BuildContext context, bool isMobile) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 0),
|
||||
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 6),
|
||||
child: Obx(() {
|
||||
final isExpanded = projectController.isProjectSelectionExpanded.value;
|
||||
final selectedProjectId = projectController.selectedProjectId?.value;
|
||||
@ -77,102 +78,96 @@ class _LayoutState extends State<Layout> {
|
||||
.updateSelectedProject(projectController.projects.first.id);
|
||||
}
|
||||
|
||||
return AnimatedContainer(
|
||||
duration: const Duration(milliseconds: 300),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
borderRadius: BorderRadius.vertical(
|
||||
bottom: Radius.circular(isExpanded ? 16 : 0),
|
||||
),
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: const Color.fromARGB(255, 67, 73, 84),
|
||||
blurRadius: 4,
|
||||
offset: Offset(0, 2),
|
||||
),
|
||||
],
|
||||
return Card(
|
||||
elevation: 4,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(isExpanded ? 16 : 12),
|
||||
),
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 16),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
ClipRRect(
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
child: Image.asset(
|
||||
Images.logoDark,
|
||||
height: 50,
|
||||
width: 50,
|
||||
fit: BoxFit.contain,
|
||||
margin: EdgeInsets.zero,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(10),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
ClipRRect(
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
child: Image.asset(
|
||||
Images.logoDark,
|
||||
height: 50,
|
||||
width: 50,
|
||||
fit: BoxFit.contain,
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
Expanded(
|
||||
child: GestureDetector(
|
||||
onTap: () =>
|
||||
projectController.isProjectSelectionExpanded.toggle(),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: MyText.bodyLarge(
|
||||
selectedProject?.name ??
|
||||
"Select Project",
|
||||
fontWeight: 700,
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
const SizedBox(width: 12),
|
||||
Expanded(
|
||||
child: GestureDetector(
|
||||
onTap: () => projectController
|
||||
.isProjectSelectionExpanded
|
||||
.toggle(),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: MyText.bodyLarge(
|
||||
selectedProject?.name ??
|
||||
"Select Project",
|
||||
fontWeight: 700,
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
),
|
||||
),
|
||||
Icon(
|
||||
isExpanded
|
||||
? Icons.arrow_drop_up_outlined
|
||||
: Icons.arrow_drop_down_outlined,
|
||||
color: Colors.black,
|
||||
),
|
||||
],
|
||||
Icon(
|
||||
isExpanded
|
||||
? Icons.arrow_drop_up_outlined
|
||||
: Icons.arrow_drop_down_outlined,
|
||||
color: Colors.black,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
MyText.bodyMedium(
|
||||
"Hi, ${employeeInfo?.firstName ?? ''}",
|
||||
color: Colors.black54,
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
MyText.bodyMedium(
|
||||
"Hi, ${employeeInfo?.firstName ?? ''}",
|
||||
color: Colors.black54,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
if (isBetaEnvironment)
|
||||
Container(
|
||||
margin: const EdgeInsets.only(left: 8),
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 8, vertical: 2),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.deepPurple,
|
||||
borderRadius: BorderRadius.circular(6),
|
||||
),
|
||||
child: MyText.bodySmall(
|
||||
'BETA',
|
||||
color: Colors.white,
|
||||
fontWeight: 700,
|
||||
if (isBetaEnvironment)
|
||||
Container(
|
||||
margin: const EdgeInsets.only(left: 8),
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 8, vertical: 2),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.deepPurple,
|
||||
borderRadius: BorderRadius.circular(6),
|
||||
),
|
||||
child: MyText.bodySmall(
|
||||
'BETA',
|
||||
color: Colors.white,
|
||||
fontWeight: 700,
|
||||
),
|
||||
),
|
||||
IconButton(
|
||||
icon: Icon(Icons.menu),
|
||||
onPressed: () =>
|
||||
controller.scaffoldKey.currentState?.openEndDrawer(),
|
||||
),
|
||||
IconButton(
|
||||
icon: Icon(Icons.menu),
|
||||
onPressed: () =>
|
||||
controller.scaffoldKey.currentState?.openEndDrawer(),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
if (isExpanded) _buildProjectList(context, isMobile),
|
||||
],
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
if (isExpanded) _buildProjectList(context, isMobile),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}),
|
||||
|
@ -4,13 +4,10 @@ import 'package:intl/intl.dart';
|
||||
import 'package:marco/helpers/theme/app_theme.dart';
|
||||
import 'package:marco/helpers/utils/mixins/ui_mixin.dart';
|
||||
import 'package:marco/helpers/utils/my_shadow.dart';
|
||||
import 'package:marco/helpers/widgets/my_breadcrumb.dart';
|
||||
import 'package:marco/helpers/widgets/my_breadcrumb_item.dart';
|
||||
import 'package:marco/helpers/widgets/my_card.dart';
|
||||
import 'package:marco/helpers/widgets/my_container.dart';
|
||||
import 'package:marco/helpers/widgets/my_spacing.dart';
|
||||
import 'package:marco/helpers/widgets/my_text.dart';
|
||||
import 'package:marco/view/layouts/layout.dart';
|
||||
import 'package:marco/controller/permission_controller.dart';
|
||||
import 'package:marco/controller/dashboard/daily_task_controller.dart';
|
||||
import 'package:marco/model/dailyTaskPlaning/daily_progress_report_filter.dart';
|
||||
@ -68,47 +65,74 @@ class _DailyProgressReportScreenState extends State<DailyProgressReportScreen>
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Layout(
|
||||
child: GetBuilder<DailyTaskController>(
|
||||
init: dailyTaskController,
|
||||
tag: 'daily_progress_report_controller',
|
||||
builder: (controller) {
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
_buildHeader(),
|
||||
MySpacing.height(flexSpacing),
|
||||
_buildActionBar(),
|
||||
Padding(
|
||||
padding: MySpacing.x(flexSpacing),
|
||||
child: _buildDailyProgressReportTab(),
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildHeader() {
|
||||
return Padding(
|
||||
padding: MySpacing.x(flexSpacing),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
MyText.titleMedium("Daily Progress Report",
|
||||
fontSize: 18, fontWeight: 600),
|
||||
MyBreadcrumb(
|
||||
children: [
|
||||
MyBreadcrumbItem(name: 'Dashboard'),
|
||||
MyBreadcrumbItem(name: 'Daily Progress Report', active: true),
|
||||
],
|
||||
return Scaffold(
|
||||
appBar: PreferredSize(
|
||||
preferredSize: const Size.fromHeight(80),
|
||||
child: AppBar(
|
||||
backgroundColor: const Color(0xFFF5F5F5),
|
||||
elevation: 0.5,
|
||||
foregroundColor: Colors.black,
|
||||
titleSpacing: 0,
|
||||
leading: IconButton(
|
||||
icon: const Icon(Icons.arrow_back_ios_new),
|
||||
onPressed: () {
|
||||
Get.offNamed('/dashboard');
|
||||
},
|
||||
),
|
||||
],
|
||||
title: Padding(
|
||||
padding: const EdgeInsets.only(top: 12.0),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
MyText.titleLarge(
|
||||
'Daily Task Progress Report',
|
||||
fontWeight: 700,
|
||||
color: Colors.black,
|
||||
),
|
||||
const SizedBox(height: 4),
|
||||
GetBuilder<ProjectController>(
|
||||
builder: (projectController) {
|
||||
final projectName =
|
||||
projectController.selectedProject?.name ??
|
||||
'Select Project';
|
||||
return MyText.bodySmall(
|
||||
projectName,
|
||||
fontWeight: 600,
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
color: Colors.grey[700],
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
body: SafeArea(
|
||||
child: SingleChildScrollView(
|
||||
padding: MySpacing.x(0),
|
||||
child: GetBuilder<DailyTaskController>(
|
||||
init: dailyTaskController,
|
||||
tag: 'daily_progress_report_controller',
|
||||
builder: (controller) {
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
MySpacing.height(flexSpacing),
|
||||
_buildActionBar(),
|
||||
Padding(
|
||||
padding: MySpacing.x(flexSpacing),
|
||||
child: _buildDailyProgressReportTab(),
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildActionBar() {
|
||||
return Padding(
|
||||
padding: MySpacing.x(flexSpacing),
|
||||
|
@ -3,12 +3,9 @@ import 'package:get/get.dart';
|
||||
import 'package:marco/helpers/theme/app_theme.dart';
|
||||
import 'package:marco/helpers/utils/mixins/ui_mixin.dart';
|
||||
import 'package:marco/helpers/utils/my_shadow.dart';
|
||||
import 'package:marco/helpers/widgets/my_breadcrumb.dart';
|
||||
import 'package:marco/helpers/widgets/my_breadcrumb_item.dart';
|
||||
import 'package:marco/helpers/widgets/my_card.dart';
|
||||
import 'package:marco/helpers/widgets/my_spacing.dart';
|
||||
import 'package:marco/helpers/widgets/my_text.dart';
|
||||
import 'package:marco/view/layouts/layout.dart';
|
||||
import 'package:marco/controller/permission_controller.dart';
|
||||
import 'package:marco/controller/task_planing/daily_task_planing_controller.dart';
|
||||
import 'package:marco/controller/project_controller.dart';
|
||||
@ -50,82 +47,108 @@ class _DailyTaskPlaningScreenState extends State<DailyTaskPlaningScreen>
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Layout(
|
||||
child: GetBuilder<DailyTaskPlaningController>(
|
||||
init: dailyTaskPlaningController,
|
||||
tag: 'daily_task_planing_controller',
|
||||
builder: (controller) {
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Padding(
|
||||
padding: MySpacing.x(flexSpacing),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
MyText.titleMedium("Daily Task Planning",
|
||||
fontSize: 18, fontWeight: 600),
|
||||
MyBreadcrumb(
|
||||
children: [
|
||||
MyBreadcrumbItem(name: 'Dashboard'),
|
||||
MyBreadcrumbItem(
|
||||
name: 'Daily Task Planning', active: true),
|
||||
],
|
||||
),
|
||||
],
|
||||
return Scaffold(
|
||||
appBar: PreferredSize(
|
||||
preferredSize: const Size.fromHeight(80),
|
||||
child: AppBar(
|
||||
backgroundColor: const Color(0xFFF5F5F5),
|
||||
elevation: 0.5,
|
||||
foregroundColor: Colors.black,
|
||||
titleSpacing: 0,
|
||||
leading: IconButton(
|
||||
icon: const Icon(Icons.arrow_back_ios_new),
|
||||
onPressed: () {
|
||||
Get.offNamed('/dashboard');
|
||||
},
|
||||
),
|
||||
title: Padding(
|
||||
padding: const EdgeInsets.only(top: 12.0),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
MyText.titleLarge(
|
||||
'Daily Task Planning',
|
||||
fontWeight: 700,
|
||||
color: Colors.black,
|
||||
),
|
||||
),
|
||||
MySpacing.height(flexSpacing),
|
||||
Padding(
|
||||
padding: MySpacing.x(flexSpacing),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.end,
|
||||
children: [
|
||||
const SizedBox(width: 8),
|
||||
MyText.bodyMedium(
|
||||
"Refresh",
|
||||
const SizedBox(height: 4),
|
||||
GetBuilder<ProjectController>(
|
||||
builder: (projectController) {
|
||||
final projectName =
|
||||
projectController.selectedProject?.name ??
|
||||
'Select Project';
|
||||
return MyText.bodySmall(
|
||||
projectName,
|
||||
fontWeight: 600,
|
||||
),
|
||||
Tooltip(
|
||||
message: 'Refresh Data',
|
||||
child: InkWell(
|
||||
borderRadius: BorderRadius.circular(24),
|
||||
onTap: () async {
|
||||
final projectId =
|
||||
projectController.selectedProjectId?.value;
|
||||
if (projectId != null) {
|
||||
try {
|
||||
await dailyTaskPlaningController
|
||||
.fetchTaskData(projectId);
|
||||
} catch (e) {
|
||||
debugPrint(
|
||||
'Error refreshing task data: ${e.toString()}');
|
||||
}
|
||||
}
|
||||
},
|
||||
child: MouseRegion(
|
||||
cursor: SystemMouseCursors.click,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: Icon(
|
||||
Icons.refresh,
|
||||
color: Colors.green,
|
||||
size: 28,
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
color: Colors.grey[700],
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
body: SafeArea(
|
||||
child: SingleChildScrollView(
|
||||
padding: MySpacing.x(0),
|
||||
child: GetBuilder<DailyTaskPlaningController>(
|
||||
init: dailyTaskPlaningController,
|
||||
tag: 'daily_task_planing_controller',
|
||||
builder: (controller) {
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
MySpacing.height(flexSpacing),
|
||||
Padding(
|
||||
padding: MySpacing.x(flexSpacing),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.end,
|
||||
children: [
|
||||
const SizedBox(width: 8),
|
||||
MyText.bodyMedium("Refresh", fontWeight: 600),
|
||||
Tooltip(
|
||||
message: 'Refresh Data',
|
||||
child: InkWell(
|
||||
borderRadius: BorderRadius.circular(24),
|
||||
onTap: () async {
|
||||
final projectId =
|
||||
projectController.selectedProjectId?.value;
|
||||
if (projectId != null) {
|
||||
try {
|
||||
await dailyTaskPlaningController
|
||||
.fetchTaskData(projectId);
|
||||
} catch (e) {
|
||||
debugPrint(
|
||||
'Error refreshing task data: ${e.toString()}');
|
||||
}
|
||||
}
|
||||
},
|
||||
child: MouseRegion(
|
||||
cursor: SystemMouseCursors.click,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: Icon(Icons.refresh,
|
||||
color: Colors.green, size: 28),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: MySpacing.x(flexSpacing),
|
||||
child: dailyProgressReportTab(),
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
),
|
||||
Padding(
|
||||
padding: MySpacing.x(flexSpacing),
|
||||
child: dailyProgressReportTab(),
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user