diff --git a/lib/helpers/services/api_service.dart b/lib/helpers/services/api_service.dart index b23a487..41c966b 100644 --- a/lib/helpers/services/api_service.dart +++ b/lib/helpers/services/api_service.dart @@ -6,7 +6,7 @@ import 'package:intl/intl.dart'; class ApiService { - static const String baseUrl = "https://api.marcoaiot.com/api"; + static const String baseUrl = "https://stageapi.marcoaiot.com/api"; // ===== Common Helpers ===== @@ -105,21 +105,24 @@ class ApiService { // ===== Upload Image ===== - static Future uploadAttendanceImage( - int id, - int employeeId, - XFile imageFile, - double latitude, - double longitude, { - required String imageName, - required int projectId, - String comment = "", - required int action, // action passed here - }) async { - final token = await _getToken(); - if (token == null) return false; + static Future uploadAttendanceImage( + int id, + int employeeId, + XFile? imageFile, + double latitude, + double longitude, { + required String imageName, + required int projectId, + String comment = "", + required int action, + bool imageCapture = true, // <- add this flag +}) async { + final token = await _getToken(); + if (token == null) return false; - try { + try { + Map? imageObject; + if (imageCapture && imageFile != null) { final bytes = await imageFile.readAsBytes(); final base64Image = base64Encode(bytes); final fileSize = await imageFile.length(); @@ -132,42 +135,47 @@ class ApiService { "description": "Employee attendance photo", "base64Data": '$base64Image', }; - - final now = DateTime.now(); - - final body = { - "id": id, - "employeeId": employeeId, - "projectId": projectId, - "markTime": DateFormat('hh:mm a').format(now), - "comment": comment, - "action": action, - "date": DateFormat('yyyy-MM-dd').format(now), - "latitude": '$latitude', - "longitude": '$longitude', - "image": imageObject - }; - print("Attendance Image Upload Body: $body"); - final response = await http.post( - Uri.parse("$baseUrl/attendance/record-image"), - headers: _headers(token), - body: jsonEncode(body), - ); - print("Attendance Image Upload Response: ${response.body}"); - final json = jsonDecode(response.body); - if (response.statusCode == 200 && json['success'] == true) { - return true; - } else { - print("Failed to upload image. API Error: ${json['message']}"); - } - } catch (e) { - print("Exception during image upload: $e"); } - return false; + final now = DateTime.now(); + + final body = { + "id": id, + "employeeId": employeeId, + "projectId": projectId, + "markTime": DateFormat('hh:mm a').format(now), + "comment": comment, + "action": action, + "date": DateFormat('yyyy-MM-dd').format(now), + "latitude": '$latitude', + "longitude": '$longitude', + }; + + if (imageObject != null) { + body["image"] = imageObject; + } + + print("Attendance Image Upload Body: $body"); + final response = await http.post( + Uri.parse("$baseUrl/attendance/record-image"), + headers: _headers(token), + body: jsonEncode(body), + ); + print("Attendance Image Upload Response: ${response.body}"); + final json = jsonDecode(response.body); + if (response.statusCode == 200 && json['success'] == true) { + return true; + } else { + print("Failed to upload image. API Error: ${json['message']}"); + } + } catch (e) { + print("Exception during image upload: $e"); } - // ===== Utilities ===== + return false; +} + + // ===== Utilities ===== static String generateImageName(int employeeId, int count) { final now = DateTime.now(); diff --git a/lib/helpers/services/auth_service.dart b/lib/helpers/services/auth_service.dart index c9ef4d5..fde383e 100644 --- a/lib/helpers/services/auth_service.dart +++ b/lib/helpers/services/auth_service.dart @@ -8,7 +8,7 @@ class AuthService { static Future?> loginUser(Map data) async { try { final response = await http.post( - Uri.parse('https://api.marcoaiot.com/api/auth/login'), + Uri.parse('https://stageapi.marcoaiot.com/api/auth/login'), headers: {'Content-Type': 'application/json'}, body: jsonEncode(data), ); diff --git a/lib/helpers/services/permission_service.dart b/lib/helpers/services/permission_service.dart index d4182d2..cb37e3f 100644 --- a/lib/helpers/services/permission_service.dart +++ b/lib/helpers/services/permission_service.dart @@ -6,7 +6,7 @@ class PermissionService { static Future> fetchPermissions(String token) async { try { final response = await http.get( - Uri.parse('https://api.marcoaiot.com/api/user/profile'), + Uri.parse('https://stageapi.marcoaiot.com/api/user/profile'), headers: {'Authorization': 'Bearer $token'}, ); diff --git a/lib/main.dart b/lib/main.dart index 46ee421..e4df27f 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -40,7 +40,7 @@ class MyApp extends StatelessWidget { darkTheme: AppTheme.darkTheme, themeMode: ThemeCustomizer.instance.theme, navigatorKey: NavigationService.navigatorKey, - initialRoute: "/dashboard/attendance", + initialRoute: "/dashboard", getPages: getPageRoute(), builder: (context, child) { NavigationService.registerContext(context); diff --git a/lib/routes.dart b/lib/routes.dart index 147f97f..f28f214 100644 --- a/lib/routes.dart +++ b/lib/routes.dart @@ -11,6 +11,7 @@ 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/attendance_screen.dart'; import 'package:marco/view/dashboard/attendanceScreen.dart'; +import 'package:marco/view/dashboard/dashboard_screen.dart'; class AuthMiddleware extends GetMiddleware { @override RouteSettings? redirect(String? route) { @@ -24,7 +25,7 @@ getPageRoute() { // Dashboard GetPage(name: '/dashboard/attendance', page: () => AttendanceScreen(), middlewares: [AuthMiddleware()]), - + GetPage(name: '/dashboard', page: () => DashboardScreen(), middlewares: [AuthMiddleware()]), // Authentication GetPage(name: '/auth/login', page: () => LoginScreen()), GetPage(name: '/auth/register_account', page: () => const RegisterAccountScreen()), diff --git a/lib/view/dashboard/dashboard_screen.dart b/lib/view/dashboard/dashboard_screen.dart new file mode 100644 index 0000000..5f26dce --- /dev/null +++ b/lib/view/dashboard/dashboard_screen.dart @@ -0,0 +1,155 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_lucide/flutter_lucide.dart'; +import 'package:get/get.dart'; +import 'package:marco/helpers/theme/app_theme.dart'; +import 'package:marco/helpers/utils/mixins/ui_mixin.dart'; +import 'package:marco/helpers/utils/my_shadow.dart'; +import 'package:marco/helpers/widgets/my_breadcrumb.dart'; +import 'package:marco/helpers/widgets/my_breadcrumb_item.dart'; +import 'package:marco/helpers/widgets/my_card.dart'; +import 'package:marco/helpers/widgets/my_container.dart'; +import 'package:marco/helpers/widgets/my_spacing.dart'; +import 'package:marco/helpers/widgets/my_text.dart'; +import 'package:marco/view/layouts/layout.dart'; +class DashboardScreen extends StatelessWidget with UIMixin { + DashboardScreen({super.key}); + + @override + Widget build(BuildContext context) { + return Layout( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Padding( + padding: MySpacing.x(flexSpacing), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + MyText.titleMedium("Dashboard", fontSize: 18, fontWeight: 600), + MyBreadcrumb(children: [MyBreadcrumbItem(name: 'Dashboard')]), + ], + ), + ), + MySpacing.height(flexSpacing), + Padding( + padding: MySpacing.x(flexSpacing / 2), + child: Column( + children: _dashboardStats().map((rowStats) { + return Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: rowStats, + ); + }).toList(), + ), + ), + ], + ), + ); + } + + List> _dashboardStats() { + final stats = [ + { + "icon": LucideIcons.layout_dashboard, + "title": "Dashboard", + "route": "/dashboard/attendance", + "color": contentTheme.primary + }, + { + "icon": LucideIcons.folder, + "title": "Projects", + "route": "/dashboard/attendance", + "color": contentTheme.secondary + }, + { + "icon": LucideIcons.check, + "title": "Attendence", + "route": "/dashboard/attendance", + "color": contentTheme.success + }, + { + "icon": LucideIcons.users, + "title": "Task", + "route": "/dashboard/attendance", + "color": contentTheme.info + }, + ]; + + // Group the stats into pairs for rows + List> rows = []; + for (int i = 0; i < stats.length; i += 2) { + rows.add([ + _buildStatCard( + icon: stats[i]['icon'] as IconData, + title: stats[i]['title'] as String, + route: stats[i]['route'] as String, + color: stats[i]['color'] as Color, + ), + if (i + 1 < stats.length) _buildStatCard( + icon: stats[i + 1]['icon'] as IconData, + title: stats[i + 1]['title'] as String, + route: stats[i + 1]['route'] as String, + color: stats[i + 1]['color'] as Color, + ), + ]); + } + + return rows; + } + + Widget _buildStatCard({ + required IconData icon, + required String title, + required String route, + required Color color, + String count = "", + String change = "", + }) { + return Expanded( + child: InkWell( + onTap: () => Get.toNamed(route), + child: MyCard.bordered( + borderRadiusAll: 10, + border: Border.all(color: Colors.grey.withOpacity(0.2)), + shadow: MyShadow(elevation: 1, position: MyShadowPosition.bottom), + paddingAll: 24, + height: 140, + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + MyContainer( + paddingAll: 16, + color: color.withOpacity(0.2), + child: MyContainer( + paddingAll: 8, + color: color, + child: Icon(icon, size: 16, color: contentTheme.light), + ), + ), + MySpacing.height(12), + MyText.labelSmall(title, maxLines: 1), + if (count.isNotEmpty) + MyText.bodyMedium(count, fontWeight: 600, maxLines: 1), + if (change.isNotEmpty) + Padding( + padding: MySpacing.top(8), + child: MyContainer( + padding: MySpacing.xy(6, 4), + color: change.startsWith('+') + ? Colors.green.withOpacity(0.2) + : theme.colorScheme.error.withOpacity(0.2), + child: MyText.labelSmall( + change, + color: change.startsWith('+') + ? Colors.green + : theme.colorScheme.error, + ), + ), + ), + ], + ), + ), + ), + ); + } +} diff --git a/lib/view/layouts/left_bar.dart b/lib/view/layouts/left_bar.dart index 7d34318..110ec72 100644 --- a/lib/view/layouts/left_bar.dart +++ b/lib/view/layouts/left_bar.dart @@ -91,7 +91,7 @@ class _LeftBarState extends State with SingleTickerProviderStateMixin, children: [ Divider(), labelWidget("Dashboard"), - + NavigationItem(iconData: LucideIcons.layout_dashboard, title: "Dashboard", isCondensed: isCondensed, route: '/dashboard'), NavigationItem(iconData: LucideIcons.layout_template, title: "Attendance", isCondensed: isCondensed, route: '/dashboard/attendance'), ], ),