feat: Refactor project selection handling and update UI across various screens

This commit is contained in:
Vaibhav Surve 2025-06-12 11:31:36 +05:30
parent b81ac33b2d
commit 56efbe8869
6 changed files with 492 additions and 385 deletions

View File

@ -10,12 +10,17 @@ class ProjectController extends GetxController {
RxList<ProjectModel> projects = <ProjectModel>[].obs; RxList<ProjectModel> projects = <ProjectModel>[].obs;
RxString? selectedProjectId; RxString? selectedProjectId;
RxBool isProjectListExpanded = false.obs; RxBool isProjectListExpanded = false.obs;
RxBool isProjectSelectionExpanded = false.obs; RxBool isProjectSelectionExpanded = false.obs;
RxBool isProjectDropdownExpanded = false.obs; RxBool isProjectDropdownExpanded = false.obs;
RxBool isLoading = true.obs; RxBool isLoading = true.obs;
RxBool isLoadingProjects = true.obs; RxBool isLoadingProjects = true.obs;
RxMap<String, RxBool> uploadingStates = <String, RxBool>{}.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 @override
void onInit() { void onInit() {
@ -57,6 +62,6 @@ class ProjectController extends GetxController {
Future<void> updateSelectedProject(String projectId) async { Future<void> updateSelectedProject(String projectId) async {
selectedProjectId?.value = projectId; selectedProjectId?.value = projectId;
await LocalStorage.saveString('selectedProjectId', projectId); await LocalStorage.saveString('selectedProjectId', projectId);
update(); update();
} }
} }

View File

@ -3,15 +3,12 @@ import 'package:get/get.dart';
import 'package:marco/helpers/theme/app_theme.dart'; import 'package:marco/helpers/theme/app_theme.dart';
import 'package:marco/helpers/utils/mixins/ui_mixin.dart'; import 'package:marco/helpers/utils/mixins/ui_mixin.dart';
import 'package:marco/helpers/utils/my_shadow.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_card.dart';
import 'package:marco/helpers/widgets/my_container.dart'; import 'package:marco/helpers/widgets/my_container.dart';
import 'package:marco/helpers/widgets/my_flex.dart'; import 'package:marco/helpers/widgets/my_flex.dart';
import 'package:marco/helpers/widgets/my_flex_item.dart'; import 'package:marco/helpers/widgets/my_flex_item.dart';
import 'package:marco/helpers/widgets/my_spacing.dart'; import 'package:marco/helpers/widgets/my_spacing.dart';
import 'package:marco/helpers/widgets/my_text.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/dashboard/attendance_screen_controller.dart';
import 'package:marco/controller/permission_controller.dart'; import 'package:marco/controller/permission_controller.dart';
import 'package:intl/intl.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/attendence_action_button.dart';
import 'package:marco/model/attendance/regualrize_action_button.dart'; import 'package:marco/model/attendance/regualrize_action_button.dart';
import 'package:marco/model/attendance/attendence_filter_sheet.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 { class AttendanceScreen extends StatefulWidget {
AttendanceScreen({super.key}); AttendanceScreen({super.key});
@ -57,169 +54,198 @@ class _AttendanceScreenState extends State<AttendanceScreen> with UIMixin {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Layout( return Scaffold(
child: GetBuilder<AttendanceController>( appBar: PreferredSize(
init: attendanceController, preferredSize: const Size.fromHeight(80),
tag: 'attendance_dashboard_controller', child: AppBar(
builder: (controller) { backgroundColor: const Color(0xFFF5F5F5),
return Column( elevation: 0.5,
crossAxisAlignment: CrossAxisAlignment.start, foregroundColor: Colors.black,
children: [ titleSpacing: 0,
Padding( leading: IconButton(
padding: MySpacing.x(flexSpacing), icon: const Icon(Icons.arrow_back_ios_new,
child: Row( color: Colors.black, size: 20),
mainAxisAlignment: MainAxisAlignment.spaceBetween, onPressed: () {
children: [ Get.offNamed('/dashboard');
MyText.titleMedium("Attendance", },
fontSize: 18, fontWeight: 600), ),
MyBreadcrumb( title: Padding(
children: [ padding: const EdgeInsets.only(top: 12.0),
MyBreadcrumbItem(name: 'Dashboard'), child: Column(
MyBreadcrumbItem(name: 'Attendance', active: true), crossAxisAlignment: CrossAxisAlignment.start,
], mainAxisAlignment: MainAxisAlignment.center,
), children: [
], MyText.titleLarge(
'Attendance',
fontWeight: 700,
color: Colors.black,
), ),
), const SizedBox(height: 4),
MySpacing.height(flexSpacing), GetBuilder<ProjectController>(
Row( builder: (projectController) {
mainAxisAlignment: MainAxisAlignment.end, 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: [ children: [
MyText.bodyMedium( MySpacing.height(flexSpacing),
"Filter", Row(
fontWeight: 600, mainAxisAlignment: MainAxisAlignment.end,
), children: [
Tooltip( MyText.bodyMedium("Filter", fontWeight: 600),
message: 'Filter Project', Tooltip(
child: InkWell( message: 'Filter Project',
borderRadius: BorderRadius.circular(24), child: InkWell(
onTap: () async { borderRadius: BorderRadius.circular(24),
final result = onTap: () async {
await showModalBottomSheet<Map<String, dynamic>>( final result = await showModalBottomSheet<
context: context, Map<String, dynamic>>(
isScrollControlled: true, context: context,
backgroundColor: Colors.white, isScrollControlled: true,
shape: const RoundedRectangleBorder( backgroundColor: Colors.white,
borderRadius: shape: const RoundedRectangleBorder(
BorderRadius.vertical(top: Radius.circular(12)), borderRadius: BorderRadius.vertical(
), top: Radius.circular(12)),
builder: (context) => AttendanceFilterBottomSheet( ),
controller: attendanceController, builder: (context) => AttendanceFilterBottomSheet(
permissionController: permissionController, controller: attendanceController,
selectedTab: selectedTab, permissionController: permissionController,
), selectedTab: selectedTab,
); ),
);
if (result != null) { if (result != null) {
final selectedProjectId = final selectedProjectId =
Get.find<ProjectController>() Get.find<ProjectController>()
.selectedProjectId .selectedProjectId
?.value; ?.value;
final selectedView = result['selectedTab'] as String?; final selectedView =
result['selectedTab'] as String?;
if (selectedProjectId != null && if (selectedProjectId != null &&
selectedProjectId != selectedProjectId !=
attendanceController.selectedProjectId) { attendanceController.selectedProjectId) {
attendanceController.selectedProjectId = attendanceController.selectedProjectId =
selectedProjectId; selectedProjectId;
try { try {
await attendanceController await attendanceController
.fetchEmployeesByProject(selectedProjectId); .fetchEmployeesByProject(
await attendanceController selectedProjectId);
.fetchAttendanceLogs(selectedProjectId); await attendanceController
await attendanceController .fetchAttendanceLogs(selectedProjectId);
.fetchRegularizationLogs(selectedProjectId); await attendanceController
await attendanceController .fetchRegularizationLogs(
.fetchProjectData(selectedProjectId); selectedProjectId);
} catch (_) {} await attendanceController
attendanceController .fetchProjectData(selectedProjectId);
.update(['attendance_dashboard_controller']); } catch (_) {}
} attendanceController.update(
['attendance_dashboard_controller']);
}
if (selectedView != null && if (selectedView != null &&
selectedView != selectedTab) { selectedView != selectedTab) {
setState(() { setState(() {
selectedTab = selectedView; selectedTab = selectedView;
}); });
} }
} }
}, },
child: MouseRegion( child: MouseRegion(
cursor: SystemMouseCursors.click, cursor: SystemMouseCursors.click,
child: Padding( child: Padding(
padding: const EdgeInsets.all(8.0), padding: const EdgeInsets.all(8.0),
child: Icon( child: Icon(
Icons.filter_list_alt, Icons.filter_list_alt,
color: Colors.blueAccent, color: Colors.blueAccent,
size: 28, size: 28,
),
),
), ),
), ),
), ),
), const SizedBox(width: 4),
), MyText.bodyMedium("Refresh", fontWeight: 600),
const SizedBox(width: 4), Tooltip(
MyText.bodyMedium( message: 'Refresh Data',
"Refresh", child: InkWell(
fontWeight: 600, borderRadius: BorderRadius.circular(24),
), onTap: () async {
Tooltip( final projectId = Get.find<ProjectController>()
message: 'Refresh Data', .selectedProjectId
child: InkWell( ?.value;
borderRadius: BorderRadius.circular(24),
onTap: () async {
final projectId = Get.find<ProjectController>()
.selectedProjectId
?.value;
if (projectId != null && projectId.isNotEmpty) { if (projectId != null && projectId.isNotEmpty) {
try { try {
await attendanceController await attendanceController
.fetchEmployeesByProject(projectId); .fetchEmployeesByProject(projectId);
await attendanceController await attendanceController
.fetchAttendanceLogs(projectId); .fetchAttendanceLogs(projectId);
await attendanceController await attendanceController
.fetchRegularizationLogs(projectId); .fetchRegularizationLogs(projectId);
await attendanceController await attendanceController
.fetchProjectData(projectId); .fetchProjectData(projectId);
attendanceController attendanceController.update(
.update(['attendance_dashboard_controller']); ['attendance_dashboard_controller']);
} catch (e) { } catch (e) {
debugPrint("Error refreshing data: $e"); debugPrint("Error refreshing data: $e");
} }
} }
}, },
child: MouseRegion( child: MouseRegion(
cursor: SystemMouseCursors.click, cursor: SystemMouseCursors.click,
child: Padding( child: Padding(
padding: const EdgeInsets.all(8.0), padding: const EdgeInsets.all(8.0),
child: Icon( child: Icon(
Icons.refresh, Icons.refresh,
color: Colors.green, color: Colors.green,
size: 28, 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(),
),
]),
),
],
);
},
), ),
); );
} }

View File

@ -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_card.dart';
import 'package:marco/helpers/widgets/my_spacing.dart'; import 'package:marco/helpers/widgets/my_spacing.dart';
import 'package:marco/helpers/widgets/my_text.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/permission_controller.dart';
import 'package:marco/model/employees/employees_screen_filter_sheet.dart'; import 'package:marco/model/employees/employees_screen_filter_sheet.dart';
import 'package:marco/model/employees/add_employee_bottom_sheet.dart'; import 'package:marco/model/employees/add_employee_bottom_sheet.dart';
import 'package:marco/controller/dashboard/employees_screen_controller.dart'; import 'package:marco/controller/dashboard/employees_screen_controller.dart';
import 'package:marco/helpers/widgets/avatar.dart'; import 'package:marco/helpers/widgets/avatar.dart';
import 'package:marco/model/employees/employee_detail_bottom_sheet.dart'; import 'package:marco/model/employees/employee_detail_bottom_sheet.dart';
import 'package:marco/controller/project_controller.dart';
class EmployeesScreen extends StatefulWidget { class EmployeesScreen extends StatefulWidget {
const EmployeesScreen({super.key}); const EmployeesScreen({super.key});
@ -79,7 +78,52 @@ class _EmployeesScreenState extends State<EmployeesScreen> with UIMixin {
@override @override
Widget build(BuildContext context) { 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( floatingActionButton: InkWell(
onTap: () async { onTap: () async {
final result = await showModalBottomSheet<bool>( final result = await showModalBottomSheet<bool>(
@ -120,13 +164,14 @@ class _EmployeesScreenState extends State<EmployeesScreen> with UIMixin {
), ),
), ),
), ),
child: GetBuilder<EmployeesScreenController>( body: SafeArea(
init: employeeScreenController, child: GetBuilder<EmployeesScreenController>(
tag: 'employee_screen_controller', init: employeeScreenController,
builder: (controller) { tag: 'employee_screen_controller',
return Stack( builder: (controller) {
children: [ return SingleChildScrollView(
Column( padding: const EdgeInsets.only(bottom: 80),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Padding( Padding(
@ -134,11 +179,6 @@ class _EmployeesScreenState extends State<EmployeesScreen> with UIMixin {
child: Row( child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [ children: [
MyText.titleMedium(
"Employees",
fontSize: 18,
fontWeight: 600,
),
MyBreadcrumb( MyBreadcrumb(
children: [ children: [
MyBreadcrumbItem(name: 'Dashboard'), MyBreadcrumbItem(name: 'Dashboard'),
@ -154,10 +194,7 @@ class _EmployeesScreenState extends State<EmployeesScreen> with UIMixin {
child: Row( child: Row(
mainAxisAlignment: MainAxisAlignment.end, mainAxisAlignment: MainAxisAlignment.end,
children: [ children: [
MyText.bodyMedium( MyText.bodyMedium("Filter", fontWeight: 600),
"Filter",
fontWeight: 600,
),
Tooltip( Tooltip(
message: 'Project', message: 'Project',
child: InkWell( child: InkWell(
@ -177,10 +214,7 @@ class _EmployeesScreenState extends State<EmployeesScreen> with UIMixin {
), ),
), ),
const SizedBox(width: 8), const SizedBox(width: 8),
MyText.bodyMedium( MyText.bodyMedium("Refresh", fontWeight: 600),
"Refresh",
fontWeight: 600,
),
Tooltip( Tooltip(
message: 'Refresh Data', message: 'Refresh Data',
child: InkWell( child: InkWell(
@ -208,9 +242,9 @@ class _EmployeesScreenState extends State<EmployeesScreen> with UIMixin {
), ),
], ],
), ),
], );
); },
}, ),
), ),
); );
} }

View File

@ -9,6 +9,7 @@ import 'package:marco/helpers/services/api_endpoints.dart';
import 'package:marco/images.dart'; import 'package:marco/images.dart';
import 'package:marco/controller/project_controller.dart'; import 'package:marco/controller/project_controller.dart';
import 'package:marco/view/layouts/user_profile_right_bar.dart'; import 'package:marco/view/layouts/user_profile_right_bar.dart';
class Layout extends StatefulWidget { class Layout extends StatefulWidget {
final Widget? child; final Widget? child;
final Widget? floatingActionButton; final Widget? floatingActionButton;
@ -42,7 +43,7 @@ class _LayoutState extends State<Layout> {
Widget _buildScaffold(BuildContext context, {bool isMobile = false}) { Widget _buildScaffold(BuildContext context, {bool isMobile = false}) {
return Scaffold( return Scaffold(
key: controller.scaffoldKey, key: controller.scaffoldKey,
endDrawer: UserProfileBar(), endDrawer: UserProfileBar(),
floatingActionButton: widget.floatingActionButton, floatingActionButton: widget.floatingActionButton,
body: SafeArea( body: SafeArea(
child: Column( child: Column(
@ -51,8 +52,8 @@ class _LayoutState extends State<Layout> {
Expanded( Expanded(
child: SingleChildScrollView( child: SingleChildScrollView(
key: controller.scrollKey, key: controller.scrollKey,
padding: EdgeInsets.symmetric(horizontal: 0, vertical: isMobile ? 16 : 32), padding: EdgeInsets.symmetric(
horizontal: 0, vertical: isMobile ? 16 : 32),
child: widget.child, child: widget.child,
), ),
), ),
@ -64,7 +65,7 @@ class _LayoutState extends State<Layout> {
Widget _buildHeader(BuildContext context, bool isMobile) { Widget _buildHeader(BuildContext context, bool isMobile) {
return Padding( return Padding(
padding: const EdgeInsets.symmetric(horizontal: 0), padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 6),
child: Obx(() { child: Obx(() {
final isExpanded = projectController.isProjectSelectionExpanded.value; final isExpanded = projectController.isProjectSelectionExpanded.value;
final selectedProjectId = projectController.selectedProjectId?.value; final selectedProjectId = projectController.selectedProjectId?.value;
@ -77,102 +78,96 @@ class _LayoutState extends State<Layout> {
.updateSelectedProject(projectController.projects.first.id); .updateSelectedProject(projectController.projects.first.id);
} }
return AnimatedContainer( return Card(
duration: const Duration(milliseconds: 300), elevation: 4,
decoration: BoxDecoration( shape: RoundedRectangleBorder(
color: Colors.white, borderRadius: BorderRadius.circular(isExpanded ? 16 : 12),
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),
),
],
), ),
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 16), margin: EdgeInsets.zero,
child: Column( child: Padding(
crossAxisAlignment: CrossAxisAlignment.start, padding: const EdgeInsets.all(10),
children: [ child: Column(
Row( crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
ClipRRect( Row(
borderRadius: BorderRadius.circular(8), children: [
child: Image.asset( ClipRRect(
Images.logoDark, borderRadius: BorderRadius.circular(8),
height: 50, child: Image.asset(
width: 50, Images.logoDark,
fit: BoxFit.contain, height: 50,
width: 50,
fit: BoxFit.contain,
),
), ),
), const SizedBox(width: 12),
const SizedBox(width: 12), Expanded(
Expanded( child: GestureDetector(
child: GestureDetector( onTap: () => projectController
onTap: () => .isProjectSelectionExpanded
projectController.isProjectSelectionExpanded.toggle(), .toggle(),
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Row( Row(
children: [ children: [
Expanded( Expanded(
child: Row( child: Row(
children: [ children: [
Expanded( Expanded(
child: MyText.bodyLarge( child: MyText.bodyLarge(
selectedProject?.name ?? selectedProject?.name ??
"Select Project", "Select Project",
fontWeight: 700, fontWeight: 700,
maxLines: 1, maxLines: 1,
overflow: TextOverflow.ellipsis, overflow: TextOverflow.ellipsis,
),
), ),
), Icon(
Icon( isExpanded
isExpanded ? Icons.arrow_drop_up_outlined
? Icons.arrow_drop_up_outlined : Icons.arrow_drop_down_outlined,
: Icons.arrow_drop_down_outlined, color: Colors.black,
color: Colors.black, ),
), ],
], ),
), ),
), ],
], ),
), MyText.bodyMedium(
MyText.bodyMedium( "Hi, ${employeeInfo?.firstName ?? ''}",
"Hi, ${employeeInfo?.firstName ?? ''}", color: Colors.black54,
color: Colors.black54, ),
), ],
], ),
), ),
), ),
), if (isBetaEnvironment)
if (isBetaEnvironment) Container(
Container( margin: const EdgeInsets.only(left: 8),
margin: const EdgeInsets.only(left: 8), padding: const EdgeInsets.symmetric(
padding: const EdgeInsets.symmetric( horizontal: 8, vertical: 2),
horizontal: 8, vertical: 2), decoration: BoxDecoration(
decoration: BoxDecoration( color: Colors.deepPurple,
color: Colors.deepPurple, borderRadius: BorderRadius.circular(6),
borderRadius: BorderRadius.circular(6), ),
), child: MyText.bodySmall(
child: MyText.bodySmall( 'BETA',
'BETA', color: Colors.white,
color: Colors.white, fontWeight: 700,
fontWeight: 700, ),
), ),
IconButton(
icon: Icon(Icons.menu),
onPressed: () =>
controller.scaffoldKey.currentState?.openEndDrawer(),
), ),
IconButton( ],
icon: Icon(Icons.menu), ),
onPressed: () => const SizedBox(height: 8),
controller.scaffoldKey.currentState?.openEndDrawer(), if (isExpanded) _buildProjectList(context, isMobile),
), ],
], ),
),
const SizedBox(height: 8),
if (isExpanded) _buildProjectList(context, isMobile),
],
), ),
); );
}), }),

View File

@ -4,13 +4,10 @@ import 'package:intl/intl.dart';
import 'package:marco/helpers/theme/app_theme.dart'; import 'package:marco/helpers/theme/app_theme.dart';
import 'package:marco/helpers/utils/mixins/ui_mixin.dart'; import 'package:marco/helpers/utils/mixins/ui_mixin.dart';
import 'package:marco/helpers/utils/my_shadow.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_card.dart';
import 'package:marco/helpers/widgets/my_container.dart'; import 'package:marco/helpers/widgets/my_container.dart';
import 'package:marco/helpers/widgets/my_spacing.dart'; import 'package:marco/helpers/widgets/my_spacing.dart';
import 'package:marco/helpers/widgets/my_text.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/permission_controller.dart';
import 'package:marco/controller/dashboard/daily_task_controller.dart'; import 'package:marco/controller/dashboard/daily_task_controller.dart';
import 'package:marco/model/dailyTaskPlaning/daily_progress_report_filter.dart'; import 'package:marco/model/dailyTaskPlaning/daily_progress_report_filter.dart';
@ -68,47 +65,74 @@ class _DailyProgressReportScreenState extends State<DailyProgressReportScreen>
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Layout( return Scaffold(
child: GetBuilder<DailyTaskController>( appBar: PreferredSize(
init: dailyTaskController, preferredSize: const Size.fromHeight(80),
tag: 'daily_progress_report_controller', child: AppBar(
builder: (controller) { backgroundColor: const Color(0xFFF5F5F5),
return Column( elevation: 0.5,
crossAxisAlignment: CrossAxisAlignment.start, foregroundColor: Colors.black,
children: [ titleSpacing: 0,
_buildHeader(), leading: IconButton(
MySpacing.height(flexSpacing), icon: const Icon(Icons.arrow_back_ios_new),
_buildActionBar(), onPressed: () {
Padding( Get.offNamed('/dashboard');
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),
],
), ),
], 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() { Widget _buildActionBar() {
return Padding( return Padding(
padding: MySpacing.x(flexSpacing), padding: MySpacing.x(flexSpacing),

View File

@ -3,12 +3,9 @@ import 'package:get/get.dart';
import 'package:marco/helpers/theme/app_theme.dart'; import 'package:marco/helpers/theme/app_theme.dart';
import 'package:marco/helpers/utils/mixins/ui_mixin.dart'; import 'package:marco/helpers/utils/mixins/ui_mixin.dart';
import 'package:marco/helpers/utils/my_shadow.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_card.dart';
import 'package:marco/helpers/widgets/my_spacing.dart'; import 'package:marco/helpers/widgets/my_spacing.dart';
import 'package:marco/helpers/widgets/my_text.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/permission_controller.dart';
import 'package:marco/controller/task_planing/daily_task_planing_controller.dart'; import 'package:marco/controller/task_planing/daily_task_planing_controller.dart';
import 'package:marco/controller/project_controller.dart'; import 'package:marco/controller/project_controller.dart';
@ -50,82 +47,108 @@ class _DailyTaskPlaningScreenState extends State<DailyTaskPlaningScreen>
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Layout( return Scaffold(
child: GetBuilder<DailyTaskPlaningController>( appBar: PreferredSize(
init: dailyTaskPlaningController, preferredSize: const Size.fromHeight(80),
tag: 'daily_task_planing_controller', child: AppBar(
builder: (controller) { backgroundColor: const Color(0xFFF5F5F5),
return Column( elevation: 0.5,
crossAxisAlignment: CrossAxisAlignment.start, foregroundColor: Colors.black,
children: [ titleSpacing: 0,
Padding( leading: IconButton(
padding: MySpacing.x(flexSpacing), icon: const Icon(Icons.arrow_back_ios_new),
child: Row( onPressed: () {
mainAxisAlignment: MainAxisAlignment.spaceBetween, Get.offNamed('/dashboard');
children: [ },
MyText.titleMedium("Daily Task Planning", ),
fontSize: 18, fontWeight: 600), title: Padding(
MyBreadcrumb( padding: const EdgeInsets.only(top: 12.0),
children: [ child: Column(
MyBreadcrumbItem(name: 'Dashboard'), crossAxisAlignment: CrossAxisAlignment.start,
MyBreadcrumbItem( mainAxisAlignment: MainAxisAlignment.center,
name: 'Daily Task Planning', active: true), children: [
], MyText.titleLarge(
), 'Daily Task Planning',
], fontWeight: 700,
color: Colors.black,
), ),
), const SizedBox(height: 4),
MySpacing.height(flexSpacing), GetBuilder<ProjectController>(
Padding( builder: (projectController) {
padding: MySpacing.x(flexSpacing), final projectName =
child: Row( projectController.selectedProject?.name ??
mainAxisAlignment: MainAxisAlignment.end, 'Select Project';
children: [ return MyText.bodySmall(
const SizedBox(width: 8), projectName,
MyText.bodyMedium(
"Refresh",
fontWeight: 600, fontWeight: 600,
), maxLines: 1,
Tooltip( overflow: TextOverflow.ellipsis,
message: 'Refresh Data', color: Colors.grey[700],
child: InkWell( );
borderRadius: BorderRadius.circular(24), },
onTap: () async { ),
final projectId = ],
projectController.selectedProjectId?.value; ),
if (projectId != null) { ),
try { ),
await dailyTaskPlaningController ),
.fetchTaskData(projectId); body: SafeArea(
} catch (e) { child: SingleChildScrollView(
debugPrint( padding: MySpacing.x(0),
'Error refreshing task data: ${e.toString()}'); child: GetBuilder<DailyTaskPlaningController>(
} init: dailyTaskPlaningController,
} tag: 'daily_task_planing_controller',
}, builder: (controller) {
child: MouseRegion( return Column(
cursor: SystemMouseCursors.click, crossAxisAlignment: CrossAxisAlignment.start,
child: Padding( children: [
padding: const EdgeInsets.all(8.0), MySpacing.height(flexSpacing),
child: Icon( Padding(
Icons.refresh, padding: MySpacing.x(flexSpacing),
color: Colors.green, child: Row(
size: 28, 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),
Padding( child: dailyProgressReportTab(),
padding: MySpacing.x(flexSpacing), ),
child: dailyProgressReportTab(), ],
), );
], },
); ),
}, ),
), ),
); );
} }