Merge pull request 'Feature_Dynamic_Menu' (#66) from Feature_Dynamic_Menu into main
Reviewed-on: #66
This commit is contained in:
commit
80d5fc5f21
@ -9,12 +9,12 @@ import 'package:marco/helpers/services/app_logger.dart';
|
||||
import 'package:marco/helpers/services/api_service.dart';
|
||||
import 'package:marco/helpers/widgets/my_image_compressor.dart';
|
||||
|
||||
import 'package:marco/model/attendance_model.dart';
|
||||
import 'package:marco/model/attendance/attendance_model.dart';
|
||||
import 'package:marco/model/project_model.dart';
|
||||
import 'package:marco/model/employee_model.dart';
|
||||
import 'package:marco/model/attendance_log_model.dart';
|
||||
import 'package:marco/model/employees/employee_model.dart';
|
||||
import 'package:marco/model/attendance/attendance_log_model.dart';
|
||||
import 'package:marco/model/regularization_log_model.dart';
|
||||
import 'package:marco/model/attendance_log_view_model.dart';
|
||||
import 'package:marco/model/attendance/attendance_log_view_model.dart';
|
||||
|
||||
import 'package:marco/controller/project_controller.dart';
|
||||
|
||||
|
@ -3,7 +3,7 @@ import 'package:get/get.dart';
|
||||
import 'package:marco/helpers/services/app_logger.dart';
|
||||
import 'package:marco/helpers/services/api_service.dart';
|
||||
import 'package:marco/model/project_model.dart';
|
||||
import 'package:marco/model/daily_task_model.dart';
|
||||
import 'package:marco/model/dailyTaskPlanning/daily_task_model.dart';
|
||||
|
||||
class DailyTaskController extends GetxController {
|
||||
List<ProjectModel> projects = [];
|
||||
|
@ -1,9 +1,9 @@
|
||||
import 'package:get/get.dart';
|
||||
import 'package:marco/helpers/services/app_logger.dart';
|
||||
import 'package:marco/helpers/services/api_service.dart';
|
||||
import 'package:marco/model/attendance_model.dart';
|
||||
import 'package:marco/model/attendance/attendance_model.dart';
|
||||
import 'package:marco/model/project_model.dart';
|
||||
import 'package:marco/model/employee_model.dart';
|
||||
import 'package:marco/model/employees/employee_model.dart';
|
||||
import 'package:marco/model/employees/employee_details_model.dart';
|
||||
import 'package:marco/controller/project_controller.dart';
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
import 'package:get/get.dart';
|
||||
import 'package:marco/helpers/services/app_logger.dart';
|
||||
import 'package:marco/helpers/services/api_service.dart';
|
||||
import 'package:marco/model/employee_model.dart';
|
||||
import 'package:marco/model/employees/employee_model.dart';
|
||||
import 'package:marco/helpers/widgets/my_snackbar.dart';
|
||||
import 'package:marco/controller/directory/directory_controller.dart';
|
||||
|
||||
|
86
lib/controller/dynamicMenu/dynamic_menu_controller.dart
Normal file
86
lib/controller/dynamicMenu/dynamic_menu_controller.dart
Normal file
@ -0,0 +1,86 @@
|
||||
import 'dart:async';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:marco/model/dynamicMenu/dynamic_menu_model.dart';
|
||||
import 'package:marco/helpers/services/api_service.dart';
|
||||
import 'package:marco/helpers/services/app_logger.dart';
|
||||
import 'package:marco/helpers/services/storage/local_storage.dart';
|
||||
|
||||
class DynamicMenuController extends GetxController {
|
||||
// UI reactive states
|
||||
final RxBool isLoading = false.obs;
|
||||
final RxBool hasError = false.obs;
|
||||
final RxString errorMessage = ''.obs;
|
||||
final RxList<MenuItem> menuItems = <MenuItem>[].obs;
|
||||
|
||||
Timer? _autoRefreshTimer;
|
||||
@override
|
||||
void onInit() {
|
||||
super.onInit();
|
||||
fetchMenu();
|
||||
|
||||
/// Auto refresh every 5 minutes (adjust as needed)
|
||||
_autoRefreshTimer = Timer.periodic(
|
||||
const Duration(minutes: 1),
|
||||
(_) => fetchMenu(),
|
||||
);
|
||||
}
|
||||
|
||||
/// Fetch dynamic menu from API with error and local storage support
|
||||
Future<void> fetchMenu() async {
|
||||
isLoading.value = true;
|
||||
hasError.value = false;
|
||||
errorMessage.value = '';
|
||||
|
||||
try {
|
||||
final responseData = await ApiService.getMenuApi();
|
||||
if (responseData != null) {
|
||||
// Directly parse full JSON into MenuResponse
|
||||
final menuResponse = MenuResponse.fromJson(responseData);
|
||||
|
||||
menuItems.assignAll(menuResponse.data);
|
||||
|
||||
// Save menus for offline use
|
||||
await LocalStorage.setMenus(menuItems);
|
||||
|
||||
logSafe("Menu loaded from API with ${menuItems.length} items");
|
||||
} else {
|
||||
// If API fails, load from cache
|
||||
final cachedMenus = LocalStorage.getMenus();
|
||||
if (cachedMenus.isNotEmpty) {
|
||||
menuItems.assignAll(cachedMenus);
|
||||
logSafe("Loaded menus from cache: ${menuItems.length} items");
|
||||
} else {
|
||||
hasError.value = true;
|
||||
errorMessage.value = "Failed to fetch menu";
|
||||
menuItems.clear();
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
logSafe("Menu fetch exception: $e", level: LogLevel.error);
|
||||
|
||||
// On error, load cached menus
|
||||
final cachedMenus = LocalStorage.getMenus();
|
||||
if (cachedMenus.isNotEmpty) {
|
||||
menuItems.assignAll(cachedMenus);
|
||||
logSafe("Loaded menus from cache after error: ${menuItems.length}");
|
||||
} else {
|
||||
hasError.value = true;
|
||||
errorMessage.value = e.toString();
|
||||
menuItems.clear();
|
||||
}
|
||||
} finally {
|
||||
isLoading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
bool isMenuAllowed(String menuName) {
|
||||
final menu = menuItems.firstWhereOrNull((m) => m.name == menuName);
|
||||
return menu?.available ?? false; // default false if not found
|
||||
}
|
||||
|
||||
@override
|
||||
void onClose() {
|
||||
_autoRefreshTimer?.cancel(); // clean up timer
|
||||
super.onClose();
|
||||
}
|
||||
}
|
@ -12,7 +12,7 @@ import 'package:marco/controller/expense/expense_screen_controller.dart';
|
||||
import 'package:marco/helpers/services/api_service.dart';
|
||||
import 'package:marco/helpers/services/app_logger.dart';
|
||||
import 'package:marco/helpers/widgets/my_snackbar.dart';
|
||||
import 'package:marco/model/employee_model.dart';
|
||||
import 'package:marco/model/employees/employee_model.dart';
|
||||
import 'package:marco/model/expense/expense_type_model.dart';
|
||||
import 'package:marco/model/expense/payment_types_model.dart';
|
||||
import 'package:mime/mime.dart';
|
||||
|
@ -2,7 +2,7 @@ import 'package:get/get.dart';
|
||||
import 'package:marco/helpers/services/api_service.dart';
|
||||
import 'package:marco/helpers/services/app_logger.dart';
|
||||
import 'package:marco/model/expense/expense_detail_model.dart';
|
||||
import 'package:marco/model/employee_model.dart';
|
||||
import 'package:marco/model/employees/employee_model.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class ExpenseDetailController extends GetxController {
|
||||
|
@ -6,7 +6,7 @@ import 'package:marco/model/expense/expense_list_model.dart';
|
||||
import 'package:marco/model/expense/payment_types_model.dart';
|
||||
import 'package:marco/model/expense/expense_type_model.dart';
|
||||
import 'package:marco/model/expense/expense_status_model.dart';
|
||||
import 'package:marco/model/employee_model.dart';
|
||||
import 'package:marco/model/employees/employee_model.dart';
|
||||
import 'package:marco/helpers/widgets/my_snackbar.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
|
@ -1,17 +0,0 @@
|
||||
import 'package:marco/controller/my_controller.dart';
|
||||
import 'package:marco/helpers/widgets/my_text_utils.dart';
|
||||
import 'package:marco/model/time_line.dart';
|
||||
|
||||
class TimeLineController extends MyController {
|
||||
List<TimeLineModel> timeline = [];
|
||||
List<String> dummyTexts = List.generate(12, (index) => MyTextUtils.getDummyText(60));
|
||||
|
||||
@override
|
||||
void onInit() {
|
||||
TimeLineModel.dummyList.then((value) {
|
||||
timeline = value.sublist(0, 6);
|
||||
update();
|
||||
});
|
||||
super.onInit();
|
||||
}
|
||||
}
|
@ -5,7 +5,7 @@ import 'package:shared_preferences/shared_preferences.dart';
|
||||
import 'package:marco/helpers/services/app_logger.dart';
|
||||
import 'package:marco/helpers/services/permission_service.dart';
|
||||
import 'package:marco/model/user_permission.dart';
|
||||
import 'package:marco/model/employee_info.dart';
|
||||
import 'package:marco/model/employees/employee_info.dart';
|
||||
import 'package:marco/model/projects_model.dart';
|
||||
|
||||
class PermissionController extends GetxController {
|
||||
|
@ -3,7 +3,7 @@ import 'package:marco/helpers/services/app_logger.dart';
|
||||
import 'package:marco/helpers/services/api_service.dart';
|
||||
import 'package:marco/helpers/widgets/my_form_validator.dart';
|
||||
import 'package:marco/helpers/widgets/my_snackbar.dart';
|
||||
import 'package:marco/model/dailyTaskPlaning/master_work_category_model.dart';
|
||||
import 'package:marco/model/dailyTaskPlanning/master_work_category_model.dart';
|
||||
|
||||
class AddTaskController extends GetxController {
|
||||
RxMap<String, RxBool> uploadingStates = <String, RxBool>{}.obs;
|
@ -4,10 +4,10 @@ import 'package:marco/helpers/services/api_service.dart';
|
||||
import 'package:marco/helpers/widgets/my_form_validator.dart';
|
||||
import 'package:marco/helpers/widgets/my_snackbar.dart';
|
||||
import 'package:marco/model/project_model.dart';
|
||||
import 'package:marco/model/dailyTaskPlaning/daily_task_planing_model.dart';
|
||||
import 'package:marco/model/employee_model.dart';
|
||||
import 'package:marco/model/dailyTaskPlanning/daily_task_planning_model.dart';
|
||||
import 'package:marco/model/employees/employee_model.dart';
|
||||
|
||||
class DailyTaskPlaningController extends GetxController {
|
||||
class DailyTaskPlanningController extends GetxController {
|
||||
List<ProjectModel> projects = [];
|
||||
List<EmployeeModel> employees = [];
|
||||
List<TaskPlanningDetailsModel> dailyTasks = [];
|
@ -7,12 +7,12 @@ import 'package:image_picker/image_picker.dart';
|
||||
import 'package:marco/helpers/services/app_logger.dart';
|
||||
|
||||
import 'package:marco/controller/my_controller.dart';
|
||||
import 'package:marco/controller/task_planing/daily_task_planing_controller.dart';
|
||||
import 'package:marco/controller/task_Planning/daily_task_Planning_controller.dart';
|
||||
import 'package:marco/helpers/services/api_service.dart';
|
||||
import 'package:marco/helpers/widgets/my_form_validator.dart';
|
||||
import 'package:marco/helpers/widgets/my_image_compressor.dart';
|
||||
import 'package:marco/helpers/widgets/my_snackbar.dart';
|
||||
import 'package:marco/model/dailyTaskPlaning/work_status_model.dart';
|
||||
import 'package:marco/model/dailyTaskPlanning/work_status_model.dart';
|
||||
|
||||
enum ApiStatus { idle, loading, success, failure }
|
||||
|
||||
@ -34,7 +34,7 @@ class ReportTaskActionController extends MyController {
|
||||
final RxString selectedWorkStatusName = ''.obs;
|
||||
|
||||
final MyFormValidator basicValidator = MyFormValidator();
|
||||
final DailyTaskPlaningController taskController = Get.put(DailyTaskPlaningController());
|
||||
final DailyTaskPlanningController taskController = Get.put(DailyTaskPlanningController());
|
||||
final ImagePicker _picker = ImagePicker();
|
||||
|
||||
final assignedDateController = TextEditingController();
|
@ -6,7 +6,7 @@ import 'package:marco/helpers/services/api_service.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:marco/helpers/services/app_logger.dart';
|
||||
import 'package:marco/helpers/widgets/my_snackbar.dart';
|
||||
import 'package:marco/controller/task_planing/daily_task_planing_controller.dart';
|
||||
import 'package:marco/controller/task_Planning/daily_task_Planning_controller.dart';
|
||||
import 'package:image_picker/image_picker.dart';
|
||||
import 'dart:io';
|
||||
import 'dart:convert';
|
||||
@ -14,7 +14,7 @@ import 'package:marco/helpers/widgets/my_image_compressor.dart';
|
||||
|
||||
enum ApiStatus { idle, loading, success, failure }
|
||||
|
||||
final DailyTaskPlaningController taskController = Get.put(DailyTaskPlaningController());
|
||||
final DailyTaskPlanningController taskController = Get.put(DailyTaskPlanningController());
|
||||
final ImagePicker _picker = ImagePicker();
|
||||
|
||||
class ReportTaskController extends MyController {
|
@ -1,26 +0,0 @@
|
||||
import 'package:marco/controller/my_controller.dart';
|
||||
import 'package:marco/helpers/widgets/my_text_utils.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:marco/model/drag_n_drop_model.dart';
|
||||
|
||||
class DragNDropController extends MyController {
|
||||
List<DragNDropModel> dragNDrop = [];
|
||||
final scrollController = ScrollController();
|
||||
final gridViewKey = GlobalKey();
|
||||
List<String> dummyTexts = List.generate(12, (index) => MyTextUtils.getDummyText(60));
|
||||
|
||||
@override
|
||||
void onInit() {
|
||||
DragNDropModel.dummyList.then((value) {
|
||||
dragNDrop = value;
|
||||
update();
|
||||
});
|
||||
super.onInit();
|
||||
}
|
||||
|
||||
void onReorder(int oldIndex, int newIndex) {
|
||||
final item = dragNDrop.removeAt(oldIndex);
|
||||
dragNDrop.insert(newIndex, item);
|
||||
update();
|
||||
}
|
||||
}
|
@ -62,4 +62,7 @@ class ApiEndpoints {
|
||||
static const String getMasterExpenseTypes = "/master/expenses-types";
|
||||
static const String updateExpenseStatus = "/expense/action";
|
||||
static const String deleteExpense = "/expense/delete";
|
||||
|
||||
////// Dynamic Menu Module API Endpoints
|
||||
static const String getDynamicMenu = "/appmenu/get/menu-mobile";
|
||||
}
|
||||
|
@ -239,6 +239,48 @@ class ApiService {
|
||||
}
|
||||
}
|
||||
|
||||
// === Menu APIs === //
|
||||
|
||||
/// Get Sidebar Menu API
|
||||
static Future<Map<String, dynamic>?> getMenuApi() async {
|
||||
logSafe("Fetching sidebar menu...");
|
||||
|
||||
try {
|
||||
final response = await _getRequest(ApiEndpoints.getDynamicMenu);
|
||||
if (response == null) {
|
||||
logSafe("Menu request failed: null response", level: LogLevel.error);
|
||||
return null;
|
||||
}
|
||||
|
||||
final body = response.body.trim();
|
||||
if (body.isEmpty) {
|
||||
logSafe("Menu response body is empty", level: LogLevel.error);
|
||||
return null;
|
||||
}
|
||||
|
||||
final jsonResponse = jsonDecode(body);
|
||||
if (jsonResponse is Map<String, dynamic>) {
|
||||
if (jsonResponse['success'] == true) {
|
||||
logSafe("Sidebar menu fetched successfully");
|
||||
return jsonResponse; // ✅ return full response
|
||||
} else {
|
||||
logSafe(
|
||||
"Failed to fetch sidebar menu: ${jsonResponse['message'] ?? 'Unknown error'}",
|
||||
level: LogLevel.warning,
|
||||
);
|
||||
}
|
||||
} else {
|
||||
logSafe("Unexpected response structure: $jsonResponse",
|
||||
level: LogLevel.error);
|
||||
}
|
||||
} catch (e, stack) {
|
||||
logSafe("Exception during getMenuApi: $e", level: LogLevel.error);
|
||||
logSafe("StackTrace: $stack", level: LogLevel.debug);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
// === Expense APIs === //
|
||||
|
||||
/// Edit Expense API
|
||||
@ -1085,66 +1127,65 @@ class ApiService {
|
||||
? _parseResponse(res, label: 'Regularization Logs')
|
||||
: null);
|
||||
|
||||
static Future<bool> uploadAttendanceImage(
|
||||
String id,
|
||||
String employeeId,
|
||||
XFile? imageFile,
|
||||
double latitude,
|
||||
double longitude, {
|
||||
required String imageName,
|
||||
required String projectId,
|
||||
String comment = "",
|
||||
required int action,
|
||||
bool imageCapture = true,
|
||||
required String markTime, // 👈 now required
|
||||
required String date, // 👈 new required param
|
||||
}) async {
|
||||
final body = {
|
||||
"id": id,
|
||||
"employeeId": employeeId,
|
||||
"projectId": projectId,
|
||||
"markTime": markTime, // 👈 directly from UI
|
||||
"comment": comment,
|
||||
"action": action,
|
||||
"date": date, // 👈 directly from UI
|
||||
if (imageCapture) "latitude": '$latitude',
|
||||
if (imageCapture) "longitude": '$longitude',
|
||||
};
|
||||
static Future<bool> uploadAttendanceImage(
|
||||
String id,
|
||||
String employeeId,
|
||||
XFile? imageFile,
|
||||
double latitude,
|
||||
double longitude, {
|
||||
required String imageName,
|
||||
required String projectId,
|
||||
String comment = "",
|
||||
required int action,
|
||||
bool imageCapture = true,
|
||||
required String markTime, // 👈 now required
|
||||
required String date, // 👈 new required param
|
||||
}) async {
|
||||
final body = {
|
||||
"id": id,
|
||||
"employeeId": employeeId,
|
||||
"projectId": projectId,
|
||||
"markTime": markTime, // 👈 directly from UI
|
||||
"comment": comment,
|
||||
"action": action,
|
||||
"date": date, // 👈 directly from UI
|
||||
if (imageCapture) "latitude": '$latitude',
|
||||
if (imageCapture) "longitude": '$longitude',
|
||||
};
|
||||
|
||||
if (imageCapture && imageFile != null) {
|
||||
try {
|
||||
final bytes = await imageFile.readAsBytes();
|
||||
final fileSize = await imageFile.length();
|
||||
final contentType = "image/${imageFile.path.split('.').last}";
|
||||
body["image"] = {
|
||||
"fileName": imageName,
|
||||
"contentType": contentType,
|
||||
"fileSize": fileSize,
|
||||
"description": "Employee attendance photo",
|
||||
"base64Data": base64Encode(bytes),
|
||||
};
|
||||
} catch (e) {
|
||||
logSafe("Image encoding error: $e", level: LogLevel.error);
|
||||
return false;
|
||||
if (imageCapture && imageFile != null) {
|
||||
try {
|
||||
final bytes = await imageFile.readAsBytes();
|
||||
final fileSize = await imageFile.length();
|
||||
final contentType = "image/${imageFile.path.split('.').last}";
|
||||
body["image"] = {
|
||||
"fileName": imageName,
|
||||
"contentType": contentType,
|
||||
"fileSize": fileSize,
|
||||
"description": "Employee attendance photo",
|
||||
"base64Data": base64Encode(bytes),
|
||||
};
|
||||
} catch (e) {
|
||||
logSafe("Image encoding error: $e", level: LogLevel.error);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
final response = await _postRequest(
|
||||
ApiEndpoints.uploadAttendanceImage,
|
||||
body,
|
||||
customTimeout: extendedTimeout,
|
||||
);
|
||||
|
||||
if (response == null) return false;
|
||||
|
||||
final json = jsonDecode(response.body);
|
||||
if (response.statusCode == 200 && json['success'] == true) return true;
|
||||
|
||||
logSafe("Failed to upload image: ${json['message'] ?? 'Unknown error'}");
|
||||
return false;
|
||||
}
|
||||
|
||||
final response = await _postRequest(
|
||||
ApiEndpoints.uploadAttendanceImage,
|
||||
body,
|
||||
customTimeout: extendedTimeout,
|
||||
);
|
||||
|
||||
if (response == null) return false;
|
||||
|
||||
final json = jsonDecode(response.body);
|
||||
if (response.statusCode == 200 && json['success'] == true) return true;
|
||||
|
||||
logSafe("Failed to upload image: ${json['message'] ?? 'Unknown error'}");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
static String generateImageName(String employeeId, int count) {
|
||||
final now = DateTime.now();
|
||||
final dateStr = DateFormat('yyyyMMdd_HHmmss').format(now);
|
||||
|
@ -3,7 +3,7 @@ import 'package:logger/logger.dart';
|
||||
|
||||
import 'package:marco/controller/dashboard/attendance_screen_controller.dart';
|
||||
import 'package:marco/controller/dashboard/daily_task_controller.dart';
|
||||
import 'package:marco/controller/task_planing/daily_task_planing_controller.dart';
|
||||
import 'package:marco/controller/task_Planning/daily_task_Planning_controller.dart';
|
||||
import 'package:marco/controller/expense/expense_screen_controller.dart';
|
||||
import 'package:marco/controller/expense/expense_detail_controller.dart';
|
||||
|
||||
@ -88,14 +88,14 @@ class NotificationActionHandler {
|
||||
return;
|
||||
}
|
||||
|
||||
_safeControllerUpdate<DailyTaskPlaningController>(
|
||||
_safeControllerUpdate<DailyTaskPlanningController>(
|
||||
onFound: (controller) {
|
||||
controller.fetchTaskData(projectId);
|
||||
},
|
||||
notFoundMessage:
|
||||
'⚠️ DailyTaskPlaningController not found, cannot refresh.',
|
||||
'⚠️ DailyTaskPlanningController not found, cannot refresh.',
|
||||
successMessage:
|
||||
'✅ DailyTaskPlaningController refreshed from notification.',
|
||||
'✅ DailyTaskPlanningController refreshed from notification.',
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -4,7 +4,7 @@ import 'package:http/http.dart' as http;
|
||||
|
||||
import 'package:marco/helpers/services/app_logger.dart';
|
||||
import 'package:marco/model/user_permission.dart';
|
||||
import 'package:marco/model/employee_info.dart';
|
||||
import 'package:marco/model/employees/employee_info.dart';
|
||||
import 'package:marco/model/projects_model.dart';
|
||||
import 'package:marco/helpers/services/storage/local_storage.dart';
|
||||
import 'package:marco/helpers/services/auth_service.dart';
|
||||
|
@ -5,8 +5,9 @@ import 'package:marco/controller/project_controller.dart';
|
||||
import 'package:marco/helpers/services/auth_service.dart';
|
||||
import 'package:marco/helpers/services/localizations/language.dart';
|
||||
import 'package:marco/helpers/theme/theme_customizer.dart';
|
||||
import 'package:marco/model/employee_info.dart';
|
||||
import 'package:marco/model/employees/employee_info.dart';
|
||||
import 'package:marco/model/user_permission.dart';
|
||||
import 'package:marco/model/dynamicMenu/dynamic_menu_model.dart';
|
||||
|
||||
class LocalStorage {
|
||||
static const String _loggedInUserKey = "user";
|
||||
@ -19,6 +20,7 @@ class LocalStorage {
|
||||
static const String _mpinTokenKey = "mpinToken";
|
||||
static const String _isMpinKey = "isMpin";
|
||||
static const String _fcmTokenKey = 'fcm_token';
|
||||
static const String _menuStorageKey = "dynamic_menus";
|
||||
|
||||
static SharedPreferences? _preferencesInstance;
|
||||
|
||||
@ -39,6 +41,30 @@ class LocalStorage {
|
||||
AuthService.isLoggedIn = preferences.getBool(_loggedInUserKey) ?? false;
|
||||
ThemeCustomizer.fromJSON(preferences.getString(_themeCustomizerKey));
|
||||
}
|
||||
/// ================== Sidebar Menu ==================
|
||||
static Future<bool> setMenus(List<MenuItem> menus) async {
|
||||
try {
|
||||
final jsonList = menus.map((e) => e.toJson()).toList();
|
||||
return preferences.setString(_menuStorageKey, jsonEncode(jsonList));
|
||||
} catch (e) {
|
||||
print("Error saving menus: $e");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static List<MenuItem> getMenus() {
|
||||
final storedJson = preferences.getString(_menuStorageKey);
|
||||
if (storedJson == null) return [];
|
||||
try {
|
||||
return (jsonDecode(storedJson) as List)
|
||||
.map((e) => MenuItem.fromJson(e as Map<String, dynamic>))
|
||||
.toList();
|
||||
} catch (e) {
|
||||
print("Error loading menus: $e");
|
||||
return [];
|
||||
}
|
||||
}
|
||||
static Future<bool> removeMenus() => preferences.remove(_menuStorageKey);
|
||||
|
||||
/// ================== User Permissions ==================
|
||||
static Future<bool> setUserPermissions(
|
||||
@ -59,8 +85,8 @@ class LocalStorage {
|
||||
preferences.remove(_userPermissionsKey);
|
||||
|
||||
/// ================== Employee Info ==================
|
||||
static Future<bool> setEmployeeInfo(EmployeeInfo employeeInfo) => preferences
|
||||
.setString(_employeeInfoKey, jsonEncode(employeeInfo.toJson()));
|
||||
static Future<bool> setEmployeeInfo(EmployeeInfo employeeInfo) =>
|
||||
preferences.setString(_employeeInfoKey, jsonEncode(employeeInfo.toJson()));
|
||||
|
||||
static EmployeeInfo? getEmployeeInfo() {
|
||||
final storedJson = preferences.getString(_employeeInfoKey);
|
||||
@ -92,7 +118,7 @@ class LocalStorage {
|
||||
print("Logout API error: $e");
|
||||
}
|
||||
|
||||
// ===== Local Cleanup =====
|
||||
/// ===== Local Cleanup =====
|
||||
await removeLoggedInUser();
|
||||
await removeToken(_jwtTokenKey);
|
||||
await removeToken(_refreshTokenKey);
|
||||
@ -100,6 +126,7 @@ class LocalStorage {
|
||||
await removeEmployeeInfo();
|
||||
await removeMpinToken();
|
||||
await removeIsMpin();
|
||||
await removeMenus(); // clear menus on logout
|
||||
await preferences.remove("mpin_verified");
|
||||
await preferences.remove(_languageKey);
|
||||
await preferences.remove(_themeCustomizerKey);
|
||||
|
@ -1,6 +1,6 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:marco/controller/task_planing/daily_task_planing_controller.dart';
|
||||
import 'package:marco/controller/task_Planning/daily_task_Planning_controller.dart';
|
||||
import 'package:marco/controller/project_controller.dart';
|
||||
import 'package:marco/helpers/widgets/my_spacing.dart';
|
||||
import 'package:marco/helpers/widgets/my_text.dart';
|
||||
@ -34,7 +34,7 @@ class AssignTaskBottomSheet extends StatefulWidget {
|
||||
}
|
||||
|
||||
class _AssignTaskBottomSheetState extends State<AssignTaskBottomSheet> {
|
||||
final DailyTaskPlaningController controller = Get.find();
|
||||
final DailyTaskPlanningController controller = Get.find();
|
||||
final ProjectController projectController = Get.find();
|
||||
final TextEditingController targetController = TextEditingController();
|
||||
final TextEditingController descriptionController = TextEditingController();
|
@ -4,7 +4,7 @@ import 'package:intl/intl.dart';
|
||||
import 'dart:io';
|
||||
import 'dart:math' as math;
|
||||
// --- Assumed Imports (ensure these paths are correct in your project) ---
|
||||
import 'package:marco/controller/task_planing/report_task_controller.dart';
|
||||
import 'package:marco/controller/task_planning/report_task_controller.dart';
|
||||
import 'package:marco/helpers/utils/mixins/ui_mixin.dart';
|
||||
import 'package:marco/helpers/widgets/my_button.dart';
|
||||
import 'package:marco/helpers/widgets/my_spacing.dart';
|
||||
@ -13,7 +13,7 @@ import 'package:marco/helpers/widgets/my_text_style.dart';
|
||||
import 'package:marco/helpers/widgets/avatar.dart';
|
||||
import 'package:marco/helpers/widgets/my_team_model_sheet.dart';
|
||||
import 'package:marco/helpers/widgets/image_viewer_dialog.dart';
|
||||
import 'package:marco/model/dailyTaskPlaning/create_task_botom_sheet.dart';
|
||||
import 'package:marco/model/dailyTaskPlanning/create_task_botom_sheet.dart';
|
||||
import 'package:marco/helpers/utils/base_bottom_sheet.dart';
|
||||
|
||||
// --- Form Field Keys (Unchanged) ---
|
@ -1,6 +1,6 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:marco/controller/task_planing/add_task_controller.dart';
|
||||
import 'package:marco/controller/task_planning/add_task_controller.dart';
|
||||
import 'package:marco/helpers/widgets/my_text.dart';
|
||||
|
||||
import 'package:marco/helpers/widgets/my_snackbar.dart';
|
@ -1,13 +1,13 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:marco/controller/permission_controller.dart';
|
||||
import 'package:marco/controller/task_planing/daily_task_planing_controller.dart';
|
||||
import 'package:marco/controller/task_Planning/daily_task_Planning_controller.dart';
|
||||
import 'package:marco/helpers/widgets/my_text.dart';
|
||||
|
||||
class DailyTaskPlaningFilter extends StatelessWidget {
|
||||
final DailyTaskPlaningController controller;
|
||||
class DailyTaskPlanningFilter extends StatelessWidget {
|
||||
final DailyTaskPlanningController controller;
|
||||
final PermissionController permissionController;
|
||||
|
||||
const DailyTaskPlaningFilter({
|
||||
const DailyTaskPlanningFilter({
|
||||
super.key,
|
||||
required this.controller,
|
||||
required this.permissionController,
|
@ -1,6 +1,6 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:marco/controller/task_planing/report_task_action_controller.dart';
|
||||
import 'package:marco/controller/task_planning/report_task_action_controller.dart';
|
||||
import 'package:marco/helpers/utils/mixins/ui_mixin.dart';
|
||||
import 'package:marco/helpers/widgets/my_spacing.dart';
|
||||
import 'package:marco/helpers/widgets/my_text.dart';
|
||||
@ -8,8 +8,8 @@ import 'package:marco/helpers/widgets/my_text_style.dart';
|
||||
import 'package:marco/helpers/widgets/avatar.dart';
|
||||
import 'package:marco/helpers/widgets/my_team_model_sheet.dart';
|
||||
import 'package:marco/helpers/widgets/image_viewer_dialog.dart';
|
||||
import 'package:marco/model/dailyTaskPlaning/create_task_botom_sheet.dart';
|
||||
import 'package:marco/model/dailyTaskPlaning/report_action_widgets.dart';
|
||||
import 'package:marco/model/dailyTaskPlanning/create_task_botom_sheet.dart';
|
||||
import 'package:marco/model/dailyTaskPlanning/report_action_widgets.dart';
|
||||
import 'package:marco/helpers/utils/base_bottom_sheet.dart';
|
||||
|
||||
class ReportActionBottomSheet extends StatefulWidget {
|
@ -1,6 +1,6 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:marco/controller/task_planing/report_task_controller.dart';
|
||||
import 'package:marco/controller/task_planning/report_task_controller.dart';
|
||||
import 'package:marco/helpers/utils/mixins/ui_mixin.dart';
|
||||
import 'package:marco/helpers/widgets/my_button.dart';
|
||||
import 'package:marco/helpers/widgets/my_spacing.dart';
|
@ -1,8 +1,8 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
import 'package:marco/model/dailyTaskPlaning/comment_task_bottom_sheet.dart';
|
||||
import 'package:marco/model/dailyTaskPlaning/report_task_bottom_sheet.dart';
|
||||
import 'package:marco/model/dailyTaskPlaning/report_action_bottom_sheet.dart';
|
||||
import 'package:marco/model/dailyTaskPlanning/comment_task_bottom_sheet.dart';
|
||||
import 'package:marco/model/dailyTaskPlanning/report_task_bottom_sheet.dart';
|
||||
import 'package:marco/model/dailyTaskPlanning/report_action_bottom_sheet.dart';
|
||||
|
||||
class TaskActionButtons {
|
||||
static Widget reportButton({
|
@ -7,7 +7,7 @@ import 'package:marco/helpers/utils/base_bottom_sheet.dart';
|
||||
import 'package:marco/helpers/widgets/my_text.dart';
|
||||
import 'package:marco/helpers/widgets/my_spacing.dart';
|
||||
import 'package:marco/helpers/widgets/my_snackbar.dart';
|
||||
import 'package:marco/model/employee_model.dart';
|
||||
import 'package:marco/model/employees/employee_model.dart';
|
||||
import 'package:marco/model/directory/contact_bucket_list_model.dart';
|
||||
|
||||
class EditBucketBottomSheet {
|
||||
|
@ -1,39 +0,0 @@
|
||||
import 'dart:convert';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:marco/helpers/services/json_decoder.dart';
|
||||
import 'package:marco/model/identifier_model.dart';
|
||||
|
||||
class DragNDropModel extends IdentifierModel {
|
||||
final String name, image, userName, contactNumber;
|
||||
|
||||
DragNDropModel(super.id, this.name, this.image, this.userName, this.contactNumber);
|
||||
|
||||
static DragNDropModel fromJSON(Map<String, dynamic> json) {
|
||||
JSONDecoder decoder = JSONDecoder(json);
|
||||
|
||||
String name = decoder.getString('name');
|
||||
String image = decoder.getString('image');
|
||||
String userName = decoder.getString('user_name');
|
||||
String contactNumber = decoder.getString('contact_number');
|
||||
|
||||
return DragNDropModel(decoder.getId, name, image, userName, contactNumber);
|
||||
}
|
||||
|
||||
static List<DragNDropModel> listFromJSON(List<dynamic> list) {
|
||||
return list.map((e) => DragNDropModel.fromJSON(e)).toList();
|
||||
}
|
||||
|
||||
static List<DragNDropModel>? _dummyList;
|
||||
|
||||
static Future<List<DragNDropModel>> get dummyList async {
|
||||
if (_dummyList == null) {
|
||||
dynamic data = json.decode(await getData());
|
||||
_dummyList = listFromJSON(data);
|
||||
}
|
||||
return _dummyList!;
|
||||
}
|
||||
|
||||
static Future<String> getData() async {
|
||||
return await rootBundle.loadString('assets/data/drag_n_drop_data.json');
|
||||
}
|
||||
}
|
86
lib/model/dynamicMenu/dynamic_menu_model.dart
Normal file
86
lib/model/dynamicMenu/dynamic_menu_model.dart
Normal file
@ -0,0 +1,86 @@
|
||||
import 'dart:convert';
|
||||
|
||||
/// Top-level response for the menu API
|
||||
class MenuResponse {
|
||||
final bool success; // API call success flag
|
||||
final String message; // Response message
|
||||
final List<MenuItem> data; // List of menu items
|
||||
final dynamic errors; // Error details (if any)
|
||||
final int statusCode; // HTTP-like status code
|
||||
final DateTime timestamp; // Response timestamp
|
||||
|
||||
MenuResponse({
|
||||
required this.success,
|
||||
required this.message,
|
||||
required this.data,
|
||||
this.errors,
|
||||
required this.statusCode,
|
||||
required this.timestamp,
|
||||
});
|
||||
|
||||
/// Creates a MenuResponse from a JSON map
|
||||
factory MenuResponse.fromJson(Map<String, dynamic> json) {
|
||||
return MenuResponse(
|
||||
success: json['success'] as bool? ?? false,
|
||||
message: json['message'] as String? ?? '',
|
||||
data: (json['data'] as List<dynamic>?)
|
||||
?.map((e) => MenuItem.fromJson(e as Map<String, dynamic>))
|
||||
.toList() ??
|
||||
<MenuItem>[],
|
||||
errors: json['errors'],
|
||||
statusCode: json['statusCode'] as int? ?? 0,
|
||||
timestamp: DateTime.tryParse(json['timestamp'] as String? ?? '') ??
|
||||
DateTime.now(),
|
||||
);
|
||||
}
|
||||
|
||||
/// Converts the MenuResponse back to JSON map
|
||||
Map<String, dynamic> toJson() {
|
||||
return {
|
||||
'success': success,
|
||||
'message': message,
|
||||
'data': data.map((e) => e.toJson()).toList(),
|
||||
'errors': errors,
|
||||
'statusCode': statusCode,
|
||||
'timestamp': timestamp.toIso8601String(),
|
||||
};
|
||||
}
|
||||
|
||||
/// Parse from raw JSON string
|
||||
static MenuResponse fromRawJson(String str) =>
|
||||
MenuResponse.fromJson(json.decode(str) as Map<String, dynamic>);
|
||||
|
||||
/// Convert to raw JSON string
|
||||
String toRawJson() => json.encode(toJson());
|
||||
}
|
||||
|
||||
/// Individual menu item
|
||||
class MenuItem {
|
||||
final String id; // Unique item ID
|
||||
final String name; // Display text
|
||||
final bool available; // Availability flag
|
||||
|
||||
MenuItem({
|
||||
required this.id,
|
||||
required this.name,
|
||||
required this.available,
|
||||
});
|
||||
|
||||
/// Creates MenuItem from JSON map
|
||||
factory MenuItem.fromJson(Map<String, dynamic> json) {
|
||||
return MenuItem(
|
||||
id: json['id'] as String? ?? '',
|
||||
name: json['name'] as String? ?? '',
|
||||
available: json['available'] as bool? ?? false,
|
||||
);
|
||||
}
|
||||
|
||||
/// Converts MenuItem back to JSON
|
||||
Map<String, dynamic> toJson() {
|
||||
return {
|
||||
'id': id,
|
||||
'name': name,
|
||||
'available': available,
|
||||
};
|
||||
}
|
||||
}
|
@ -3,7 +3,7 @@ import 'package:get/get.dart';
|
||||
import 'package:marco/helpers/widgets/my_spacing.dart';
|
||||
import 'package:marco/helpers/widgets/my_text.dart';
|
||||
import 'package:marco/helpers/utils/base_bottom_sheet.dart';
|
||||
import 'package:marco/model/employee_model.dart';
|
||||
import 'package:marco/model/employees/employee_model.dart';
|
||||
|
||||
class ReusableEmployeeSelectorBottomSheet extends StatelessWidget {
|
||||
final TextEditingController searchController;
|
||||
|
@ -3,7 +3,7 @@ import 'package:get/get.dart';
|
||||
import 'package:marco/helpers/widgets/my_spacing.dart';
|
||||
import 'package:marco/helpers/widgets/my_text.dart';
|
||||
import 'package:marco/helpers/widgets/my_text_style.dart';
|
||||
import 'package:marco/model/employee_model.dart';
|
||||
import 'package:marco/model/employees/employee_model.dart';
|
||||
import 'package:marco/helpers/utils/base_bottom_sheet.dart';
|
||||
|
||||
class EmployeeSelectorBottomSheet extends StatefulWidget {
|
||||
|
@ -1,45 +0,0 @@
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:marco/helpers/services/json_decoder.dart';
|
||||
import 'package:marco/model/identifier_model.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
|
||||
class JobRecentApplicationModel extends IdentifierModel {
|
||||
final String candidate, category, designation, mail, location, type;
|
||||
final DateTime date;
|
||||
|
||||
JobRecentApplicationModel(super.id, this.candidate, this.category, this.designation, this.mail, this.location, this.type, this.date);
|
||||
|
||||
static JobRecentApplicationModel fromJSON(Map<String, dynamic> json) {
|
||||
JSONDecoder decoder = JSONDecoder(json);
|
||||
|
||||
String candidate = decoder.getString('candidate');
|
||||
String category = decoder.getString('category');
|
||||
String designation = decoder.getString('designation');
|
||||
String mail = decoder.getString('mail');
|
||||
String location = decoder.getString('location');
|
||||
String type = decoder.getString('type');
|
||||
DateTime date = decoder.getDateTime('date');
|
||||
|
||||
return JobRecentApplicationModel(decoder.getId, candidate, category, designation, mail, location, type, date);
|
||||
}
|
||||
|
||||
static List<JobRecentApplicationModel> listFromJSON(List<dynamic> list) {
|
||||
return list.map((e) => JobRecentApplicationModel.fromJSON(e)).toList();
|
||||
}
|
||||
|
||||
static List<JobRecentApplicationModel>? _dummyList;
|
||||
|
||||
static Future<List<JobRecentApplicationModel>> get dummyList async {
|
||||
if (_dummyList == null) {
|
||||
dynamic data = json.decode(await getData());
|
||||
_dummyList = listFromJSON(data);
|
||||
}
|
||||
|
||||
return _dummyList!;
|
||||
}
|
||||
|
||||
static Future<String> getData() async {
|
||||
return await rootBundle.loadString('assets/data/job_recent_application.json');
|
||||
}
|
||||
}
|
@ -1,47 +0,0 @@
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:marco/helpers/services/json_decoder.dart';
|
||||
import 'package:marco/model/identifier_model.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
|
||||
class LeadReportModel extends IdentifierModel {
|
||||
final String firstName, email, phoneNumber, companyName, status, location;
|
||||
final DateTime date;
|
||||
final int amount;
|
||||
|
||||
LeadReportModel(super.id, this.firstName, this.email, this.phoneNumber, this.companyName, this.status, this.location, this.date, this.amount);
|
||||
|
||||
static LeadReportModel fromJSON(Map<String, dynamic> json) {
|
||||
JSONDecoder decoder = JSONDecoder(json);
|
||||
|
||||
String firstName = decoder.getString('first_name');
|
||||
String email = decoder.getString('email');
|
||||
String phoneNumber = decoder.getString('phone_number');
|
||||
String companyName = decoder.getString('company_name');
|
||||
String status = decoder.getString('status');
|
||||
String location = decoder.getString('location');
|
||||
DateTime date = decoder.getDateTime('date');
|
||||
int amount = decoder.getInt('amount');
|
||||
|
||||
return LeadReportModel(decoder.getId, firstName, email, phoneNumber, companyName, status, location, date, amount);
|
||||
}
|
||||
|
||||
static List<LeadReportModel> listFromJSON(List<dynamic> list) {
|
||||
return list.map((e) => LeadReportModel.fromJSON(e)).toList();
|
||||
}
|
||||
|
||||
static List<LeadReportModel>? _dummyList;
|
||||
|
||||
static Future<List<LeadReportModel>> get dummyList async {
|
||||
if (_dummyList == null) {
|
||||
dynamic data = json.decode(await getData());
|
||||
_dummyList = listFromJSON(data);
|
||||
}
|
||||
|
||||
return _dummyList!;
|
||||
}
|
||||
|
||||
static Future<String> getData() async {
|
||||
return await rootBundle.loadString('assets/data/leads_report_data.json');
|
||||
}
|
||||
}
|
@ -1,53 +0,0 @@
|
||||
import 'dart:convert';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:marco/helpers/services/json_decoder.dart';
|
||||
import 'package:marco/model/identifier_model.dart';
|
||||
|
||||
class Product extends IdentifierModel {
|
||||
final String name, description, image, category, sku;
|
||||
final double price, rating;
|
||||
final int stock, ordersCount, ratingCount;
|
||||
final DateTime createdAt;
|
||||
|
||||
Product(super.id, this.name, this.description, this.image, this.category, this.sku, this.price, this.rating, this.stock, this.ordersCount, this.ratingCount,
|
||||
this.createdAt);
|
||||
|
||||
static Product fromJSON(Map<String, dynamic> json) {
|
||||
JSONDecoder decoder = JSONDecoder(json);
|
||||
|
||||
String name = decoder.getString('name');
|
||||
String description = decoder.getString('description');
|
||||
String image = decoder.getString('image');
|
||||
String category = decoder.getString('category');
|
||||
String sku = decoder.getString('sku');
|
||||
double price = decoder.getDouble('price');
|
||||
double rating = decoder.getDouble('rating');
|
||||
int stock = decoder.getInt('stock');
|
||||
int ordersCount = decoder.getInt('order_counts');
|
||||
int ratingCount = decoder.getInt('rating_count');
|
||||
DateTime createdAt = decoder.getDateTime('created_at');
|
||||
|
||||
return Product(decoder.getId, name, description, image, category, sku, price, rating, stock, ordersCount, ratingCount, createdAt);
|
||||
}
|
||||
|
||||
static List<Product> listFromJSON(List<dynamic> list) {
|
||||
return list.map((e) => Product.fromJSON(e)).toList();
|
||||
}
|
||||
|
||||
//Dummy
|
||||
|
||||
static List<Product>? _dummyList;
|
||||
|
||||
static Future<List<Product>> get dummyList async {
|
||||
if (_dummyList == null) {
|
||||
dynamic data = json.decode(await getData());
|
||||
_dummyList = listFromJSON(data);
|
||||
}
|
||||
|
||||
return _dummyList!.sublist(0, 10);
|
||||
}
|
||||
|
||||
static Future<String> getData() async {
|
||||
return await rootBundle.loadString('assets/data/product_data.json');
|
||||
}
|
||||
}
|
@ -1,47 +0,0 @@
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:marco/helpers/services/json_decoder.dart';
|
||||
import 'package:marco/model/identifier_model.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
|
||||
class ProductOrderModal extends IdentifierModel {
|
||||
final String orderId, customerName, location, payment, status;
|
||||
final int quantity, price;
|
||||
final DateTime orderDate;
|
||||
|
||||
ProductOrderModal(super.id, this.orderId, this.customerName, this.location, this.payment, this.status, this.quantity, this.price, this.orderDate);
|
||||
|
||||
static ProductOrderModal fromJSON(Map<String, dynamic> json) {
|
||||
JSONDecoder decoder = JSONDecoder(json);
|
||||
|
||||
String orderId = decoder.getString('order_id');
|
||||
String customerName = decoder.getString('customer_name');
|
||||
String location = decoder.getString('location');
|
||||
String payment = decoder.getString('payments');
|
||||
String status = decoder.getString('status');
|
||||
int quantity = decoder.getInt('quantity');
|
||||
int price = decoder.getInt('price');
|
||||
DateTime orderDate = decoder.getDateTime('order_date');
|
||||
|
||||
return ProductOrderModal(decoder.getId, orderId, customerName, location, payment, status, quantity, price, orderDate);
|
||||
}
|
||||
|
||||
static List<ProductOrderModal> listFromJSON(List<dynamic> list) {
|
||||
return list.map((e) => ProductOrderModal.fromJSON(e)).toList();
|
||||
}
|
||||
|
||||
static List<ProductOrderModal>? _dummyList;
|
||||
|
||||
static Future<List<ProductOrderModal>> get dummyList async {
|
||||
if (_dummyList == null) {
|
||||
dynamic data = json.decode(await getData());
|
||||
_dummyList = listFromJSON(data);
|
||||
}
|
||||
|
||||
return _dummyList!;
|
||||
}
|
||||
|
||||
static Future<String> getData() async {
|
||||
return await rootBundle.loadString('assets/data/product_order.json');
|
||||
}
|
||||
}
|
@ -1,50 +0,0 @@
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:marco/helpers/services/json_decoder.dart';
|
||||
import 'package:marco/model/identifier_model.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
|
||||
class ProjectSummaryModel extends IdentifierModel {
|
||||
final String title, assignTo, priority, status;
|
||||
final DateTime date;
|
||||
|
||||
ProjectSummaryModel(
|
||||
super.id,
|
||||
this.title,
|
||||
this.assignTo,
|
||||
this.priority,
|
||||
this.status,
|
||||
this.date,
|
||||
);
|
||||
|
||||
static ProjectSummaryModel fromJSON(Map<String, dynamic> json) {
|
||||
JSONDecoder decoder = JSONDecoder(json);
|
||||
|
||||
String title = decoder.getString('title');
|
||||
String assignTo = decoder.getString('assign_to');
|
||||
String priority = decoder.getString('priority');
|
||||
String status = decoder.getString('status');
|
||||
DateTime date = decoder.getDateTime('date');
|
||||
|
||||
return ProjectSummaryModel(decoder.getId, title, assignTo, priority, status, date);
|
||||
}
|
||||
|
||||
static List<ProjectSummaryModel> listFromJSON(List<dynamic> list) {
|
||||
return list.map((e) => ProjectSummaryModel.fromJSON(e)).toList();
|
||||
}
|
||||
|
||||
static List<ProjectSummaryModel>? _dummyList;
|
||||
|
||||
static Future<List<ProjectSummaryModel>> get dummyList async {
|
||||
if (_dummyList == null) {
|
||||
dynamic data = json.decode(await getData());
|
||||
_dummyList = listFromJSON(data);
|
||||
}
|
||||
|
||||
return _dummyList!;
|
||||
}
|
||||
|
||||
static Future<String> getData() async {
|
||||
return await rootBundle.loadString('assets/data/project_summary.json');
|
||||
}
|
||||
}
|
@ -1,53 +0,0 @@
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:marco/helpers/services/json_decoder.dart';
|
||||
import 'package:marco/model/identifier_model.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
|
||||
class RecentOrderModel extends IdentifierModel {
|
||||
final String productName, customer, status;
|
||||
final int quantity, price;
|
||||
final DateTime orderDate;
|
||||
|
||||
RecentOrderModel(
|
||||
super.id,
|
||||
this.productName,
|
||||
this.customer,
|
||||
this.status,
|
||||
this.quantity,
|
||||
this.price,
|
||||
this.orderDate,
|
||||
);
|
||||
|
||||
static RecentOrderModel fromJSON(Map<String, dynamic> json) {
|
||||
JSONDecoder decoder = JSONDecoder(json);
|
||||
|
||||
String productName = decoder.getString('product_name');
|
||||
String customer = decoder.getString('customer');
|
||||
String status = decoder.getString('status');
|
||||
int quantity = decoder.getInt('quantity');
|
||||
int price = decoder.getInt('price');
|
||||
DateTime orderDate = decoder.getDateTime('order_date');
|
||||
|
||||
return RecentOrderModel(decoder.getId, productName, customer, status, quantity, price, orderDate);
|
||||
}
|
||||
|
||||
static List<RecentOrderModel> listFromJSON(List<dynamic> list) {
|
||||
return list.map((e) => RecentOrderModel.fromJSON(e)).toList();
|
||||
}
|
||||
|
||||
static List<RecentOrderModel>? _dummyList;
|
||||
|
||||
static Future<List<RecentOrderModel>> get dummyList async {
|
||||
if (_dummyList == null) {
|
||||
dynamic data = json.decode(await getData());
|
||||
_dummyList = listFromJSON(data);
|
||||
}
|
||||
|
||||
return _dummyList!;
|
||||
}
|
||||
|
||||
static Future<String> getData() async {
|
||||
return await rootBundle.loadString('assets/data/recent_order.json');
|
||||
}
|
||||
}
|
@ -1,38 +0,0 @@
|
||||
import 'dart:convert';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:marco/helpers/services/json_decoder.dart';
|
||||
import 'package:marco/model/identifier_model.dart';
|
||||
|
||||
class TimeLineModel extends IdentifierModel {
|
||||
final String firstName, lastName, email;
|
||||
|
||||
TimeLineModel(super.id, this.firstName, this.lastName, this.email);
|
||||
|
||||
static TimeLineModel fromJSON(Map<String, dynamic> json) {
|
||||
JSONDecoder decoder = JSONDecoder(json);
|
||||
|
||||
String firstName = decoder.getString('first_name');
|
||||
String lastName = decoder.getString('last_name');
|
||||
String email = decoder.getString('email');
|
||||
|
||||
return TimeLineModel(decoder.getId, firstName, lastName, email);
|
||||
}
|
||||
|
||||
static List<TimeLineModel> listFromJSON(List<dynamic> list) {
|
||||
return list.map((e) => TimeLineModel.fromJSON(e)).toList();
|
||||
}
|
||||
|
||||
static List<TimeLineModel>? _dummyList;
|
||||
|
||||
static Future<List<TimeLineModel>> get dummyList async {
|
||||
if (_dummyList == null) {
|
||||
dynamic data = json.decode(await getData());
|
||||
_dummyList = listFromJSON(data);
|
||||
}
|
||||
return _dummyList!;
|
||||
}
|
||||
|
||||
static Future<String> getData() async {
|
||||
return await rootBundle.loadString('assets/data/time_line.json');
|
||||
}
|
||||
}
|
@ -1,10 +0,0 @@
|
||||
import 'package:marco/model/identifier_model.dart';
|
||||
|
||||
class User extends IdentifierModel {
|
||||
final String username, firstName, lastName;
|
||||
|
||||
User(super.id, this.username, this.firstName, this.lastName);
|
||||
|
||||
String get name => "$firstName $lastName";
|
||||
|
||||
}
|
@ -1,46 +0,0 @@
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:marco/helpers/services/json_decoder.dart';
|
||||
import 'package:marco/model/identifier_model.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
|
||||
class VisitorByChannelsModel extends IdentifierModel {
|
||||
final String channel;
|
||||
final int session, targetReached;
|
||||
final double bounceRate, pagePerSession;
|
||||
final DateTime sessionDuration;
|
||||
|
||||
VisitorByChannelsModel(super.id, this.channel, this.session, this.targetReached, this.bounceRate, this.pagePerSession, this.sessionDuration);
|
||||
|
||||
static VisitorByChannelsModel fromJSON(Map<String, dynamic> json) {
|
||||
JSONDecoder decoder = JSONDecoder(json);
|
||||
|
||||
String channel = decoder.getString('channel');
|
||||
int session = decoder.getInt('session');
|
||||
int targetReached = decoder.getInt('target_reached');
|
||||
double bounceRate = decoder.getDouble('bounce_rate');
|
||||
double pagePerSession = decoder.getDouble('page_per_session');
|
||||
DateTime sessionDuration = decoder.getDateTime('session_duration');
|
||||
|
||||
return VisitorByChannelsModel(decoder.getId, channel, session, targetReached, bounceRate, pagePerSession, sessionDuration);
|
||||
}
|
||||
|
||||
static List<VisitorByChannelsModel> listFromJSON(List<dynamic> list) {
|
||||
return list.map((e) => VisitorByChannelsModel.fromJSON(e)).toList();
|
||||
}
|
||||
|
||||
static List<VisitorByChannelsModel>? _dummyList;
|
||||
|
||||
static Future<List<VisitorByChannelsModel>> get dummyList async {
|
||||
if (_dummyList == null) {
|
||||
dynamic data = json.decode(await getData());
|
||||
_dummyList = listFromJSON(data);
|
||||
}
|
||||
|
||||
return _dummyList!;
|
||||
}
|
||||
|
||||
static Future<String> getData() async {
|
||||
return await rootBundle.loadString('assets/data/visitors_by_channels_data.json');
|
||||
}
|
||||
}
|
@ -10,8 +10,8 @@ import 'package:marco/view/error_pages/error_404_screen.dart';
|
||||
import 'package:marco/view/error_pages/error_500_screen.dart';
|
||||
import 'package:marco/view/dashboard/dashboard_screen.dart';
|
||||
import 'package:marco/view/dashboard/Attendence/attendance_screen.dart';
|
||||
import 'package:marco/view/taskPlaning/daily_task_planing.dart';
|
||||
import 'package:marco/view/taskPlaning/daily_progress.dart';
|
||||
import 'package:marco/view/taskPlanning/daily_task_planning.dart';
|
||||
import 'package:marco/view/taskPlanning/daily_progress.dart';
|
||||
import 'package:marco/view/employees/employees_screen.dart';
|
||||
import 'package:marco/view/auth/login_option_screen.dart';
|
||||
import 'package:marco/view/auth/mpin_screen.dart';
|
||||
@ -55,8 +55,8 @@ getPageRoute() {
|
||||
middlewares: [AuthMiddleware()]),
|
||||
// Daily Task Planning
|
||||
GetPage(
|
||||
name: '/dashboard/daily-task-planing',
|
||||
page: () => DailyTaskPlaningScreen(),
|
||||
name: '/dashboard/daily-task-Planning',
|
||||
page: () => DailyTaskPlanningScreen(),
|
||||
middlewares: [AuthMiddleware()]),
|
||||
GetPage(
|
||||
name: '/dashboard/daily-task-progress',
|
||||
|
@ -11,6 +11,8 @@ import 'package:marco/helpers/widgets/my_spacing.dart';
|
||||
import 'package:marco/helpers/widgets/my_text.dart';
|
||||
import 'package:marco/view/dashboard/dashboard_chart.dart';
|
||||
import 'package:marco/view/layouts/layout.dart';
|
||||
import 'package:marco/controller/dynamicMenu/dynamic_menu_controller.dart';
|
||||
|
||||
// import 'package:marco/helpers/services/firebase/firebase_messaging_service.dart'; // ❌ Commented out
|
||||
|
||||
class DashboardScreen extends StatefulWidget {
|
||||
@ -21,7 +23,7 @@ class DashboardScreen extends StatefulWidget {
|
||||
static const String projectsRoute = "/dashboard";
|
||||
static const String attendanceRoute = "/dashboard/attendance";
|
||||
static const String tasksRoute = "/dashboard/daily-task";
|
||||
static const String dailyTasksRoute = "/dashboard/daily-task-planing";
|
||||
static const String dailyTasksRoute = "/dashboard/daily-task-Planning";
|
||||
static const String dailyTasksProgressRoute =
|
||||
"/dashboard/daily-task-progress";
|
||||
static const String directoryMainPageRoute = "/dashboard/directory-main-page";
|
||||
@ -33,7 +35,10 @@ class DashboardScreen extends StatefulWidget {
|
||||
|
||||
class _DashboardScreenState extends State<DashboardScreen> with UIMixin {
|
||||
final DashboardController dashboardController =
|
||||
Get.put(DashboardController());
|
||||
Get.put(DashboardController(), permanent: true);
|
||||
final DynamicMenuController menuController =
|
||||
Get.put(DynamicMenuController(), permanent: true);
|
||||
|
||||
bool hasMpin = true;
|
||||
|
||||
@override
|
||||
@ -78,77 +83,106 @@ class _DashboardScreenState extends State<DashboardScreen> with UIMixin {
|
||||
);
|
||||
}
|
||||
|
||||
/// Dashboard Statistics Section with ProjectController
|
||||
/// Dashboard Statistics Section with ProjectController, Obx reactivity for menus
|
||||
Widget _buildDashboardStats(BuildContext context) {
|
||||
final stats = [
|
||||
_StatItem(LucideIcons.scan_face, "Attendance", contentTheme.success,
|
||||
DashboardScreen.attendanceRoute),
|
||||
_StatItem(LucideIcons.users, "Employees", contentTheme.warning,
|
||||
DashboardScreen.employeesRoute),
|
||||
_StatItem(LucideIcons.logs, "Daily Task Planing", contentTheme.info,
|
||||
DashboardScreen.dailyTasksRoute),
|
||||
_StatItem(LucideIcons.list_todo, "Daily Task Progress", contentTheme.info,
|
||||
DashboardScreen.dailyTasksProgressRoute),
|
||||
_StatItem(LucideIcons.folder, "Directory", contentTheme.info,
|
||||
DashboardScreen.directoryMainPageRoute),
|
||||
_StatItem(LucideIcons.badge_dollar_sign, "Expense", contentTheme.info,
|
||||
DashboardScreen.expenseMainPageRoute),
|
||||
];
|
||||
|
||||
return GetBuilder<ProjectController>(
|
||||
id: 'dashboard_controller',
|
||||
builder: (controller) {
|
||||
if (controller.isLoading.value) {
|
||||
return _buildLoadingSkeleton(context);
|
||||
}
|
||||
|
||||
final isProjectSelected = controller.selectedProject != null;
|
||||
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
if (!isProjectSelected) _buildNoProjectMessage(),
|
||||
LayoutBuilder(
|
||||
builder: (context, constraints) {
|
||||
final maxWidth = constraints.maxWidth;
|
||||
final crossAxisCount = (maxWidth / 100).floor().clamp(2, 4);
|
||||
final cardWidth =
|
||||
(maxWidth - (crossAxisCount - 1) * 10) / crossAxisCount;
|
||||
|
||||
return Wrap(
|
||||
spacing: 10,
|
||||
runSpacing: 10,
|
||||
children: stats
|
||||
.map((stat) =>
|
||||
_buildStatCard(stat, cardWidth, isProjectSelected))
|
||||
.toList(),
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
/// Attendance Chart Section
|
||||
Widget _buildAttendanceChartSection() {
|
||||
return GetBuilder<ProjectController>(
|
||||
id: 'dashboard_controller',
|
||||
builder: (projectController) {
|
||||
final isProjectSelected = projectController.selectedProject != null;
|
||||
return Opacity(
|
||||
opacity: isProjectSelected ? 1.0 : 0.4,
|
||||
child: IgnorePointer(
|
||||
ignoring: !isProjectSelected,
|
||||
child: ClipRRect(
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
child: AttendanceDashboardChart(),
|
||||
return Obx(() {
|
||||
if (menuController.isLoading.value) {
|
||||
return _buildLoadingSkeleton(context);
|
||||
}
|
||||
if (menuController.hasError.value) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.all(16),
|
||||
child: Center(
|
||||
child: MyText.bodySmall(
|
||||
menuController.errorMessage.value,
|
||||
color: Colors.red,
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
final stats = [
|
||||
_StatItem(LucideIcons.scan_face, "Attendance", contentTheme.success,
|
||||
DashboardScreen.attendanceRoute),
|
||||
_StatItem(LucideIcons.users, "Employees", contentTheme.warning,
|
||||
DashboardScreen.employeesRoute),
|
||||
_StatItem(LucideIcons.logs, "Daily Task Planning", contentTheme.info,
|
||||
DashboardScreen.dailyTasksRoute),
|
||||
_StatItem(LucideIcons.list_todo, "Daily Progress Report",
|
||||
contentTheme.info, DashboardScreen.dailyTasksProgressRoute),
|
||||
_StatItem(LucideIcons.folder, "Directory", contentTheme.info,
|
||||
DashboardScreen.directoryMainPageRoute),
|
||||
_StatItem(LucideIcons.badge_dollar_sign, "Expense", contentTheme.info,
|
||||
DashboardScreen.expenseMainPageRoute),
|
||||
];
|
||||
|
||||
final projectController = Get.find<ProjectController>();
|
||||
final isProjectSelected = projectController.selectedProject != null;
|
||||
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
if (!isProjectSelected) _buildNoProjectMessage(),
|
||||
LayoutBuilder(
|
||||
builder: (context, constraints) {
|
||||
final maxWidth = constraints.maxWidth;
|
||||
final crossAxisCount = (maxWidth / 100).floor().clamp(2, 4);
|
||||
final cardWidth =
|
||||
(maxWidth - (crossAxisCount - 1) * 10) / crossAxisCount;
|
||||
|
||||
return Wrap(
|
||||
spacing: 10,
|
||||
runSpacing: 10,
|
||||
children: stats
|
||||
.where((stat) {
|
||||
final isAllowed =
|
||||
menuController.isMenuAllowed(stat.title);
|
||||
|
||||
// 👇 Log what is being checked
|
||||
debugPrint(
|
||||
"[Dashboard Menu] Checking menu: ${stat.title} -> Allowed: $isAllowed");
|
||||
|
||||
return isAllowed;
|
||||
})
|
||||
.map((stat) =>
|
||||
_buildStatCard(stat, cardWidth, isProjectSelected))
|
||||
.toList(),
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
/// Attendance Chart Section
|
||||
/// Attendance Chart Section
|
||||
Widget _buildAttendanceChartSection() {
|
||||
return Obx(() {
|
||||
final isAttendanceAllowed = menuController.isMenuAllowed("Attendance");
|
||||
|
||||
if (!isAttendanceAllowed) {
|
||||
// 🚫 Don't render anything if attendance menu is not allowed
|
||||
return const SizedBox.shrink();
|
||||
}
|
||||
|
||||
return GetBuilder<ProjectController>(
|
||||
id: 'dashboard_controller',
|
||||
builder: (projectController) {
|
||||
final isProjectSelected = projectController.selectedProject != null;
|
||||
return Opacity(
|
||||
opacity: isProjectSelected ? 1.0 : 0.4,
|
||||
child: IgnorePointer(
|
||||
ignoring: !isProjectSelected,
|
||||
child: ClipRRect(
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
child: AttendanceDashboardChart(),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
/// No Project Assigned Message
|
||||
|
@ -10,7 +10,7 @@ import 'package:marco/helpers/widgets/avatar.dart';
|
||||
import 'package:marco/controller/project_controller.dart';
|
||||
import 'package:marco/helpers/widgets/my_custom_skeleton.dart';
|
||||
import 'package:marco/view/employees/employee_detail_screen.dart';
|
||||
import 'package:marco/model/employee_model.dart';
|
||||
import 'package:marco/model/employees/employee_model.dart';
|
||||
import 'package:marco/helpers/utils/launcher_utils.dart';
|
||||
import 'package:marco/view/employees/assign_employee_bottom_sheet.dart';
|
||||
import 'package:marco/controller/permission_controller.dart';
|
||||
|
@ -19,7 +19,7 @@ import 'package:marco/helpers/widgets/expense_detail_helpers.dart';
|
||||
import 'package:marco/helpers/widgets/my_spacing.dart';
|
||||
import 'package:marco/helpers/widgets/my_text.dart';
|
||||
import 'package:marco/helpers/services/storage/local_storage.dart';
|
||||
import 'package:marco/model/employee_info.dart';
|
||||
import 'package:marco/model/employees/employee_info.dart';
|
||||
import 'package:timeline_tile/timeline_tile.dart';
|
||||
class ExpenseDetailScreen extends StatefulWidget {
|
||||
final String expenseId;
|
||||
|
@ -6,7 +6,7 @@ import 'package:marco/helpers/utils/date_time_utils.dart';
|
||||
import 'package:marco/helpers/widgets/my_spacing.dart';
|
||||
import 'package:marco/helpers/widgets/my_text.dart';
|
||||
import 'package:marco/helpers/widgets/my_text_style.dart';
|
||||
import 'package:marco/model/employee_model.dart';
|
||||
import 'package:marco/model/employees/employee_model.dart';
|
||||
import 'package:marco/model/expense/employee_selector_for_filter_bottom_sheet.dart';
|
||||
|
||||
class ExpenseFilterBottomSheet extends StatelessWidget {
|
||||
|
@ -4,7 +4,7 @@ import 'package:marco/controller/layout/layout_controller.dart';
|
||||
import 'package:marco/helpers/widgets/my_responsive.dart';
|
||||
import 'package:marco/helpers/widgets/my_text.dart';
|
||||
import 'package:marco/helpers/services/storage/local_storage.dart';
|
||||
import 'package:marco/model/employee_info.dart';
|
||||
import 'package:marco/model/employees/employee_info.dart';
|
||||
import 'package:marco/helpers/services/api_endpoints.dart';
|
||||
import 'package:marco/images.dart';
|
||||
import 'package:marco/controller/project_controller.dart';
|
||||
|
@ -12,7 +12,7 @@ import 'package:flutter/material.dart';
|
||||
import 'package:get/route_manager.dart';
|
||||
import 'package:flutter_lucide/flutter_lucide.dart';
|
||||
import 'package:marco/helpers/services/storage/local_storage.dart';
|
||||
import 'package:marco/model/employee_info.dart';
|
||||
import 'package:marco/model/employees/employee_info.dart';
|
||||
import 'package:marco/helpers/widgets/avatar.dart';
|
||||
|
||||
typedef LeftbarMenuFunction = void Function(String key);
|
||||
@ -124,9 +124,9 @@ class _LeftBarState extends State<LeftBar>
|
||||
route: '/dashboard/employees'),
|
||||
NavigationItem(
|
||||
iconData: LucideIcons.logs,
|
||||
title: "Daily Task Planing",
|
||||
title: "Daily Task Planning",
|
||||
isCondensed: isCondensed,
|
||||
route: '/dashboard/daily-task-planing'),
|
||||
route: '/dashboard/daily-task-Planning'),
|
||||
NavigationItem(
|
||||
iconData: LucideIcons.list_todo,
|
||||
title: "Daily Progress Report",
|
||||
|
@ -13,7 +13,7 @@ import 'package:marco/widgets/custom_pop_menu.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_lucide/flutter_lucide.dart';
|
||||
import 'package:marco/helpers/services/storage/local_storage.dart';
|
||||
import 'package:marco/model/employee_info.dart';
|
||||
import 'package:marco/model/employees/employee_info.dart';
|
||||
import 'package:marco/helpers/widgets/avatar.dart';
|
||||
|
||||
class TopBar extends StatefulWidget {
|
||||
|
@ -7,7 +7,7 @@ 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:flutter_lucide/flutter_lucide.dart';
|
||||
import 'package:marco/model/employee_info.dart';
|
||||
import 'package:marco/model/employees/employee_info.dart';
|
||||
import 'package:marco/helpers/widgets/avatar.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:marco/controller/auth/mpin_controller.dart';
|
||||
|
@ -10,10 +10,10 @@ import 'package:marco/helpers/widgets/my_spacing.dart';
|
||||
import 'package:marco/helpers/widgets/my_text.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';
|
||||
import 'package:marco/model/dailyTaskPlanning/daily_progress_report_filter.dart';
|
||||
import 'package:marco/helpers/widgets/avatar.dart';
|
||||
import 'package:marco/controller/project_controller.dart';
|
||||
import 'package:marco/model/dailyTaskPlaning/task_action_buttons.dart';
|
||||
import 'package:marco/model/dailyTaskPlanning/task_action_buttons.dart';
|
||||
import 'package:marco/helpers/widgets/my_custom_skeleton.dart';
|
||||
import 'package:marco/helpers/utils/permission_constants.dart';
|
||||
import 'package:marco/helpers/widgets/my_refresh_indicator.dart';
|
||||
@ -92,7 +92,7 @@ class _DailyProgressReportScreenState extends State<DailyProgressReportScreen>
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
MyText.titleLarge(
|
||||
'Daily Task Progress',
|
||||
'Daily Progress Report',
|
||||
fontWeight: 700,
|
||||
color: Colors.black,
|
||||
),
|
@ -6,25 +6,25 @@ 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/controller/permission_controller.dart';
|
||||
import 'package:marco/controller/task_planing/daily_task_planing_controller.dart';
|
||||
import 'package:marco/controller/task_Planning/daily_task_Planning_controller.dart';
|
||||
import 'package:marco/controller/project_controller.dart';
|
||||
import 'package:percent_indicator/percent_indicator.dart';
|
||||
import 'package:marco/model/dailyTaskPlaning/assign_task_bottom_sheet .dart';
|
||||
import 'package:marco/model/dailyTaskPlanning/assign_task_bottom_sheet .dart';
|
||||
import 'package:marco/helpers/widgets/my_custom_skeleton.dart';
|
||||
import 'package:marco/helpers/utils/permission_constants.dart';
|
||||
import 'package:marco/helpers/widgets/my_refresh_indicator.dart';
|
||||
|
||||
class DailyTaskPlaningScreen extends StatefulWidget {
|
||||
DailyTaskPlaningScreen({super.key});
|
||||
class DailyTaskPlanningScreen extends StatefulWidget {
|
||||
DailyTaskPlanningScreen({super.key});
|
||||
|
||||
@override
|
||||
State<DailyTaskPlaningScreen> createState() => _DailyTaskPlaningScreenState();
|
||||
State<DailyTaskPlanningScreen> createState() => _DailyTaskPlanningScreenState();
|
||||
}
|
||||
|
||||
class _DailyTaskPlaningScreenState extends State<DailyTaskPlaningScreen>
|
||||
class _DailyTaskPlanningScreenState extends State<DailyTaskPlanningScreen>
|
||||
with UIMixin {
|
||||
final DailyTaskPlaningController dailyTaskPlaningController =
|
||||
Get.put(DailyTaskPlaningController());
|
||||
final DailyTaskPlanningController dailyTaskPlanningController =
|
||||
Get.put(DailyTaskPlanningController());
|
||||
final PermissionController permissionController =
|
||||
Get.put(PermissionController());
|
||||
final ProjectController projectController = Get.find<ProjectController>();
|
||||
@ -36,7 +36,7 @@ class _DailyTaskPlaningScreenState extends State<DailyTaskPlaningScreen>
|
||||
// Initial fetch if a project is already selected
|
||||
final projectId = projectController.selectedProjectId.value;
|
||||
if (projectId.isNotEmpty) {
|
||||
dailyTaskPlaningController.fetchTaskData(projectId);
|
||||
dailyTaskPlanningController.fetchTaskData(projectId);
|
||||
}
|
||||
|
||||
// Reactive fetch on project ID change
|
||||
@ -44,7 +44,7 @@ class _DailyTaskPlaningScreenState extends State<DailyTaskPlaningScreen>
|
||||
projectController.selectedProjectId,
|
||||
(newProjectId) {
|
||||
if (newProjectId.isNotEmpty) {
|
||||
dailyTaskPlaningController.fetchTaskData(newProjectId);
|
||||
dailyTaskPlanningController.fetchTaskData(newProjectId);
|
||||
}
|
||||
},
|
||||
);
|
||||
@ -77,7 +77,7 @@ class _DailyTaskPlaningScreenState extends State<DailyTaskPlaningScreen>
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
MyText.titleLarge(
|
||||
'Daily Task Planing',
|
||||
'Daily Task Planning',
|
||||
fontWeight: 700,
|
||||
color: Colors.black,
|
||||
),
|
||||
@ -118,7 +118,7 @@ class _DailyTaskPlaningScreenState extends State<DailyTaskPlaningScreen>
|
||||
final projectId = projectController.selectedProjectId.value;
|
||||
if (projectId.isNotEmpty) {
|
||||
try {
|
||||
await dailyTaskPlaningController.fetchTaskData(projectId);
|
||||
await dailyTaskPlanningController.fetchTaskData(projectId);
|
||||
} catch (e) {
|
||||
debugPrint('Error refreshing task data: ${e.toString()}');
|
||||
}
|
||||
@ -135,9 +135,9 @@ class _DailyTaskPlaningScreenState extends State<DailyTaskPlaningScreen>
|
||||
kToolbarHeight -
|
||||
MediaQuery.of(context).padding.top,
|
||||
),
|
||||
child: GetBuilder<DailyTaskPlaningController>(
|
||||
init: dailyTaskPlaningController,
|
||||
tag: 'daily_task_planing_controller',
|
||||
child: GetBuilder<DailyTaskPlanningController>(
|
||||
init: dailyTaskPlanningController,
|
||||
tag: 'daily_task_Planning_controller',
|
||||
builder: (controller) {
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
@ -160,8 +160,8 @@ class _DailyTaskPlaningScreenState extends State<DailyTaskPlaningScreen>
|
||||
|
||||
Widget dailyProgressReportTab() {
|
||||
return Obx(() {
|
||||
final isLoading = dailyTaskPlaningController.isLoading.value;
|
||||
final dailyTasks = dailyTaskPlaningController.dailyTasks;
|
||||
final isLoading = dailyTaskPlanningController.isLoading.value;
|
||||
final dailyTasks = dailyTaskPlanningController.dailyTasks;
|
||||
|
||||
if (isLoading) {
|
||||
return SkeletonLoaders.dailyProgressPlanningSkeletonCollapsedOnly();
|
Loading…
x
Reference in New Issue
Block a user