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;
|
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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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(),
|
|
||||||
),
|
|
||||||
]),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -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 {
|
|||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
],
|
);
|
||||||
);
|
},
|
||||||
},
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -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),
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}),
|
}),
|
||||||
|
@ -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),
|
||||||
|
@ -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(),
|
],
|
||||||
),
|
);
|
||||||
],
|
},
|
||||||
);
|
),
|
||||||
},
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user