From 77fb952f00b5546fae9526a0e3b5b11a21c67b71 Mon Sep 17 00:00:00 2001 From: faris Aljohari <83524184+farisaljohari@users.noreply.github.com> Date: Tue, 3 Sep 2024 22:05:19 +0300 Subject: [PATCH 1/9] remove last changes --- ...ic-web-apps-zealous-mushroom-0d31a3303.yml | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/.github/workflows/azure-static-web-apps-zealous-mushroom-0d31a3303.yml b/.github/workflows/azure-static-web-apps-zealous-mushroom-0d31a3303.yml index 95a05014..b8d576b9 100644 --- a/.github/workflows/azure-static-web-apps-zealous-mushroom-0d31a3303.yml +++ b/.github/workflows/azure-static-web-apps-zealous-mushroom-0d31a3303.yml @@ -15,6 +15,7 @@ jobs: runs-on: ubuntu-latest name: Build and Deploy Job steps: + - name: Checkout Code uses: actions/checkout@v3 with: @@ -26,20 +27,11 @@ jobs: with: flutter-version: '3.22.2' # Specify the Flutter version you want to use - - name: Load Environment Variables - run: | - echo "Loading environment variables from .env.development" - cat .env.development | grep -v '^#' >> $GITHUB_ENV - - name: Install dependencies run: flutter pub get - name: Build Flutter Web App - run: | - flutter build web --release \ - --dart-define=ENV_NAME=${{ env.ENV_NAME }} \ - --dart-define=BASE_URL=${{ env.BASE_URL }} \ - --dart-define=FLAVOR=development + run: flutter build web - name: Build And Deploy id: builddeploy @@ -48,9 +40,12 @@ jobs: azure_static_web_apps_api_token: ${{ secrets.AZURE_STATIC_WEB_APPS_API_TOKEN_ZEALOUS_MUSHROOM_0D31A3303 }} repo_token: ${{ secrets.GITHUB_TOKEN }} # Used for Github integrations (i.e. PR comments) action: "upload" - app_location: "build/web" # App source code path + ###### Repository/Build Configurations - These values can be configured to match your app requirements. ###### + # For more information regarding Static Web App workflow configurations, please visit: https://aka.ms/swaworkflowconfig + app_location: "/build/web" # App source code path api_location: "" # Api source code path - optional - output_location: "build/web" # Built app content directory - optional + output_location: "/build/web" # Built app content directory - optional + ###### End of Repository/Build Configurations ###### close_pull_request_job: if: github.event_name == 'pull_request' && github.event.action == 'closed' From c4fc274dbfad775487d3f1c44724ff2d61874f8d Mon Sep 17 00:00:00 2001 From: Abdullah Alassaf Date: Wed, 4 Sep 2024 00:16:11 +0300 Subject: [PATCH 2/9] Added base url --- lib/utils/constants/api_const.dart | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/utils/constants/api_const.dart b/lib/utils/constants/api_const.dart index 5d24b501..cc4fc478 100644 --- a/lib/utils/constants/api_const.dart +++ b/lib/utils/constants/api_const.dart @@ -1,7 +1,8 @@ import 'package:flutter_dotenv/flutter_dotenv.dart'; abstract class ApiEndpoints { - static String baseUrl = dotenv.env['BASE_URL'] ?? ''; + // static String baseUrl = dotenv.env['BASE_URL'] ?? ''; + static String baseUrl = 'https://syncrow-dev.azurewebsites.net'; static const String signUp = '/authentication/user/signup'; static const String login = '/authentication/user/login'; static const String forgetPassword = '/authentication/user/forget-password'; From 316efd653ced169e42f1788054cdb2d5a0d9c335 Mon Sep 17 00:00:00 2001 From: faris Aljohari <83524184+farisaljohari@users.noreply.github.com> Date: Wed, 4 Sep 2024 16:38:42 +0300 Subject: [PATCH 3/9] ci: add Azure Static Web Apps workflow file on-behalf-of: @Azure opensource@microsoft.com --- ...static-web-apps-polite-smoke-017c65c10.yml | 46 +++++++++++++++++++ 1 file changed, 46 insertions(+) create mode 100644 .github/workflows/azure-static-web-apps-polite-smoke-017c65c10.yml diff --git a/.github/workflows/azure-static-web-apps-polite-smoke-017c65c10.yml b/.github/workflows/azure-static-web-apps-polite-smoke-017c65c10.yml new file mode 100644 index 00000000..22cdc827 --- /dev/null +++ b/.github/workflows/azure-static-web-apps-polite-smoke-017c65c10.yml @@ -0,0 +1,46 @@ +name: Azure Static Web Apps CI/CD + +on: + push: + branches: + - dev + pull_request: + types: [opened, synchronize, reopened, closed] + branches: + - dev + +jobs: + build_and_deploy_job: + if: github.event_name == 'push' || (github.event_name == 'pull_request' && github.event.action != 'closed') + runs-on: ubuntu-latest + name: Build and Deploy Job + steps: + - uses: actions/checkout@v3 + with: + submodules: true + lfs: false + - name: Build And Deploy + id: builddeploy + uses: Azure/static-web-apps-deploy@v1 + with: + azure_static_web_apps_api_token: ${{ secrets.AZURE_STATIC_WEB_APPS_API_TOKEN_POLITE_SMOKE_017C65C10 }} + repo_token: ${{ secrets.GITHUB_TOKEN }} # Used for Github integrations (i.e. PR comments) + action: "upload" + ###### Repository/Build Configurations - These values can be configured to match your app requirements. ###### + # For more information regarding Static Web App workflow configurations, please visit: https://aka.ms/swaworkflowconfig + app_location: "/web" # App source code path + api_location: "" # Api source code path - optional + output_location: "/web" # Built app content directory - optional + ###### End of Repository/Build Configurations ###### + + close_pull_request_job: + if: github.event_name == 'pull_request' && github.event.action == 'closed' + runs-on: ubuntu-latest + name: Close Pull Request Job + steps: + - name: Close Pull Request + id: closepullrequest + uses: Azure/static-web-apps-deploy@v1 + with: + azure_static_web_apps_api_token: ${{ secrets.AZURE_STATIC_WEB_APPS_API_TOKEN_POLITE_SMOKE_017C65C10 }} + action: "close" From a4b1a9e85d42d5829c0dada1c501916f80fbb4a8 Mon Sep 17 00:00:00 2001 From: faris Aljohari <83524184+farisaljohari@users.noreply.github.com> Date: Wed, 4 Sep 2024 16:44:25 +0300 Subject: [PATCH 4/9] test deploy dev --- ...static-web-apps-polite-smoke-017c65c10.yml | 22 +++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/.github/workflows/azure-static-web-apps-polite-smoke-017c65c10.yml b/.github/workflows/azure-static-web-apps-polite-smoke-017c65c10.yml index 22cdc827..5cb7a8d3 100644 --- a/.github/workflows/azure-static-web-apps-polite-smoke-017c65c10.yml +++ b/.github/workflows/azure-static-web-apps-polite-smoke-017c65c10.yml @@ -15,10 +15,24 @@ jobs: runs-on: ubuntu-latest name: Build and Deploy Job steps: - - uses: actions/checkout@v3 + + - name: Checkout Code + uses: actions/checkout@v3 with: submodules: true lfs: false + + - name: Set up Flutter + uses: subosito/flutter-action@v2 + with: + flutter-version: '3.22.2' # Specify the Flutter version you want to use + + - name: Install dependencies + run: flutter pub get + + - name: Build Flutter Web App + run: flutter build web + - name: Build And Deploy id: builddeploy uses: Azure/static-web-apps-deploy@v1 @@ -28,9 +42,9 @@ jobs: action: "upload" ###### Repository/Build Configurations - These values can be configured to match your app requirements. ###### # For more information regarding Static Web App workflow configurations, please visit: https://aka.ms/swaworkflowconfig - app_location: "/web" # App source code path + app_location: "/build/web" # App source code path api_location: "" # Api source code path - optional - output_location: "/web" # Built app content directory - optional + output_location: "/build/web" # Built app content directory - optional ###### End of Repository/Build Configurations ###### close_pull_request_job: @@ -43,4 +57,4 @@ jobs: uses: Azure/static-web-apps-deploy@v1 with: azure_static_web_apps_api_token: ${{ secrets.AZURE_STATIC_WEB_APPS_API_TOKEN_POLITE_SMOKE_017C65C10 }} - action: "close" + action: "close" \ No newline at end of file From c9160debd329674c51ac034f09f68b0446a40a4b Mon Sep 17 00:00:00 2001 From: mohammad Date: Wed, 4 Sep 2024 16:55:46 +0300 Subject: [PATCH 5/9] fix --- lib/main.dart | 3 +- .../view/access_management.dart | 2 +- lib/pages/auth/bloc/auth_bloc.dart | 3 +- .../auth/view/forget_password_web_page.dart | 10 +-- lib/pages/auth/view/login_web_page.dart | 65 ++++++++++--------- lib/pages/home/bloc/home_bloc.dart | 12 +++- lib/services/auth_api.dart | 6 +- lib/web_layout/web_app_bar.dart | 44 ++++++++----- 8 files changed, 81 insertions(+), 64 deletions(-) diff --git a/lib/main.dart b/lib/main.dart index eeaa4685..59be154e 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -13,7 +13,7 @@ import 'package:syncrow_web/utils/theme/theme.dart'; Future main() async { try { - const environment = String.fromEnvironment('FLAVOR', defaultValue: 'production'); + const environment = String.fromEnvironment('FLAVOR', defaultValue: 'development'); await dotenv.load(fileName: '.env.$environment'); WidgetsFlutterBinding.ensureInitialized(); initialSetup(); @@ -43,6 +43,7 @@ class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { + HomeBloc.fetchUserInfo(); return MultiBlocProvider( providers: [ BlocProvider(create: (context) => HomeBloc()), diff --git a/lib/pages/access_management/view/access_management.dart b/lib/pages/access_management/view/access_management.dart index 0d4e3a19..f66c8a99 100644 --- a/lib/pages/access_management/view/access_management.dart +++ b/lib/pages/access_management/view/access_management.dart @@ -59,7 +59,6 @@ class AccessManagementPage extends StatelessWidget with HelperResponsiveLayout { builder: (context, state) { final accessBloc = BlocProvider.of(context); final filteredData = accessBloc.filteredData; - return state is AccessLoaded ? const Center(child: CircularProgressIndicator()) : Container( @@ -87,6 +86,7 @@ class AccessManagementPage extends StatelessWidget with HelperResponsiveLayout { const SizedBox(height: 20), Expanded( child: DynamicTable( + tableName: 'AccessManagement', uuidIndex: 1, withSelectAll: true, isEmpty: filteredData.isEmpty, diff --git a/lib/pages/auth/bloc/auth_bloc.dart b/lib/pages/auth/bloc/auth_bloc.dart index 4e4a7b0e..2d18d8e3 100644 --- a/lib/pages/auth/bloc/auth_bloc.dart +++ b/lib/pages/auth/bloc/auth_bloc.dart @@ -132,7 +132,6 @@ class AuthBloc extends Bloc { void _login(LoginButtonPressed event, Emitter emit) async { emit(AuthLoading()); - if (isChecked) { try { if (event.username.isEmpty || event.password.isEmpty) { @@ -149,7 +148,7 @@ class AuthBloc extends Bloc { ); } catch (failure) { validate = 'Invalid Credentials!'; - emit(const LoginFailure(error: 'Invalid Credentials!')); + emit(LoginInitial()); return; } diff --git a/lib/pages/auth/view/forget_password_web_page.dart b/lib/pages/auth/view/forget_password_web_page.dart index efc580b3..a342a9a3 100644 --- a/lib/pages/auth/view/forget_password_web_page.dart +++ b/lib/pages/auth/view/forget_password_web_page.dart @@ -138,15 +138,6 @@ class ForgetPasswordWebPage extends StatelessWidget { CrossAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.start, children: [ - Text( - "Country/Region", - style: Theme.of(context) - .textTheme - .bodySmall! - .copyWith( - fontSize: 14, - fontWeight: FontWeight.w400), - ), const SizedBox(height: 10), SizedBox( child: _buildDropdownField(context, forgetBloc, size) @@ -171,6 +162,7 @@ class ForgetPasswordWebPage extends StatelessWidget { const SizedBox(height: 10), SizedBox( child: TextFormField( + controller:forgetBloc.forgetEmailController , validator: forgetBloc.validateEmail, decoration: textBoxDecoration()! .copyWith( diff --git a/lib/pages/auth/view/login_web_page.dart b/lib/pages/auth/view/login_web_page.dart index af211308..a728e850 100644 --- a/lib/pages/auth/view/login_web_page.dart +++ b/lib/pages/auth/view/login_web_page.dart @@ -410,38 +410,39 @@ class _LoginWebPageState extends State }, ), ), - SizedBox( - width: 220, - child: RichText( - text: TextSpan( - text: 'Agree to ', - style: const TextStyle(color: Colors.white), - children: [ - TextSpan( - text: '(Terms of Service)', - style: const TextStyle(color: Colors.black), - recognizer: TapGestureRecognizer() - ..onTap = () { - loginBloc.launchURL('https://example.com/terms'); - }, - ), - TextSpan( - text: ' (Legal Statement)', - style: const TextStyle(color: Colors.black), - recognizer: TapGestureRecognizer() - ..onTap = () { - loginBloc.launchURL('https://example.com/legal'); - }, - ), - TextSpan( - text: ' (Privacy Statement)', - style: const TextStyle(color: Colors.black), - recognizer: TapGestureRecognizer() - ..onTap = () { - loginBloc.launchURL('https://example.com/privacy'); - }, - ), - ], + Expanded( + child: SizedBox( + child: RichText( + text: TextSpan( + text: 'Agree to ', + style: const TextStyle(color: Colors.white), + children: [ + TextSpan( + text: '(Terms of Service)', + style: const TextStyle(color: Colors.black), + recognizer: TapGestureRecognizer() + ..onTap = () { + loginBloc.launchURL('https://example.com/terms'); + }, + ), + TextSpan( + text: ' (Legal Statement)', + style: const TextStyle(color: Colors.black), + recognizer: TapGestureRecognizer() + ..onTap = () { + loginBloc.launchURL('https://example.com/legal'); + }, + ), + TextSpan( + text: ' (Privacy Statement)', + style: const TextStyle(color: Colors.black), + recognizer: TapGestureRecognizer() + ..onTap = () { + loginBloc.launchURL('https://example.com/privacy'); + }, + ), + ], + ), ), ), ), diff --git a/lib/pages/home/bloc/home_bloc.dart b/lib/pages/home/bloc/home_bloc.dart index cc40b8fe..32de812e 100644 --- a/lib/pages/home/bloc/home_bloc.dart +++ b/lib/pages/home/bloc/home_bloc.dart @@ -16,7 +16,7 @@ class HomeBloc extends Bloc { final BuchheimWalkerConfiguration builder = BuchheimWalkerConfiguration(); List sourcesList = []; List destinationsList = []; - UserModel? user; + static UserModel? user; HomeBloc() : super((HomeInitial())) { on(_createNode); @@ -50,6 +50,16 @@ class HomeBloc extends Bloc { } } + static Future fetchUserInfo() async { + try { + var uuid = + await const FlutterSecureStorage().read(key: UserModel.userUuidKey); + user = await HomeApi().fetchUserInfo(uuid); + } catch (e) { + return; + } + } + List homeItems = [ HomeItemModel( title: 'Access', diff --git a/lib/services/auth_api.dart b/lib/services/auth_api.dart index a09fa7ba..af8f3e9d 100644 --- a/lib/services/auth_api.dart +++ b/lib/services/auth_api.dart @@ -39,13 +39,17 @@ class AuthenticationAPI { expectedResponseModel: (json) { return json['data']['cooldown']; }); - return response; } on DioException catch (e) { + final errorData = e.response!.data; + String errorMessage = errorData['message']; + print('sendOtp=$errorMessage'); if (e.response != null) { if (e.response!.statusCode == 400) { final errorData = e.response!.data; String errorMessage = errorData['message']; + print('sendOtp=$errorMessage'); + if (errorMessage == 'User not found') { return 1; } else { diff --git a/lib/web_layout/web_app_bar.dart b/lib/web_layout/web_app_bar.dart index 69f88f09..44109268 100644 --- a/lib/web_layout/web_app_bar.dart +++ b/lib/web_layout/web_app_bar.dart @@ -5,19 +5,29 @@ import 'package:syncrow_web/pages/home/bloc/home_state.dart'; import 'package:syncrow_web/utils/color_manager.dart'; import 'package:syncrow_web/utils/helpers/responsice_layout_helper/responsive_layout_helper.dart'; -class WebAppBar extends StatelessWidget with HelperResponsiveLayout { +import '../pages/auth/model/user_model.dart'; + +class WebAppBar extends StatefulWidget{ final Widget? title; final Widget? centerBody; final Widget? rightBody; const WebAppBar({super.key, this.title, this.centerBody, this.rightBody}); + @override + State createState() => _WebAppBarState(); +} + +class _WebAppBarState extends State with HelperResponsiveLayout { + @override + void initState() { + super.initState(); + } @override Widget build(BuildContext context) { bool isSmallScreen = isSmallScreenSize(context); bool isHalfMediumScreen = isHafMediumScreenSize(context); return BlocBuilder(builder: (context, state) { - final user = context.read().user; return Container( height: (isSmallScreen || isHalfMediumScreen) ? 130 : 100, decoration: const BoxDecoration(color: ColorsManager.secondaryColor), @@ -26,21 +36,21 @@ class WebAppBar extends StatelessWidget with HelperResponsiveLayout { ? Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - if (title != null) + if (widget.title != null) Align( alignment: Alignment.centerLeft, - child: title!, + child: widget.title!, ), - if (centerBody != null) + if (widget.centerBody != null) Padding( padding: const EdgeInsets.only(top: 8.0), - child: centerBody, + child: widget.centerBody, ), - if (rightBody != null || user != null) + if (widget.rightBody != null || HomeBloc.user != null) Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - if (rightBody != null) rightBody!, + if (widget.rightBody != null) widget.rightBody!, Row( children: [ const SizedBox.square( @@ -59,9 +69,9 @@ class WebAppBar extends StatelessWidget with HelperResponsiveLayout { const SizedBox( width: 10, ), - if (user != null) + if (HomeBloc.user != null) Text( - '${user.firstName} ${user.lastName}', + '${HomeBloc.user!.firstName} ${HomeBloc.user!.lastName}', style: Theme.of(context).textTheme.bodyLarge, ), ], @@ -77,11 +87,11 @@ class WebAppBar extends StatelessWidget with HelperResponsiveLayout { Expanded( child: Row( children: [ - title!, - if (centerBody != null) + widget.title!, + if (widget.centerBody != null) Padding( padding: const EdgeInsets.only(left: 80), - child: centerBody!, + child: widget.centerBody!, ), ], ), @@ -89,10 +99,10 @@ class WebAppBar extends StatelessWidget with HelperResponsiveLayout { Row( mainAxisSize: MainAxisSize.min, children: [ - if (rightBody != null) + if (widget.rightBody != null) Align( alignment: Alignment.centerRight, - child: rightBody, + child: widget.rightBody, ), const SizedBox( width: 10, @@ -113,9 +123,9 @@ class WebAppBar extends StatelessWidget with HelperResponsiveLayout { const SizedBox( width: 10, ), - if (user != null) + if (HomeBloc.user != null) Text( - '${user.firstName} ${user.lastName}', + '${HomeBloc.user!.firstName} ${HomeBloc.user!.lastName}', style: Theme.of(context).textTheme.bodyLarge, ), ], From 0db7a9e1ba2156e504173c16d035691a83cb5049 Mon Sep 17 00:00:00 2001 From: Abdullah Alassaf Date: Wed, 4 Sep 2024 17:03:21 +0300 Subject: [PATCH 6/9] Read base url from development, and removed unused lines --- ...static-web-apps-polite-smoke-017c65c10.yml | 2 +- ...static-web-apps-salmon-ocean-0c2902310.yml | 65 ------------------- ...ic-web-apps-zealous-mushroom-0d31a3303.yml | 60 ----------------- lib/services/access_mang_api.dart | 1 - lib/services/auth_api.dart | 29 ++++----- lib/utils/constants/api_const.dart | 3 +- 6 files changed, 15 insertions(+), 145 deletions(-) delete mode 100644 .github/workflows/azure-static-web-apps-salmon-ocean-0c2902310.yml delete mode 100644 .github/workflows/azure-static-web-apps-zealous-mushroom-0d31a3303.yml diff --git a/.github/workflows/azure-static-web-apps-polite-smoke-017c65c10.yml b/.github/workflows/azure-static-web-apps-polite-smoke-017c65c10.yml index 5cb7a8d3..e28d1bb2 100644 --- a/.github/workflows/azure-static-web-apps-polite-smoke-017c65c10.yml +++ b/.github/workflows/azure-static-web-apps-polite-smoke-017c65c10.yml @@ -31,7 +31,7 @@ jobs: run: flutter pub get - name: Build Flutter Web App - run: flutter build web + run: flutter build web --release --dart-define=FLAVOR=development - name: Build And Deploy id: builddeploy diff --git a/.github/workflows/azure-static-web-apps-salmon-ocean-0c2902310.yml b/.github/workflows/azure-static-web-apps-salmon-ocean-0c2902310.yml deleted file mode 100644 index d556b9e5..00000000 --- a/.github/workflows/azure-static-web-apps-salmon-ocean-0c2902310.yml +++ /dev/null @@ -1,65 +0,0 @@ -name: Azure Static Web Apps CI/CD - -on: - push: - branches: - - main - pull_request: - types: [opened, synchronize, reopened, closed] - branches: - - main - -jobs: - build_and_deploy_job: - if: github.event_name == 'push' || (github.event_name == 'pull_request' && github.event.action != 'closed') - runs-on: ubuntu-latest - name: Build and Deploy Job - steps: - - name: Checkout Code - uses: actions/checkout@v3 - with: - submodules: true - lfs: false - - - name: Set up Flutter - uses: subosito/flutter-action@v2 - with: - flutter-version: '3.22.2' # Specify the Flutter version you want to use - - - name: Load Environment Variables - run: | - echo "Loading environment variables from .env.production" - cat .env.production | grep -v '^#' >> $GITHUB_ENV - - - name: Install dependencies - run: flutter pub get - - - name: Build Flutter Web App - run: | - flutter build web --release \ - --dart-define=ENV_NAME=${{ env.ENV_NAME }} \ - --dart-define=BASE_URL=${{ env.BASE_URL }} \ - --dart-define=FLAVOR=production - - - name: Build And Deploy - id: builddeploy - uses: Azure/static-web-apps-deploy@v1 - with: - azure_static_web_apps_api_token: ${{ secrets.AZURE_STATIC_WEB_APPS_API_TOKEN_ZEALOUS_MUSHROOM_0D31A3303 }} - repo_token: ${{ secrets.GITHUB_TOKEN }} # Used for Github integrations (i.e. PR comments) - action: "upload" - app_location: "." # Root of your Flutter project - api_location: "" # API source code path - optional - output_location: "build/web" # Path to the built web app content directory - - close_pull_request_job: - if: github.event_name == 'pull_request' && github.event.action == 'closed' - runs-on: ubuntu-latest - name: Close Pull Request Job - steps: - - name: Close Pull Request - id: closepullrequest - uses: Azure/static-web-apps-deploy@v1 - with: - azure_static_web_apps_api_token: ${{ secrets.AZURE_STATIC_WEB_APPS_API_TOKEN_ZEALOUS_MUSHROOM_0D31A3303 }} - action: "close" diff --git a/.github/workflows/azure-static-web-apps-zealous-mushroom-0d31a3303.yml b/.github/workflows/azure-static-web-apps-zealous-mushroom-0d31a3303.yml deleted file mode 100644 index b8d576b9..00000000 --- a/.github/workflows/azure-static-web-apps-zealous-mushroom-0d31a3303.yml +++ /dev/null @@ -1,60 +0,0 @@ -name: Azure Static Web Apps CI/CD - -on: - push: - branches: - - dev - pull_request: - types: [opened, synchronize, reopened, closed] - branches: - - dev - -jobs: - build_and_deploy_job: - if: github.event_name == 'push' || (github.event_name == 'pull_request' && github.event.action != 'closed') - runs-on: ubuntu-latest - name: Build and Deploy Job - steps: - - - name: Checkout Code - uses: actions/checkout@v3 - with: - submodules: true - lfs: false - - - name: Set up Flutter - uses: subosito/flutter-action@v2 - with: - flutter-version: '3.22.2' # Specify the Flutter version you want to use - - - name: Install dependencies - run: flutter pub get - - - name: Build Flutter Web App - run: flutter build web - - - name: Build And Deploy - id: builddeploy - uses: Azure/static-web-apps-deploy@v1 - with: - azure_static_web_apps_api_token: ${{ secrets.AZURE_STATIC_WEB_APPS_API_TOKEN_ZEALOUS_MUSHROOM_0D31A3303 }} - repo_token: ${{ secrets.GITHUB_TOKEN }} # Used for Github integrations (i.e. PR comments) - action: "upload" - ###### Repository/Build Configurations - These values can be configured to match your app requirements. ###### - # For more information regarding Static Web App workflow configurations, please visit: https://aka.ms/swaworkflowconfig - app_location: "/build/web" # App source code path - api_location: "" # Api source code path - optional - output_location: "/build/web" # Built app content directory - optional - ###### End of Repository/Build Configurations ###### - - close_pull_request_job: - if: github.event_name == 'pull_request' && github.event.action == 'closed' - runs-on: ubuntu-latest - name: Close Pull Request Job - steps: - - name: Close Pull Request - id: closepullrequest - uses: Azure/static-web-apps-deploy@v1 - with: - azure_static_web_apps_api_token: ${{ secrets.AZURE_STATIC_WEB_APPS_API_TOKEN_ZEALOUS_MUSHROOM_0D31A3303 }} - action: "close" diff --git a/lib/services/access_mang_api.dart b/lib/services/access_mang_api.dart index b99b75a9..103f6121 100644 --- a/lib/services/access_mang_api.dart +++ b/lib/services/access_mang_api.dart @@ -1,5 +1,4 @@ import 'dart:convert'; -import 'package:flutter/cupertino.dart'; import 'package:syncrow_web/pages/access_management/model/password_model.dart'; import 'package:syncrow_web/pages/visitor_password/model/device_model.dart'; import 'package:syncrow_web/pages/visitor_password/model/schedule_model.dart'; diff --git a/lib/services/auth_api.dart b/lib/services/auth_api.dart index a09fa7ba..44a4e7cf 100644 --- a/lib/services/auth_api.dart +++ b/lib/services/auth_api.dart @@ -1,5 +1,4 @@ import 'package:dio/dio.dart'; -import 'package:flutter/foundation.dart'; import 'package:syncrow_web/pages/auth/model/region_model.dart'; import 'package:syncrow_web/pages/auth/model/token.dart'; import 'package:syncrow_web/services/api/http_service.dart'; @@ -25,8 +24,7 @@ class AuthenticationAPI { path: ApiEndpoints.forgetPassword, body: {"email": email, "password": password}, showServerMessage: true, - expectedResponseModel: (json) { - }); + expectedResponseModel: (json) {}); return response; } @@ -64,19 +62,18 @@ class AuthenticationAPI { } static Future verifyOtp({required String email, required String otpCode}) async { - final response = await HTTPService().post( - path: ApiEndpoints.verifyOtp, - body: {"email": email, "type": "PASSWORD", "otpCode": otpCode}, - showServerMessage: true, - expectedResponseModel: (json) { - if (json['message'] == 'Otp Verified Successfully') { - return true; - } else { - return false; - } - }); - return response; - + final response = await HTTPService().post( + path: ApiEndpoints.verifyOtp, + body: {"email": email, "type": "PASSWORD", "otpCode": otpCode}, + showServerMessage: true, + expectedResponseModel: (json) { + if (json['message'] == 'Otp Verified Successfully') { + return true; + } else { + return false; + } + }); + return response; } static Future> fetchRegion() async { diff --git a/lib/utils/constants/api_const.dart b/lib/utils/constants/api_const.dart index cc4fc478..5d24b501 100644 --- a/lib/utils/constants/api_const.dart +++ b/lib/utils/constants/api_const.dart @@ -1,8 +1,7 @@ import 'package:flutter_dotenv/flutter_dotenv.dart'; abstract class ApiEndpoints { - // static String baseUrl = dotenv.env['BASE_URL'] ?? ''; - static String baseUrl = 'https://syncrow-dev.azurewebsites.net'; + static String baseUrl = dotenv.env['BASE_URL'] ?? ''; static const String signUp = '/authentication/user/signup'; static const String login = '/authentication/user/login'; static const String forgetPassword = '/authentication/user/forget-password'; From 74dccbf89951af1988e575bb776c6ae70be8c94f Mon Sep 17 00:00:00 2001 From: ashrafzarkanisala Date: Thu, 5 Sep 2024 00:43:57 +0300 Subject: [PATCH 7/9] push search, space color selection, global user access fixes --- assets/icons/bathroom.svg | 55 +++++++------ assets/icons/bedroom.svg | 61 ++++++++------- assets/icons/dyi.svg | 25 +++--- assets/icons/office.svg | 77 +++++++++---------- assets/icons/parlour.svg | 57 +++++++------- lib/main.dart | 9 ++- .../bloc/device_managment_bloc.dart | 38 +++++++-- .../all_devices/models/devices_model.dart | 4 +- .../sensors_widgets/presence_space_type.dart | 15 ++-- lib/pages/home/bloc/home_bloc.dart | 20 ++--- lib/pages/home/view/home_page.dart | 16 +--- lib/web_layout/web_app_bar.dart | 18 ++--- 12 files changed, 203 insertions(+), 192 deletions(-) diff --git a/assets/icons/bathroom.svg b/assets/icons/bathroom.svg index 51fc8b6a..8a75f646 100644 --- a/assets/icons/bathroom.svg +++ b/assets/icons/bathroom.svg @@ -1,29 +1,28 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/assets/icons/bedroom.svg b/assets/icons/bedroom.svg index d579b003..6797009c 100644 --- a/assets/icons/bedroom.svg +++ b/assets/icons/bedroom.svg @@ -1,34 +1,33 @@ - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + - + - - - - - - + + + + + + diff --git a/assets/icons/dyi.svg b/assets/icons/dyi.svg index 7da61e8e..938d2ba2 100644 --- a/assets/icons/dyi.svg +++ b/assets/icons/dyi.svg @@ -1,14 +1,13 @@ - - - - - - - - - - - - - + + + + + + + + + + + + diff --git a/assets/icons/office.svg b/assets/icons/office.svg index 03a2badd..479352c6 100644 --- a/assets/icons/office.svg +++ b/assets/icons/office.svg @@ -1,40 +1,39 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/assets/icons/parlour.svg b/assets/icons/parlour.svg index 3298393a..52562dd2 100644 --- a/assets/icons/parlour.svg +++ b/assets/icons/parlour.svg @@ -1,30 +1,29 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/lib/main.dart b/lib/main.dart index 59be154e..3b861d10 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -4,6 +4,7 @@ import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_dotenv/flutter_dotenv.dart'; import 'package:syncrow_web/pages/auth/bloc/auth_bloc.dart'; import 'package:syncrow_web/pages/home/bloc/home_bloc.dart'; +import 'package:syncrow_web/pages/home/bloc/home_event.dart'; import 'package:syncrow_web/pages/visitor_password/bloc/visitor_password_bloc.dart'; import 'package:go_router/go_router.dart'; import 'package:syncrow_web/services/locator.dart'; @@ -13,7 +14,8 @@ import 'package:syncrow_web/utils/theme/theme.dart'; Future main() async { try { - const environment = String.fromEnvironment('FLAVOR', defaultValue: 'development'); + const environment = + String.fromEnvironment('FLAVOR', defaultValue: 'development'); await dotenv.load(fileName: '.env.$environment'); WidgetsFlutterBinding.ensureInitialized(); initialSetup(); @@ -43,10 +45,11 @@ class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { - HomeBloc.fetchUserInfo(); + //HomeBloc.fetchUserInfo(); return MultiBlocProvider( providers: [ - BlocProvider(create: (context) => HomeBloc()), + BlocProvider( + create: (context) => HomeBloc()..add(const FetchUserInfo())), BlocProvider( create: (context) => VisitorPasswordBloc(), ) diff --git a/lib/pages/device_managment/all_devices/bloc/device_managment_bloc.dart b/lib/pages/device_managment/all_devices/bloc/device_managment_bloc.dart index d64599f1..c11e05e1 100644 --- a/lib/pages/device_managment/all_devices/bloc/device_managment_bloc.dart +++ b/lib/pages/device_managment/all_devices/bloc/device_managment_bloc.dart @@ -134,11 +134,27 @@ class DeviceManagementBloc void _onSearchDevices( SearchDevices event, Emitter emit) { - if (_devices.isNotEmpty) { - _selectedDevices.clear(); - _selectedIndex = 0; + // If the search fields are all empty, restore the last filtered devices + if ((event.community == null || event.community!.isEmpty) && + (event.unitName == null || event.unitName!.isEmpty) && + (event.productName == null || event.productName!.isEmpty)) { + // If the current state is filtered, re-emit the filtered state + if (state is DeviceManagementFiltered) { + add(FilterDevices(_getFilterFromIndex(_selectedIndex))); + } + } - final filteredDevices = _devices.where((device) { + List devicesToSearch = _devices; + + if (state is DeviceManagementFiltered) { + devicesToSearch = (state as DeviceManagementFiltered).filteredDevices; + } + + if (devicesToSearch.isNotEmpty) { + _selectedDevices.clear(); + _selectedIndex = _selectedIndex; + + final filteredDevices = devicesToSearch.where((device) { final matchesCommunity = event.community == null || event.community!.isEmpty || (device.room?.name @@ -157,11 +173,21 @@ class DeviceManagementBloc ?.toLowerCase() .contains(event.productName!.toLowerCase()) ?? false); - return matchesCommunity && matchesUnit && matchesProductName; + final matchesDeviceName = event.productName == null || + event.productName!.isEmpty || + (device.categoryName + ?.toLowerCase() + .contains(event.productName!.toLowerCase()) ?? + false); + + return matchesCommunity && + matchesUnit && + (matchesProductName || matchesDeviceName); }).toList(); + emit(DeviceManagementFiltered( filteredDevices: filteredDevices, - selectedIndex: 0, + selectedIndex: _selectedIndex, onlineCount: _onlineCount, offlineCount: _offlineCount, lowBatteryCount: _lowBatteryCount, diff --git a/lib/pages/device_managment/all_devices/models/devices_model.dart b/lib/pages/device_managment/all_devices/models/devices_model.dart index 48e99a1a..13d3dd2e 100644 --- a/lib/pages/device_managment/all_devices/models/devices_model.dart +++ b/lib/pages/device_managment/all_devices/models/devices_model.dart @@ -119,7 +119,7 @@ class AllDevicesModel { timeZone = json['timeZone']?.toString(); updateTime = int.tryParse(json['updateTime']?.toString() ?? ''); uuid = json['uuid']?.toString(); - batteryLevel = int.tryParse(json['batteryLevel']?.toString() ?? ''); + batteryLevel = int.tryParse(json['battery']?.toString() ?? ''); } Map toJson() { final data = {}; @@ -151,7 +151,7 @@ class AllDevicesModel { data['timeZone'] = timeZone; data['updateTime'] = updateTime; data['uuid'] = uuid; - data['batteryLevel'] = batteryLevel; + data['battery'] = batteryLevel; return data; } } diff --git a/lib/pages/device_managment/shared/sensors_widgets/presence_space_type.dart b/lib/pages/device_managment/shared/sensors_widgets/presence_space_type.dart index 2f593abe..e1ca0586 100644 --- a/lib/pages/device_managment/shared/sensors_widgets/presence_space_type.dart +++ b/lib/pages/device_managment/shared/sensors_widgets/presence_space_type.dart @@ -53,18 +53,19 @@ class PresenceSpaceType extends StatelessWidget { return GestureDetector( onTap: () => action(spaceType.name), child: Container( + width: 40, + height: 40, + padding: const EdgeInsets.all(8), decoration: BoxDecoration( borderRadius: BorderRadius.circular(100), - border: Border.all( - color: value == spaceType - ? ColorsManager.blueColor - : Colors.transparent, - ), + color: value == spaceType + ? ColorsManager.primaryColorWithOpacity + : ColorsManager.textGray, ), child: SvgPicture.asset( icon, - width: 40, - height: 40, + width: 25, + height: 22, ), ), ); diff --git a/lib/pages/home/bloc/home_bloc.dart b/lib/pages/home/bloc/home_bloc.dart index 32de812e..98320a88 100644 --- a/lib/pages/home/bloc/home_bloc.dart +++ b/lib/pages/home/bloc/home_bloc.dart @@ -16,7 +16,7 @@ class HomeBloc extends Bloc { final BuchheimWalkerConfiguration builder = BuchheimWalkerConfiguration(); List sourcesList = []; List destinationsList = []; - static UserModel? user; + UserModel? user; HomeBloc() : super((HomeInitial())) { on(_createNode); @@ -50,15 +50,15 @@ class HomeBloc extends Bloc { } } - static Future fetchUserInfo() async { - try { - var uuid = - await const FlutterSecureStorage().read(key: UserModel.userUuidKey); - user = await HomeApi().fetchUserInfo(uuid); - } catch (e) { - return; - } - } +// static Future fetchUserInfo() async { +// try { +// var uuid = +// await const FlutterSecureStorage().read(key: UserModel.userUuidKey); +// user = await HomeApi().fetchUserInfo(uuid); +// } catch (e) { +// return; +// } +// } List homeItems = [ HomeItemModel( diff --git a/lib/pages/home/view/home_page.dart b/lib/pages/home/view/home_page.dart index 75107e84..9159011f 100644 --- a/lib/pages/home/view/home_page.dart +++ b/lib/pages/home/view/home_page.dart @@ -1,25 +1,11 @@ import 'package:flutter/cupertino.dart'; -import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:syncrow_web/pages/home/bloc/home_bloc.dart'; -import 'package:syncrow_web/pages/home/bloc/home_event.dart'; import 'package:syncrow_web/pages/home/view/home_page_mobile.dart'; import 'package:syncrow_web/pages/home/view/home_page_web.dart'; import 'package:syncrow_web/utils/helpers/responsice_layout_helper/responsive_layout_helper.dart'; -class HomePage extends StatefulWidget { +class HomePage extends StatelessWidget with HelperResponsiveLayout { const HomePage({super.key}); - @override - State createState() => _HomePageState(); -} - -class _HomePageState extends State with HelperResponsiveLayout { - @override - void initState() { - super.initState(); - context.read().add(const FetchUserInfo()); - } - @override Widget build(BuildContext context) { final isSmallScreen = isSmallScreenSize(context); diff --git a/lib/web_layout/web_app_bar.dart b/lib/web_layout/web_app_bar.dart index 44109268..b3236aae 100644 --- a/lib/web_layout/web_app_bar.dart +++ b/lib/web_layout/web_app_bar.dart @@ -5,9 +5,7 @@ import 'package:syncrow_web/pages/home/bloc/home_state.dart'; import 'package:syncrow_web/utils/color_manager.dart'; import 'package:syncrow_web/utils/helpers/responsice_layout_helper/responsive_layout_helper.dart'; -import '../pages/auth/model/user_model.dart'; - -class WebAppBar extends StatefulWidget{ +class WebAppBar extends StatefulWidget { final Widget? title; final Widget? centerBody; final Widget? rightBody; @@ -18,16 +16,18 @@ class WebAppBar extends StatefulWidget{ State createState() => _WebAppBarState(); } -class _WebAppBarState extends State with HelperResponsiveLayout { +class _WebAppBarState extends State with HelperResponsiveLayout { @override void initState() { super.initState(); } + @override Widget build(BuildContext context) { bool isSmallScreen = isSmallScreenSize(context); bool isHalfMediumScreen = isHafMediumScreenSize(context); return BlocBuilder(builder: (context, state) { + final user = context.read().user; return Container( height: (isSmallScreen || isHalfMediumScreen) ? 130 : 100, decoration: const BoxDecoration(color: ColorsManager.secondaryColor), @@ -46,7 +46,7 @@ class _WebAppBarState extends State with HelperResponsiveLayout { padding: const EdgeInsets.only(top: 8.0), child: widget.centerBody, ), - if (widget.rightBody != null || HomeBloc.user != null) + if (widget.rightBody != null || user != null) Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ @@ -69,9 +69,9 @@ class _WebAppBarState extends State with HelperResponsiveLayout { const SizedBox( width: 10, ), - if (HomeBloc.user != null) + if (user != null) Text( - '${HomeBloc.user!.firstName} ${HomeBloc.user!.lastName}', + '${user.firstName} ${user.lastName}', style: Theme.of(context).textTheme.bodyLarge, ), ], @@ -123,9 +123,9 @@ class _WebAppBarState extends State with HelperResponsiveLayout { const SizedBox( width: 10, ), - if (HomeBloc.user != null) + if (user != null) Text( - '${HomeBloc.user!.firstName} ${HomeBloc.user!.lastName}', + '${user.firstName} ${user.lastName}', style: Theme.of(context).textTheme.bodyLarge, ), ], From b94717bc70cb99d5d93c71328c8ff27e67a2f6ef Mon Sep 17 00:00:00 2001 From: Abdullah Alassaf Date: Thu, 5 Sep 2024 15:22:14 +0300 Subject: [PATCH 8/9] Bug fixes, and added a logout button --- lib/pages/auth/bloc/auth_bloc.dart | 74 ++++++++--------- lib/pages/common/custom_dialog.dart | 79 ++++++++----------- .../bloc/device_managment_bloc.dart | 57 +++++-------- lib/web_layout/web_app_bar.dart | 56 +++++++++++++ 4 files changed, 139 insertions(+), 127 deletions(-) diff --git a/lib/pages/auth/bloc/auth_bloc.dart b/lib/pages/auth/bloc/auth_bloc.dart index 2d18d8e3..9e4fb1d1 100644 --- a/lib/pages/auth/bloc/auth_bloc.dart +++ b/lib/pages/auth/bloc/auth_bloc.dart @@ -31,8 +31,7 @@ class AuthBloc extends Bloc { ////////////////////////////// forget password ////////////////////////////////// final TextEditingController forgetEmailController = TextEditingController(); - final TextEditingController forgetPasswordController = - TextEditingController(); + final TextEditingController forgetPasswordController = TextEditingController(); final TextEditingController forgetOtp = TextEditingController(); final forgetFormKey = GlobalKey(); late bool checkValidate = false; @@ -41,15 +40,13 @@ class AuthBloc extends Bloc { int _remainingTime = 0; List? regionList = [RegionModel(name: 'name', id: 'id')]; - Future _onStartTimer( - StartTimerEvent event, Emitter emit) async { + Future _onStartTimer(StartTimerEvent event, Emitter emit) async { if (_validateInputs(emit)) return; if (_timer != null && _timer!.isActive) { return; } _remainingTime = 1; - add(UpdateTimerEvent( - remainingTime: _remainingTime, isButtonEnabled: false)); + add(UpdateTimerEvent(remainingTime: _remainingTime, isButtonEnabled: false)); _remainingTime = (await AuthenticationAPI.sendOtp( email: forgetEmailController.text, regionUuid: regionUuid))!; _timer = Timer.periodic(const Duration(seconds: 1), (timer) { @@ -58,8 +55,7 @@ class AuthBloc extends Bloc { _timer?.cancel(); add(const UpdateTimerEvent(remainingTime: 0, isButtonEnabled: true)); } else { - add(UpdateTimerEvent( - remainingTime: _remainingTime, isButtonEnabled: false)); + add(UpdateTimerEvent(remainingTime: _remainingTime, isButtonEnabled: false)); } }); } @@ -69,36 +65,32 @@ class AuthBloc extends Bloc { emit(const TimerState(isButtonEnabled: true, remainingTime: 0)); } - Future changePassword( - ChangePasswordEvent event, Emitter emit) async { + Future changePassword(ChangePasswordEvent event, Emitter emit) async { try { emit(LoadingForgetState()); var response = await AuthenticationAPI.verifyOtp( email: forgetEmailController.text, otpCode: forgetOtp.text); if (response == true) { await AuthenticationAPI.forgetPassword( - password: forgetPasswordController.text, - email: forgetEmailController.text); + password: forgetPasswordController.text, email: forgetEmailController.text); _timer?.cancel(); emit(const TimerState(isButtonEnabled: true, remainingTime: 0)); emit(SuccessForgetState()); } + } on DioException catch (e) { + final errorData = e.response!.data; + String errorMessage = errorData['message']; + if (errorMessage == 'this email is not registered') { + validate = 'Invalid Credentials!'; + emit(AuthInitialState()); + } else if (errorMessage == "You entered wrong otp") { + forgetValidate = 'Wrong one time password.'; + emit(AuthInitialState()); + } else if (errorMessage == "OTP expired") { + forgetValidate = 'One time password has been expired.'; + emit(AuthInitialState()); + } } - on DioException catch (e) { - final errorData = e.response!.data; - String errorMessage = errorData['message']; - if(errorMessage=='this email is not registered'){ - validate='Invalid Credentials!'; - emit(AuthInitialState()); - }else if (errorMessage == "You entered wrong otp") { - forgetValidate = 'Wrong one time password.'; - emit(AuthInitialState()); - } else if (errorMessage == "OTP expired") { - forgetValidate = 'One time password has been expired.'; - emit(AuthInitialState()); - } - - } } String? validateCode(String? value) { @@ -109,9 +101,7 @@ class AuthBloc extends Bloc { } void _onUpdateTimer(UpdateTimerEvent event, Emitter emit) { - emit(TimerState( - isButtonEnabled: event.isButtonEnabled, - remainingTime: event.remainingTime)); + emit(TimerState(isButtonEnabled: event.isButtonEnabled, remainingTime: event.remainingTime)); } ///////////////////////////////////// login ///////////////////////////////////// @@ -142,9 +132,7 @@ class AuthBloc extends Bloc { token = await AuthenticationAPI.loginWithEmail( model: LoginWithEmailModel( - email: event.username, - password: event.password, - regionUuid: event.regionUuid), + email: event.username, password: event.password, regionUuid: event.regionUuid), ); } catch (failure) { validate = 'Invalid Credentials!'; @@ -154,8 +142,7 @@ class AuthBloc extends Bloc { if (token.accessTokenIsNotEmpty) { FlutterSecureStorage storage = const FlutterSecureStorage(); - await storage.write( - key: Token.loginAccessTokenKey, value: token.accessToken); + await storage.write(key: Token.loginAccessTokenKey, value: token.accessToken); const FlutterSecureStorage().write( key: UserModel.userUuidKey, value: Token.decodeToken(token.accessToken)['uuid'].toString()); @@ -310,14 +297,12 @@ class AuthBloc extends Bloc { static Future getTokenAndValidate() async { try { const storage = FlutterSecureStorage(); - final firstLaunch = await SharedPreferencesHelper.readBoolFromSP( - StringsManager.firstLaunch) ?? - true; + final firstLaunch = + await SharedPreferencesHelper.readBoolFromSP(StringsManager.firstLaunch) ?? true; if (firstLaunch) { storage.deleteAll(); } - await SharedPreferencesHelper.saveBoolToSP( - StringsManager.firstLaunch, false); + await SharedPreferencesHelper.saveBoolToSP(StringsManager.firstLaunch, false); final value = await storage.read(key: Token.loginAccessTokenKey) ?? ''; if (value.isEmpty) { return 'Token not found'; @@ -369,9 +354,7 @@ class AuthBloc extends Bloc { final String formattedTime = [ if (days > 0) '${days}d', // Append 'd' for days if (days > 0 || hours > 0) - hours - .toString() - .padLeft(2, '0'), // Show hours if there are days or hours + hours.toString().padLeft(2, '0'), // Show hours if there are days or hours minutes.toString().padLeft(2, '0'), seconds.toString().padLeft(2, '0'), ].join(':'); @@ -409,4 +392,9 @@ class AuthBloc extends Bloc { forgetValidate = ''; emit(LoginInitial()); } + + static logout() { + const storage = FlutterSecureStorage(); + storage.deleteAll(); + } } diff --git a/lib/pages/common/custom_dialog.dart b/lib/pages/common/custom_dialog.dart index e75c1e90..a40ef10f 100644 --- a/lib/pages/common/custom_dialog.dart +++ b/lib/pages/common/custom_dialog.dart @@ -1,6 +1,5 @@ import 'package:flutter/material.dart'; import 'package:flutter_svg/flutter_svg.dart'; -import 'package:syncrow_web/utils/color_manager.dart'; Future showCustomDialog({ required BuildContext context, @@ -13,7 +12,7 @@ Future showCustomDialog({ double? iconWidth, VoidCallback? onOkPressed, bool barrierDismissible = false, - required actions, + required List actions, }) { return showDialog( context: context, @@ -21,59 +20,43 @@ Future showCustomDialog({ builder: (BuildContext context) { final size = MediaQuery.of(context).size; return AlertDialog( - alignment: Alignment.center, - content: SizedBox( - height: dialogHeight ?? size.height * 0.15, - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - if (iconPath != null) - SvgPicture.asset( - iconPath, - height: iconHeight ?? 35, - width: iconWidth ?? 35, - ), - if (title != null) + alignment: Alignment.center, + content: SizedBox( + height: dialogHeight ?? size.height * 0.15, + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + if (iconPath != null) + SvgPicture.asset( + iconPath, + height: iconHeight ?? 35, + width: iconWidth ?? 35, + ), + if (title != null) + Padding( + padding: const EdgeInsets.only(top: 8.0), + child: Text( + title, + style: Theme.of(context) + .textTheme + .headlineLarge! + .copyWith(fontSize: 20, fontWeight: FontWeight.w400, color: Colors.black), + ), + ), Padding( padding: const EdgeInsets.only(top: 8.0), child: Text( - title, - style: Theme.of(context).textTheme.headlineLarge!.copyWith( - fontSize: 20, - fontWeight: FontWeight.w400, - color: Colors.black), + message, + style: Theme.of(context).textTheme.bodyMedium!.copyWith(color: Colors.black), + textAlign: TextAlign.center, ), ), - Padding( - padding: const EdgeInsets.only(top: 8.0), - child: Text( - message, - style: Theme.of(context) - .textTheme - .bodyMedium! - .copyWith(color: Colors.black), - textAlign: TextAlign.center, - ), - ), - if(widget!=null) - Expanded(child:widget) - ], - ), - ), - actionsAlignment: MainAxisAlignment.center, - actions: [ - TextButton( - onPressed: onOkPressed ?? () => Navigator.of(context).pop(), - child: Text( - 'OK', - style: Theme.of(context).textTheme.bodySmall!.copyWith( - fontWeight: FontWeight.w400, - color: ColorsManager.blackColor, - fontSize: 16), + if (widget != null) Expanded(child: widget) + ], ), ), - ], - ); + actionsAlignment: MainAxisAlignment.center, + actions: actions); }, ); } diff --git a/lib/pages/device_managment/all_devices/bloc/device_managment_bloc.dart b/lib/pages/device_managment/all_devices/bloc/device_managment_bloc.dart index c11e05e1..3f2e9f2a 100644 --- a/lib/pages/device_managment/all_devices/bloc/device_managment_bloc.dart +++ b/lib/pages/device_managment/all_devices/bloc/device_managment_bloc.dart @@ -6,14 +6,14 @@ import 'package:syncrow_web/services/devices_mang_api.dart'; part 'device_managment_event.dart'; part 'device_managment_state.dart'; -class DeviceManagementBloc - extends Bloc { +class DeviceManagementBloc extends Bloc { int _selectedIndex = 0; List _devices = []; int _onlineCount = 0; int _offlineCount = 0; int _lowBatteryCount = 0; List _selectedDevices = []; + String productName = ''; DeviceManagementBloc() : super(DeviceManagementInitial()) { on(_onFetchDevices); @@ -23,8 +23,7 @@ class DeviceManagementBloc on(_onSelectDevice); } - Future _onFetchDevices( - FetchDevices event, Emitter emit) async { + Future _onFetchDevices(FetchDevices event, Emitter emit) async { emit(DeviceManagementLoading()); try { final devices = await DevicesManagementApi().fetchDevices(); @@ -44,8 +43,7 @@ class DeviceManagementBloc } } - void _onFilterDevices( - FilterDevices event, Emitter emit) { + void _onFilterDevices(FilterDevices event, Emitter emit) async { if (_devices.isNotEmpty) { final filteredDevices = _devices.where((device) { switch (event.filter) { @@ -65,20 +63,20 @@ class DeviceManagementBloc onlineCount: _onlineCount, offlineCount: _offlineCount, lowBatteryCount: _lowBatteryCount, - selectedDevice: - _selectedDevices.isNotEmpty ? _selectedDevices.first : null, + selectedDevice: _selectedDevices.isNotEmpty ? _selectedDevices.first : null, )); + if (productName.isNotEmpty) { + add(SearchDevices(productName: productName)); + } } } - void _onSelectedFilterChanged( - SelectedFilterChanged event, Emitter emit) { + void _onSelectedFilterChanged(SelectedFilterChanged event, Emitter emit) { _selectedIndex = event.selectedIndex; add(FilterDevices(_getFilterFromIndex(_selectedIndex))); } - void _onSelectDevice( - SelectDevice event, Emitter emit) { + void _onSelectDevice(SelectDevice event, Emitter emit) { final selectedUuid = event.selectedDevice.uuid; if (_selectedDevices.any((device) => device.uuid == selectedUuid)) { @@ -113,10 +111,8 @@ class DeviceManagementBloc void _calculateDeviceCounts() { _onlineCount = _devices.where((device) => device.online == true).length; _offlineCount = _devices.where((device) => device.online == false).length; - _lowBatteryCount = _devices - .where((device) => - device.batteryLevel != null && device.batteryLevel! < 20) - .length; + _lowBatteryCount = + _devices.where((device) => device.batteryLevel != null && device.batteryLevel! < 20).length; } String _getFilterFromIndex(int index) { @@ -132,18 +128,20 @@ class DeviceManagementBloc } } - void _onSearchDevices( - SearchDevices event, Emitter emit) { + void _onSearchDevices(SearchDevices event, Emitter emit) { // If the search fields are all empty, restore the last filtered devices if ((event.community == null || event.community!.isEmpty) && (event.unitName == null || event.unitName!.isEmpty) && (event.productName == null || event.productName!.isEmpty)) { + productName = ''; // If the current state is filtered, re-emit the filtered state if (state is DeviceManagementFiltered) { add(FilterDevices(_getFilterFromIndex(_selectedIndex))); } } + productName = event.productName ?? ''; + List devicesToSearch = _devices; if (state is DeviceManagementFiltered) { @@ -157,32 +155,19 @@ class DeviceManagementBloc final filteredDevices = devicesToSearch.where((device) { final matchesCommunity = event.community == null || event.community!.isEmpty || - (device.room?.name - ?.toLowerCase() - .contains(event.community!.toLowerCase()) ?? - false); + (device.room?.name?.toLowerCase().contains(event.community!.toLowerCase()) ?? false); final matchesUnit = event.unitName == null || event.unitName!.isEmpty || - (device.unit?.name - ?.toLowerCase() - .contains(event.unitName!.toLowerCase()) ?? - false); + (device.unit?.name?.toLowerCase().contains(event.unitName!.toLowerCase()) ?? false); final matchesProductName = event.productName == null || event.productName!.isEmpty || - (device.name - ?.toLowerCase() - .contains(event.productName!.toLowerCase()) ?? - false); + (device.name?.toLowerCase().contains(event.productName!.toLowerCase()) ?? false); final matchesDeviceName = event.productName == null || event.productName!.isEmpty || - (device.categoryName - ?.toLowerCase() - .contains(event.productName!.toLowerCase()) ?? + (device.categoryName?.toLowerCase().contains(event.productName!.toLowerCase()) ?? false); - return matchesCommunity && - matchesUnit && - (matchesProductName || matchesDeviceName); + return matchesCommunity && matchesUnit && (matchesProductName || matchesDeviceName); }).toList(); emit(DeviceManagementFiltered( diff --git a/lib/web_layout/web_app_bar.dart b/lib/web_layout/web_app_bar.dart index b3236aae..1052a23d 100644 --- a/lib/web_layout/web_app_bar.dart +++ b/lib/web_layout/web_app_bar.dart @@ -1,8 +1,13 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:go_router/go_router.dart'; +import 'package:syncrow_web/pages/auth/bloc/auth_bloc.dart'; +import 'package:syncrow_web/pages/common/buttons/default_button.dart'; +import 'package:syncrow_web/pages/common/custom_dialog.dart'; import 'package:syncrow_web/pages/home/bloc/home_bloc.dart'; import 'package:syncrow_web/pages/home/bloc/home_state.dart'; import 'package:syncrow_web/utils/color_manager.dart'; +import 'package:syncrow_web/utils/constants/routes_const.dart'; import 'package:syncrow_web/utils/helpers/responsice_layout_helper/responsive_layout_helper.dart'; class WebAppBar extends StatefulWidget { @@ -128,6 +133,57 @@ class _WebAppBarState extends State with HelperResponsiveLayout { '${user.firstName} ${user.lastName}', style: Theme.of(context).textTheme.bodyLarge, ), + const SizedBox( + width: 10, + ), + GestureDetector( + onTap: () { + showCustomDialog( + context: context, + barrierDismissible: true, + title: 'Logout', + message: 'Are you sure you want to logout?', + actions: [ + GestureDetector( + onTap: () { + AuthBloc.logout(); + context.go(RoutesConst.auth); + }, + child: DefaultButton( + child: Text( + 'Ok', + style: Theme.of(context) + .textTheme + .bodyMedium! + .copyWith(fontSize: 12, color: Colors.white), + ), + ), + ), + const SizedBox( + height: 10, + ), + GestureDetector( + onTap: () { + context.pop(); + }, + child: DefaultButton( + child: Text( + 'Cancel', + style: Theme.of(context) + .textTheme + .bodyMedium! + .copyWith(fontSize: 12, color: Colors.white), + ), + ), + ), + ], + ); + }, + child: const Icon( + Icons.logout, + color: ColorsManager.whiteColors, + ), + ) ], ), ], From 67ea7af5950cc43e8927a6e621412f97ab4cc1af Mon Sep 17 00:00:00 2001 From: Abdullah Alassaf Date: Thu, 5 Sep 2024 16:39:42 +0300 Subject: [PATCH 9/9] Improved the search for device in device management --- README.md | 8 ++++++++ .../all_devices/bloc/device_managment_bloc.dart | 16 ++++++---------- 2 files changed, 14 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index 301f90fd..e6fef581 100644 --- a/README.md +++ b/README.md @@ -14,3 +14,11 @@ A few resources to get you started if this is your first Flutter project: For help getting started with Flutter development, view the [online documentation](https://docs.flutter.dev/), which offers tutorials, samples, guidance on mobile development, and a full API reference. + + +## USEFUL COMMANDS + +Run on chrome: flutter run -d chrome --dart-define=FLAVOR='ENV_NAME' + +Build: flutter build web --release --dart-define=FLAVOR='ENV_NAME' + diff --git a/lib/pages/device_managment/all_devices/bloc/device_managment_bloc.dart b/lib/pages/device_managment/all_devices/bloc/device_managment_bloc.dart index 3f2e9f2a..381c7969 100644 --- a/lib/pages/device_managment/all_devices/bloc/device_managment_bloc.dart +++ b/lib/pages/device_managment/all_devices/bloc/device_managment_bloc.dart @@ -1,8 +1,7 @@ -import 'package:bloc/bloc.dart'; import 'package:equatable/equatable.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:syncrow_web/pages/device_managment/all_devices/models/devices_model.dart'; import 'package:syncrow_web/services/devices_mang_api.dart'; - part 'device_managment_event.dart'; part 'device_managment_state.dart'; @@ -13,6 +12,7 @@ class DeviceManagementBloc extends Bloc _selectedDevices = []; + List _filteredDevices = []; String productName = ''; DeviceManagementBloc() : super(DeviceManagementInitial()) { @@ -29,6 +29,7 @@ class DeviceManagementBloc extends Bloc emit) async { if (_devices.isNotEmpty) { - final filteredDevices = _devices.where((device) { + _filteredDevices = _devices.where((device) { switch (event.filter) { case 'Online': return device.online == true; @@ -58,7 +59,7 @@ class DeviceManagementBloc extends Bloc devicesToSearch = _devices; - - if (state is DeviceManagementFiltered) { - devicesToSearch = (state as DeviceManagementFiltered).filteredDevices; - } + List devicesToSearch = _filteredDevices; if (devicesToSearch.isNotEmpty) { _selectedDevices.clear();