From 1279b0e00fd9bc7071869249905521f945c2e48f Mon Sep 17 00:00:00 2001 From: Vaibhav Surve Date: Wed, 10 Dec 2025 14:54:27 +0530 Subject: [PATCH] updated packages --- README.md | 2 +- android/app/build.gradle | 2 +- android/app/src/main/AndroidManifest.xml | 2 +- .../gradle/wrapper/gradle-wrapper.properties | 2 +- android/settings.gradle | 2 +- build_release.sh | 2 +- ios/Runner/Info.plist | 2 +- lib/app_constant.dart | 2 +- .../tenant/tenant_selection_controller.dart | 2 +- lib/helpers/services/auth_service.dart | 91 ++++++++++-------- lib/helpers/theme/app_theme.dart | 92 +++++++++++++------ lib/view/dashboard/dashboard_screen.dart | 79 ++++++++-------- lib/view/layouts/layout.dart | 88 +++++++++--------- pubspec.yaml | 23 ++--- 14 files changed, 219 insertions(+), 172 deletions(-) diff --git a/README.md b/README.md index 071a587..f50d993 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# On Field Work +# OnFieldWork.com A new Flutter project. diff --git a/android/app/build.gradle b/android/app/build.gradle index 23b8b74..5242a55 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -39,7 +39,7 @@ android { // Specify your unique Application ID. This identifies your app on Google Play. applicationId = "com.marcoonfieldwork.aiot" // Set minimum and target SDK versions based on Flutter's configuration - minSdk = 23 + minSdkVersion = flutter.minSdkVersion targetSdk = flutter.targetSdkVersion // Set version code and name based on Flutter's configuration (from pubspec.yaml) versionCode = flutter.versionCode diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index 4a7cbdf..1ef4b05 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -8,7 +8,7 @@ CFBundleDevelopmentRegion $(DEVELOPMENT_LANGUAGE) CFBundleDisplayName - On Field Work + OnFieldWork.com CFBundleExecutable $(EXECUTABLE_NAME) CFBundleIdentifier diff --git a/lib/app_constant.dart b/lib/app_constant.dart index 55c95b9..0d360da 100644 --- a/lib/app_constant.dart +++ b/lib/app_constant.dart @@ -8,5 +8,5 @@ class AppConstant { static int iOSAppVersion = 1; static String version = "1.0.0"; - static String get appName => 'On Field Work'; + static String get appName => 'OnFieldWork.com'; } diff --git a/lib/controller/tenant/tenant_selection_controller.dart b/lib/controller/tenant/tenant_selection_controller.dart index 3dd88a3..3ac8f84 100644 --- a/lib/controller/tenant/tenant_selection_controller.dart +++ b/lib/controller/tenant/tenant_selection_controller.dart @@ -7,7 +7,6 @@ import 'package:on_field_work/helpers/services/storage/local_storage.dart'; import 'package:on_field_work/controller/permission_controller.dart'; class TenantSelectionController extends GetxController { - // Tenant list final tenants = [].obs; @@ -35,6 +34,7 @@ class TenantSelectionController extends GetxController { if (data == null || data.isEmpty) { tenants.clear(); logSafe("⚠️ No tenants found for the user.", level: LogLevel.warning); + await LocalStorage.logout(); return; } diff --git a/lib/helpers/services/auth_service.dart b/lib/helpers/services/auth_service.dart index 36777b9..2378032 100644 --- a/lib/helpers/services/auth_service.dart +++ b/lib/helpers/services/auth_service.dart @@ -124,7 +124,7 @@ class AuthService { method: _HttpMethod.post, body: body, ); - + if (data != null && data['success'] == true && data['data'] != null) { await LocalStorage.setJwtToken(data['data']['token']); await LocalStorage.setRefreshToken(data['data']['refreshToken']); @@ -181,9 +181,10 @@ class AuthService { _wrapErrorHandling( () async { final employeeInfo = await LocalStorage.getEmployeeInfo(); - if (employeeInfo == null) return null; // Fails immediately if info is missing + if (employeeInfo == null) + return null; // Fails immediately if info is missing final token = await LocalStorage.getJwtToken(); - + final responseData = await _networkRequest( path: "/auth/login-mpin", method: _HttpMethod.post, @@ -196,7 +197,7 @@ class AuthService { authToken: token, ); - // Handle token updates from MPIN login success if necessary, + // Handle token updates from MPIN login success if necessary, // though typically refresh or a separate login handles this. if (responseData?['data'] != null) { await _handleLoginSuccess(responseData!['data']); @@ -299,11 +300,10 @@ class AuthService { final refreshed = await refreshToken(); if (refreshed) return getTenants(hasRetried: true); } - + // Fallback on all other failures if (data != null && data['statusCode'] != 401) { - _handleApiError( - data['statusCode'], data, "Fetching tenants"); + _handleApiError(data['statusCode'], data, "Fetching tenants"); } else if (data?['statusCode'] == 401 && hasRetried) { await _handleUnauthorized(); } @@ -354,7 +354,7 @@ class AuthService { if (refreshed) return selectTenant(tenantId, hasRetried: true); await _handleUnauthorized(); } - + // Fallback on all other failures if (data != null) { _handleApiError(data['statusCode'], data, "Selecting tenant"); @@ -385,33 +385,37 @@ class AuthService { authToken: token, ); - if (data != null && data['success'] == true && data['data'] is Map) { - final responseData = data['data'] as Map; - - final result = { - 'permissions': _parsePermissions(responseData['featurePermissions']), - 'employeeInfo': await _parseEmployeeInfo(responseData['employeeInfo']), - 'projects': _parseProjectsInfo(responseData['projects']), - }; + if (data != null && + data['success'] == true && + data['data'] is Map) { + final responseData = data['data'] as Map; - _userDataCache[token] = result; - logSafe("User data fetched and decrypted successfully."); - return result; + final result = { + 'permissions': _parsePermissions(responseData['featurePermissions']), + 'employeeInfo': await _parseEmployeeInfo(responseData['employeeInfo']), + 'projects': _parseProjectsInfo(responseData['projects']), + }; + + _userDataCache[token] = result; + logSafe("User data fetched and decrypted successfully."); + return result; } - + // Handle 401 Unauthorized via refreshToken/retry logic if (data?['statusCode'] == 401 && !hasRetried) { - final refreshed = await refreshToken(); - final newToken = await LocalStorage.getJwtToken(); - if (refreshed && newToken != null) { - return fetchAllUserData(newToken, hasRetried: true); - } + final refreshed = await refreshToken(); + final newToken = await LocalStorage.getJwtToken(); + if (refreshed && newToken != null) { + return fetchAllUserData(newToken, hasRetried: true); + } } // Handle failure and unauthorized - if (data?['statusCode'] == 401 || data?['statusCode'] == 403 || data == null) { - await _handleUnauthorized(); - throw Exception('Unauthorized or Network Error. Token refresh failed.'); + if (data?['statusCode'] == 401 || + data?['statusCode'] == 403 || + data == null) { + await _handleUnauthorized(); + throw Exception('Unauthorized or Network Error. Token refresh failed.'); } final errorMsg = data['message'] ?? 'Unknown error'; @@ -469,7 +473,6 @@ class AuthService { logSafe("❌ $context failed: $message [Status: $statusCode]", level: level); } - /// General network request handler for both GET and POST. static Future?> _networkRequest({ required String path, @@ -490,8 +493,10 @@ class AuthService { level: LogLevel.info); if (method == _HttpMethod.post) { - response = await http.post(uri, headers: headers, body: jsonEncode(body)); - } else { // GET + response = + await http.post(uri, headers: headers, body: jsonEncode(body)); + } else { + // GET response = await http.get(uri, headers: headers); } @@ -501,26 +506,34 @@ class AuthService { if (response.statusCode == 401) { await _handleUnauthorized(); } - return {"statusCode": response.statusCode, "success": false, "message": "Empty response body"}; + return { + "statusCode": response.statusCode, + "success": false, + "message": "Empty response body" + }; } final decrypted = decryptResponse(response.body); if (decrypted == null) { - logSafe("❌ Response decryption failed for $path", level: LogLevel.error); - return {"statusCode": response.statusCode, "success": false, "message": "Failed to decrypt response"}; + logSafe("❌ Response decryption failed for $path", + level: LogLevel.error); + return { + "statusCode": response.statusCode, + "success": false, + "message": "Failed to decrypt response" + }; } - final Map result = decrypted is Map - ? decrypted + final Map result = decrypted is Map + ? decrypted : {"data": decrypted}; // Wrap non-map responses logSafe( "⬅️ Response: ${jsonEncode(result)} [Status: ${response.statusCode}]", level: LogLevel.info); - - return {"statusCode": response.statusCode, ...result}; + return {"statusCode": response.statusCode, ...result}; } catch (e, st) { _handleError("$path ${method.name.toUpperCase()} error", e, st); return null; @@ -561,4 +574,4 @@ class AuthService { } isLoggedIn = true; } -} \ No newline at end of file +} diff --git a/lib/helpers/theme/app_theme.dart b/lib/helpers/theme/app_theme.dart index f994415..7f30e78 100644 --- a/lib/helpers/theme/app_theme.dart +++ b/lib/helpers/theme/app_theme.dart @@ -25,7 +25,8 @@ int get flexColumns => MyScreenMedia.flexColumns; class MaterialRadius { double xs, small, medium, large; - MaterialRadius({this.xs = 2, this.small = 4, this.medium = 6, this.large = 8}); + MaterialRadius( + {this.xs = 2, this.small = 4, this.medium = 6, this.large = 8}); } class ColorGroup { @@ -41,10 +42,12 @@ class AppTheme { static Color primaryColor = Color(0xff663399); static ThemeData getThemeFromThemeMode() { - return ThemeCustomizer.instance.theme == ThemeMode.light ? lightTheme : darkTheme; + return ThemeCustomizer.instance.theme == ThemeMode.light + ? lightTheme + : darkTheme; } - /// -------------------------- Light Theme -------------------------------------------- /// + /// -------------------------- Light Theme  -------------------------------------------- /// static final ThemeData lightTheme = ThemeData( /// Brightness @@ -60,14 +63,18 @@ class AppTheme { /// AppBar Theme appBarTheme: AppBarTheme( - backgroundColor: Color(0xffF5F5F5), iconTheme: IconThemeData(color: Color(0xff495057)), actionsIconTheme: IconThemeData(color: Color(0xff495057))), + backgroundColor: Color(0xffF5F5F5), + iconTheme: IconThemeData(color: Color(0xff495057)), + actionsIconTheme: IconThemeData(color: Color(0xff495057))), /// Card Theme - cardTheme: CardTheme(color: Color(0xffffffff)), + // FIX: Use CardThemeData + cardTheme: CardThemeData(color: Color(0xffffffff)), cardColor: Color(0xffffffff), /// Colorscheme - colorScheme: ColorScheme.fromSeed(seedColor: Color(0xff663399), brightness: Brightness.light), + colorScheme: ColorScheme.fromSeed( + seedColor: Color(0xff663399), brightness: Brightness.light), snackBarTheme: SnackBarThemeData(actionTextColor: Colors.white), @@ -86,10 +93,12 @@ class AppTheme { dividerColor: Color(0xffdddddd), /// Bottom AppBar Theme - bottomAppBarTheme: BottomAppBarTheme(color: Color(0xffeeeeee), elevation: 2), + // FIX: Use BottomAppBarThemeData + bottomAppBarTheme: + BottomAppBarThemeData(color: Color(0xffeeeeee), elevation: 2), /// Tab bar Theme - tabBarTheme: TabBarTheme( + tabBarTheme: TabBarThemeData( unselectedLabelColor: Color(0xff495057), labelColor: AppTheme.primaryColor, indicatorSize: TabBarIndicatorSize.label, @@ -123,8 +132,11 @@ class AppTheme { checkColor: WidgetStateProperty.all(Color(0xffffffff)), fillColor: WidgetStateProperty.all(AppTheme.primaryColor), ), - switchTheme: - SwitchThemeData(thumbColor: WidgetStateProperty.resolveWith((states) => states.contains(WidgetState.selected) ? AppTheme.primaryColor : Colors.white)), + switchTheme: SwitchThemeData( + thumbColor: WidgetStateProperty.resolveWith((states) => + states.contains(WidgetState.selected) + ? AppTheme.primaryColor + : Colors.white)), /// Other Colors splashColor: Colors.white.withAlpha(100), @@ -132,8 +144,9 @@ class AppTheme { highlightColor: Color(0xffeeeeee), ); - /// -------------------------- Dark Theme -------------------------------------------- /// - static final ThemeData darkTheme = ThemeData.dark(useMaterial3: false).copyWith( + /// -------------------------- Dark Theme  -------------------------------------------- /// + static final ThemeData darkTheme = + ThemeData.dark(useMaterial3: false).copyWith( /// Brightness /// Scaffold and Background color @@ -146,7 +159,8 @@ class AppTheme { appBarTheme: AppBarTheme(backgroundColor: Color(0xff262729)), /// Card Theme - cardTheme: CardTheme(color: Color(0xff1b1b1c)), + // FIX: Use CardThemeData + cardTheme: CardThemeData(color: Color(0xff1b1b1c)), cardColor: Color(0xff1b1b1c), /// Colorscheme @@ -175,10 +189,13 @@ class AppTheme { foregroundColor: Colors.white), /// Bottom AppBar Theme - bottomAppBarTheme: BottomAppBarTheme(color: Color(0xff464c52), elevation: 2), + // FIX: Use BottomAppBarThemeData + bottomAppBarTheme: + BottomAppBarThemeData(color: Color(0xff464c52), elevation: 2), /// Tab bar Theme - tabBarTheme: TabBarTheme( + // FIX: Use TabBarThemeData + tabBarTheme: TabBarThemeData( unselectedLabelColor: Color(0xff495057), labelColor: AppTheme.primaryColor, indicatorSize: TabBarIndicatorSize.label, @@ -230,7 +247,8 @@ class AppStyle { containerRadius: AppStyle.containerRadius.medium, cardRadius: AppStyle.cardRadius.medium, buttonRadius: AppStyle.buttonRadius.medium, - defaultBreadCrumbItem: MyBreadcrumbItem(name: 'On Field Work', route: '/client/dashboard'), + defaultBreadCrumbItem: + MyBreadcrumbItem(name: 'OnFieldWork.com', route: '/client/dashboard'), )); bool isMobile = true; try { @@ -241,12 +259,16 @@ class AppStyle { My.setFlexSpacing(isMobile ? 16 : 24); } - /// -------------------------- Styles -------------------------------------------- /// + /// -------------------------- Styles  -------------------------------------------- /// - static MaterialRadius buttonRadius = MaterialRadius(small: 2, medium: 4, large: 8); - static MaterialRadius cardRadius = MaterialRadius(xs: 2, small: 4, medium: 4, large: 8); - static MaterialRadius containerRadius = MaterialRadius(xs: 2, small: 4, medium: 4, large: 8); - static MaterialRadius imageRadius = MaterialRadius(xs: 2, small: 4, medium: 4, large: 8); + static MaterialRadius buttonRadius = + MaterialRadius(small: 2, medium: 4, large: 8); + static MaterialRadius cardRadius = + MaterialRadius(xs: 2, small: 4, medium: 4, large: 8); + static MaterialRadius containerRadius = + MaterialRadius(xs: 2, small: 4, medium: 4, large: 8); + static MaterialRadius imageRadius = + MaterialRadius(xs: 2, small: 4, medium: 4, large: 8); } class AppColors { @@ -262,13 +284,16 @@ class AppColors { static ColorGroup orange = ColorGroup(Color(0xffFFCEC2), Color(0xffFF3B0A)); static ColorGroup skyBlue = ColorGroup(Color(0xffC2F0FF), Color(0xff0099CC)); static ColorGroup lavender = ColorGroup(Color(0xffEAE2F3), Color(0xff7748AD)); - static ColorGroup queenPink = ColorGroup(Color(0xffE8D9DC), Color(0xff804D57)); - static ColorGroup blueViolet = ColorGroup(Color(0xffC5C6E7), Color(0xff3B3E91)); + static ColorGroup queenPink = + ColorGroup(Color(0xffE8D9DC), Color(0xff804D57)); + static ColorGroup blueViolet = + ColorGroup(Color(0xffC5C6E7), Color(0xff3B3E91)); static ColorGroup rosePink = ColorGroup(Color(0xffFCB1E0), Color(0xffEC0999)); static ColorGroup rubinRed = ColorGroup(Color(0x98f6a8bd), Color(0xffd03760)); static ColorGroup favorite = rubinRed; - static ColorGroup redOrange = ColorGroup(Color(0xffFFAD99), Color(0xffF53100)); + static ColorGroup redOrange = + ColorGroup(Color(0xffFFAD99), Color(0xffF53100)); static Color notificationSuccessBGColor = Color(0xff117E68); static Color notificationSuccessTextColor = Color(0xffffffff); @@ -278,7 +303,16 @@ class AppColors { static Color notificationErrorTextColor = Color(0xffFF3B0A); static Color notificationErrorActionColor = Color(0xff006784); - static List list = [redOrange, violet, blue, green, orange, skyBlue, lavender, blueViolet]; + static List list = [ + redOrange, + violet, + blue, + green, + orange, + skyBlue, + lavender, + blueViolet + ]; static ColorGroup get random => list[Random().nextInt(list.length)]; @@ -287,7 +321,13 @@ class AppColors { } static Color getColorByRating(int rating) { - var colors = {1: Color(0xfff0323c), 2: Color(0xcdf0323c), 3: star, 4: Color(0xcd3cd278), 5: Color(0xff3cd278)}; + var colors = { + 1: Color(0xfff0323c), + 2: Color(0xcdf0323c), + 3: star, + 4: Color(0xcd3cd278), + 5: Color(0xff3cd278) + }; return colors[rating] ?? colors[1]!; } diff --git a/lib/view/dashboard/dashboard_screen.dart b/lib/view/dashboard/dashboard_screen.dart index 4af888c..b14af24 100644 --- a/lib/view/dashboard/dashboard_screen.dart +++ b/lib/view/dashboard/dashboard_screen.dart @@ -11,8 +11,8 @@ import 'package:on_field_work/helpers/utils/permission_constants.dart'; import 'package:on_field_work/helpers/widgets/avatar.dart'; import 'package:on_field_work/helpers/widgets/dashbaord/expense_breakdown_chart.dart'; import 'package:on_field_work/helpers/widgets/dashbaord/expense_by_status_widget.dart'; -import 'package:on_field_work/helpers/widgets/dashbaord/collection_dashboard_card.dart'; -import 'package:on_field_work/helpers/widgets/dashbaord/purchase_invoice_dashboard.dart'; +// import 'package:on_field_work/helpers/widgets/dashbaord/collection_dashboard_card.dart'; // Unused +// import 'package:on_field_work/helpers/widgets/dashbaord/purchase_invoice_dashboard.dart'; // Unused import 'package:on_field_work/helpers/widgets/dashbaord/monthly_expense_dashboard_chart.dart'; import 'package:on_field_work/helpers/widgets/my_custom_skeleton.dart'; import 'package:on_field_work/helpers/widgets/my_spacing.dart'; @@ -80,7 +80,6 @@ class _DashboardScreenState extends State with UIMixin { Widget _sectionTitle(String title) { return Padding( padding: const EdgeInsets.only(left: 4, bottom: 8), - // OPTIMIZATION: Use MyText for consistent styling child: MyText.titleMedium( title, fontWeight: 700, @@ -127,12 +126,13 @@ class _DashboardScreenState extends State with UIMixin { children: [ Row( children: [ - Icon(Icons.info_outline, size: 30, color: Colors.white), + const Icon(Icons.info_outline, + size: 30, color: Colors.white), MySpacing.width(10), - Expanded( + const Expanded( child: Text( "No attendance data available yet.", - style: const TextStyle( + style: TextStyle( color: Colors.white, fontSize: 13, fontWeight: FontWeight.w500, @@ -142,9 +142,9 @@ class _DashboardScreenState extends State with UIMixin { ], ), MySpacing.height(12), - Text( + const Text( "You are not added to this project or attendance data is not available.", - style: const TextStyle(color: Colors.white70, fontSize: 13), + style: TextStyle(color: Colors.white70, fontSize: 13), ), ], ), @@ -215,7 +215,7 @@ class _DashboardScreenState extends State with UIMixin { ), ], ), - MySpacing.height(12), // OPTIMIZED + MySpacing.height(12), Text( infoText, style: const TextStyle( @@ -223,7 +223,7 @@ class _DashboardScreenState extends State with UIMixin { fontSize: 13, ), ), - MySpacing.height(12), // OPTIMIZED + MySpacing.height(12), Row( mainAxisAlignment: MainAxisAlignment.end, children: [ @@ -262,7 +262,6 @@ class _DashboardScreenState extends State with UIMixin { final bool projectSelected = projectController.selectedProject != null; - // These are String constants from permission_constants.dart (kept outside of Obx) const List cardOrder = [ MenuItems.attendance, MenuItems.employees, @@ -273,7 +272,6 @@ class _DashboardScreenState extends State with UIMixin { MenuItems.infraProjects, ]; - // OPTIMIZATION: Using a static map for meta data final Map meta = { MenuItems.attendance: _DashboardCardMeta(LucideIcons.scan_face, contentTheme.success), @@ -291,7 +289,6 @@ class _DashboardScreenState extends State with UIMixin { _DashboardCardMeta(LucideIcons.building_2, contentTheme.primary), }; - // OPTIMIZATION: Use map for faster lookup, then filter the preferred order final Map allowed = { for (final m in menuController.menuItems) if (m.available && meta.containsKey(m.id)) m.id: m, @@ -308,8 +305,7 @@ class _DashboardScreenState extends State with UIMixin { child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - _sectionTitle( - 'Modules'), // OPTIMIZATION: Reused section title helper + _sectionTitle('Modules'), if (!projectSelected) Container( padding: const EdgeInsets.symmetric( @@ -334,7 +330,7 @@ class _DashboardScreenState extends State with UIMixin { ), GridView.builder( shrinkWrap: true, - physics: const NeverScrollableScrollPhysics(), + physics: const NeverScrollableScrollPhysics(), // Important! padding: const EdgeInsets.symmetric(vertical: 2, horizontal: 2), gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount( crossAxisCount: 3, @@ -394,7 +390,7 @@ class _DashboardScreenState extends State with UIMixin { color: isEnabled ? cardMeta.color : Colors.grey.shade300, ), - MySpacing.height(6), // OPTIMIZED + MySpacing.height(6), Padding( padding: const EdgeInsets.symmetric(horizontal: 2), child: Text( @@ -521,7 +517,6 @@ class _DashboardScreenState extends State with UIMixin { child: Column( children: [ const TextField( - // OPTIMIZED: Added const decoration: InputDecoration( hintText: 'Search project...', isDense: true, @@ -557,8 +552,9 @@ class _DashboardScreenState extends State with UIMixin { ), ); } + // --------------------------------------------------------------------------- - // Build + // Build (MODIFIED FOR FIXED HEADER) // --------------------------------------------------------------------------- @override @@ -566,34 +562,39 @@ class _DashboardScreenState extends State with UIMixin { return Scaffold( backgroundColor: const Color(0xfff5f6fa), body: Layout( - child: SingleChildScrollView( + child: Padding( padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 0), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ _projectSelector(), MySpacing.height(20), - _quickActions(), - MySpacing.height(20), - _dashboardModules(), - MySpacing.height(20), - _sectionTitle('Reports & Analytics'), - const CompactPurchaseInvoiceDashboard(), - MySpacing.height(20), - CollectionsHealthWidget(), - MySpacing.height(20), - _cardWrapper( - child: ExpenseTypeReportChart(), - ), - _cardWrapper( - child: ExpenseByStatusWidget( - controller: dashboardController, + Expanded( + child: SingleChildScrollView( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + _quickActions(), + MySpacing.height(20), + _dashboardModules(), + MySpacing.height(20), + _sectionTitle('Reports & Analytics'), + _cardWrapper( + child: ExpenseTypeReportChart(), + ), + _cardWrapper( + child: ExpenseByStatusWidget( + controller: dashboardController, + ), + ), + _cardWrapper( + child: MonthlyExpenseDashboardChart(), + ), + MySpacing.height(20), + ], + ), ), ), - _cardWrapper( - child: MonthlyExpenseDashboardChart(), - ), - MySpacing.height(20), ], ), ), diff --git a/lib/view/layouts/layout.dart b/lib/view/layouts/layout.dart index a3f3039..6479002 100644 --- a/lib/view/layouts/layout.dart +++ b/lib/view/layouts/layout.dart @@ -24,7 +24,6 @@ class Layout extends StatefulWidget { class _LayoutState extends State with UIMixin { final LayoutController controller = LayoutController(); final EmployeeInfo? employeeInfo = LocalStorage.getEmployeeInfo(); - final bool isBetaEnvironment = ApiEndpoints.baseUrl.contains("stage"); bool hasMpin = true; @@ -46,72 +45,69 @@ class _LayoutState extends State with UIMixin { @override Widget build(BuildContext context) { return MyResponsive(builder: (context, _, screenMT) { - return GetBuilder( + return GetBuilder( init: controller, builder: (_) { - return (screenMT.isMobile || screenMT.isTablet) - ? _buildScaffold(context, isMobile: true) - : _buildScaffold(context); + return _buildScaffold(context); }, ); }); } - Widget _buildScaffold(BuildContext context, {bool isMobile = false}) { + Widget _buildScaffold(BuildContext context) { final primaryColor = contentTheme.primary; return Scaffold( - key: controller.scaffoldKey, - endDrawer: const UserProfileBar(), - floatingActionButton: widget.floatingActionButton, - body: Column( - children: [ - // Solid primary background area - Container( + key: controller.scaffoldKey, + endDrawer: const UserProfileBar(), + floatingActionButton: widget.floatingActionButton, + body: Column( + children: [ + Container( + width: double.infinity, + color: primaryColor, + child: _buildHeaderContent(), + ), + Expanded( + child: Container( width: double.infinity, - color: primaryColor, - child: _buildHeaderContent(isMobile), - ), - Expanded( - child: Container( - width: double.infinity, - decoration: BoxDecoration( - gradient: LinearGradient( - begin: Alignment.topCenter, - end: Alignment.bottomCenter, - colors: [ - primaryColor, - primaryColor.withOpacity(0.7), - primaryColor.withOpacity(0.0), - ], - stops: const [0.0, 0.1, 0.3], - ), + decoration: BoxDecoration( + gradient: LinearGradient( + begin: Alignment.topCenter, + end: Alignment.bottomCenter, + colors: [ + primaryColor, + primaryColor.withOpacity(0.7), + primaryColor.withOpacity(0.0), + ], + stops: const [0.0, 0.1, 0.3], ), - child: SafeArea( - top: false, - child: GestureDetector( - behavior: HitTestBehavior.translucent, - onTap: () {}, - child: SingleChildScrollView( - key: controller.scrollKey, - padding: EdgeInsets.zero, - child: widget.child, - ), - ), + ), + child: SafeArea( + top: false, + child: GestureDetector( + behavior: HitTestBehavior.translucent, + onTap: () => + FocusScope.of(context).unfocus(), + child: widget.child ?? + const SizedBox.shrink(), ), ), ), - ], - )); + ), + ], + ), + ); } - Widget _buildHeaderContent(bool isMobile) { + Widget _buildHeaderContent() { final selectedTenant = AuthService.currentTenant; + final bool isBeta = ApiEndpoints.baseUrl.contains("stage"); return Padding( padding: const EdgeInsets.fromLTRB(10, 45, 10, 0), child: Container( - margin: const EdgeInsets.only(bottom: 18), + margin: const EdgeInsets.only(bottom: 10), width: double.infinity, padding: const EdgeInsets.all(12), decoration: BoxDecoration( @@ -139,7 +135,7 @@ class _LayoutState extends State with UIMixin { ), // Beta badge - if (ApiEndpoints.baseUrl.contains("stage")) + if (isBeta) Positioned( bottom: 0, left: 0, diff --git a/pubspec.yaml b/pubspec.yaml index 637a4ef..1d4a4cc 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -46,15 +46,15 @@ dependencies: carousel_slider: ^5.0.0 reorderable_grid: ^1.0.10 loading_animation_widget: ^1.3.0 - intl: ^0.19.0 - syncfusion_flutter_core: ^29.1.40 - syncfusion_flutter_sliders: ^29.1.40 + intl: ^0.20.2 + syncfusion_flutter_core: ^31.2.18 + syncfusion_flutter_sliders: ^31.2.18 file_picker: ^10.3.2 timelines_plus: ^1.0.4 - syncfusion_flutter_charts: ^29.1.40 + syncfusion_flutter_charts: ^31.2.18 appflowy_board: ^0.1.2 - syncfusion_flutter_calendar: ^29.1.40 - syncfusion_flutter_maps: ^29.1.40 + syncfusion_flutter_calendar: ^31.2.18 + syncfusion_flutter_maps: ^31.2.18 http: ^1.6.0 geolocator: ^14.0.2 permission_handler: ^12.0.1 @@ -71,13 +71,13 @@ dependencies: font_awesome_flutter: ^10.8.0 flutter_html: ^3.0.0 tab_indicator_styler: ^2.0.0 - connectivity_plus: ^6.1.4 + connectivity_plus: ^7.0.0 geocoding: ^4.0.0 firebase_core: ^4.0.0 firebase_messaging: ^16.0.0 googleapis_auth: ^2.0.0 - device_info_plus: ^11.3.0 - flutter_local_notifications: 19.4.0 + device_info_plus: ^12.3.0 + flutter_local_notifications: ^19.5.0 equatable: ^2.0.7 mime: ^2.0.0 timeago: ^3.7.1 @@ -97,7 +97,7 @@ dev_dependencies: # activated in the `analysis_options.yaml` file located at the root of your # package. See that file for information about deactivating specific lint # rules and activating additional ones. - flutter_lints: ^5.0.0 + flutter_lints: ^6.0.0 # For information on the generic Dart part of this file, see the # following page: https://dart.dev/tools/pub/pubspec @@ -150,6 +150,3 @@ flutter: # # For details regarding fonts from package dependencies, # see https://flutter.dev/to/font-from-package - -dependency_overrides: - http: ^1.6.0