From 1d226742e6fb1f64a6d340ae7bfca8defd1dd43f Mon Sep 17 00:00:00 2001 From: mohammad Date: Thu, 8 Aug 2024 16:54:02 +0300 Subject: [PATCH] get user info and access management page --- lib/main.dart | 12 +- .../access_management/access_management.dart | 142 +++++++++++++++ lib/pages/auth/model/user_model.dart | 19 +- lib/pages/home/bloc/home_bloc.dart | 96 +++++++++- lib/pages/home/bloc/home_state.dart | 6 + .../home/home_model/home_item_model.dart | 22 +++ lib/pages/home/view/home_page_web.dart | 164 +++++++----------- lib/services/home_api.dart | 18 ++ lib/utils/color_manager.dart | 1 + lib/utils/constants/api_const.dart | 1 + lib/web_layout/web_app_bar.dart | 10 +- pubspec.lock | 8 + pubspec.yaml | 1 + 13 files changed, 385 insertions(+), 115 deletions(-) create mode 100644 lib/pages/access_management/access_management.dart create mode 100644 lib/pages/home/home_model/home_item_model.dart create mode 100644 lib/services/home_api.dart diff --git a/lib/main.dart b/lib/main.dart index d52c6541..14d48c3e 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,7 +1,9 @@ import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:syncrow_web/pages/auth/bloc/auth_bloc.dart'; import 'package:syncrow_web/pages/auth/view/login_page.dart'; +import 'package:syncrow_web/pages/home/bloc/home_bloc.dart'; import 'package:syncrow_web/pages/home/view/home_page.dart'; import 'package:syncrow_web/services/locator.dart'; import 'package:syncrow_web/utils/color_manager.dart'; @@ -23,7 +25,13 @@ class MyApp extends StatelessWidget { }); @override Widget build(BuildContext context) { - return MaterialApp( + return MultiBlocProvider( + providers: [ + BlocProvider(create: (context) => HomeBloc()), + ], + child: + + MaterialApp( debugShowCheckedModeBanner: false, // Hide debug banner scrollBehavior: const MaterialScrollBehavior().copyWith( dragDevices: { @@ -51,6 +59,6 @@ class MyApp extends StatelessWidget { useMaterial3: true, // Enable Material 3 ), home: isLoggedIn == 'Success' ? const HomePage() : const LoginPage(), - ); + )); } } diff --git a/lib/pages/access_management/access_management.dart b/lib/pages/access_management/access_management.dart new file mode 100644 index 00000000..f7fee005 --- /dev/null +++ b/lib/pages/access_management/access_management.dart @@ -0,0 +1,142 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_svg/svg.dart'; +import 'package:syncrow_web/utils/color_manager.dart'; +import 'package:syncrow_web/utils/constants/assets.dart'; +import 'package:syncrow_web/web_layout/web_scaffold.dart'; + +class AccessManagementPage extends StatelessWidget { + const AccessManagementPage({super.key}); + + @override + Widget build(BuildContext context) { + Size size = MediaQuery.of(context).size; + + return WebScaffold( + enableMenuSideba: false, + appBarTitle: Row( + children: [ + Text( + 'Access Management', + style: Theme.of(context).textTheme.headlineLarge, + ) + ], + ), + appBarBody: [ + Text( + 'Physical Access', + style: Theme.of(context) + .textTheme + .headlineMedium! + .copyWith(color: Colors.white), + ), + Text( + 'App Access', + style: Theme.of(context) + .textTheme + .headlineMedium! + .copyWith(color: Colors.white), + ) + ], + scaffoldBody: Container( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Container( + width:size.width*0.3, + height: size.height*0.05, + decoration: BoxDecoration( + boxShadow: [ + BoxShadow( + color: Colors.grey.withOpacity(0.5), + spreadRadius: 5, + blurRadius: 7, + offset: Offset(0, 3), // changes position of shadow + ), + ], + color: ColorsManager.boxColor, + borderRadius: BorderRadius.all(Radius.circular(10))), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceAround, + children: [ + Text('All'), + Text('To Be Effective (0)'), + Text('Effective (0)'), + Text('Expired'), + ], + ), + ), + SizedBox(height: 10,), + Row( + children: [ + Container( + width:size.width*0.08, + height: size.height*0.05, + decoration: BoxDecoration( + boxShadow: [ + BoxShadow( + color: Colors.grey.withOpacity(0.5), + spreadRadius: 5, + blurRadius: 7, + offset: Offset(0, 3), // changes position of shadow + ), + ], + color: ColorsManager.boxColor, + borderRadius: BorderRadius.all(Radius.circular(10))), + child: TextFormField() + ), + Container( + width:size.width*0.08, + height: size.height*0.05, + decoration: BoxDecoration( + boxShadow: [ + BoxShadow( + color: Colors.grey.withOpacity(0.5), + spreadRadius: 5, + blurRadius: 7, + offset: Offset(0, 3), // changes position of shadow + ), + ], + color: ColorsManager.boxColor, + borderRadius: BorderRadius.all(Radius.circular(10))), + child: TextFormField() + ), + Container( + width:size.width*0.08, + height: size.height*0.05, + decoration: BoxDecoration( + boxShadow: [ + BoxShadow( + color: Colors.grey.withOpacity(0.5), + spreadRadius: 5, + blurRadius: 7, + offset: Offset(0, 3), // changes position of shadow + ), + ], + color: ColorsManager.boxColor, + borderRadius: BorderRadius.all(Radius.circular(10))), + child: TextFormField() + ), + Container( + width:size.width*0.08, + height: size.height*0.05, + decoration: BoxDecoration( + boxShadow: [ + BoxShadow( + color: Colors.grey.withOpacity(0.5), + spreadRadius: 5, + blurRadius: 7, + offset: Offset(0, 3), // changes position of shadow + ), + ], + color: ColorsManager.boxColor, + borderRadius: BorderRadius.all(Radius.circular(10))), + child: TextFormField() + ) + ], + ), + + ], + ), + )); + } +} diff --git a/lib/pages/auth/model/user_model.dart b/lib/pages/auth/model/user_model.dart index 674eee9a..cbae385a 100644 --- a/lib/pages/auth/model/user_model.dart +++ b/lib/pages/auth/model/user_model.dart @@ -6,19 +6,17 @@ class UserModel { static String userUuidKey = 'userUuid'; final String? uuid; final String? email; - final String? name; + final String? firstName; + final String? lastName; final String? photoUrl; - final String? phoneNumber; - final bool? isEmailVerified; - final bool? isAgreementAccepted; - UserModel({ required this.uuid, required this.email, - required this.name, + required this.firstName, + required this.lastName, required this.photoUrl, required this.phoneNumber, required this.isEmailVerified, @@ -29,7 +27,8 @@ class UserModel { return UserModel( uuid: json['id'], email: json['email'], - name: json['name'], + firstName: json['firstName'], + lastName: json['lastName'], photoUrl: json['photoUrl'], phoneNumber: json['phoneNumber'], isEmailVerified: json['isEmailVerified'], @@ -46,7 +45,8 @@ class UserModel { return UserModel( uuid: tempJson['uuid'].toString(), email: tempJson['email'], - name: null, + firstName: null, + lastName: null, photoUrl: null, phoneNumber: null, isEmailVerified: null, @@ -58,7 +58,8 @@ class UserModel { return { 'id': uuid, 'email': email, - 'name': name, + 'firstName': firstName, + 'lastName': lastName, 'photoUrl': photoUrl, 'phoneNumber': phoneNumber, 'isEmailVerified': isEmailVerified, diff --git a/lib/pages/home/bloc/home_bloc.dart b/lib/pages/home/bloc/home_bloc.dart index a1475532..f261c39a 100644 --- a/lib/pages/home/bloc/home_bloc.dart +++ b/lib/pages/home/bloc/home_bloc.dart @@ -1,23 +1,34 @@ +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:flutter_secure_storage/flutter_secure_storage.dart'; import 'package:graphview/GraphView.dart'; +import 'package:syncrow_web/pages/access_management/access_management.dart'; +import 'package:syncrow_web/pages/auth/model/user_model.dart'; import 'package:syncrow_web/pages/home/bloc/home_event.dart'; import 'package:syncrow_web/pages/home/bloc/home_state.dart'; +import 'package:syncrow_web/pages/home/home_model/home_item_model.dart'; +import 'package:syncrow_web/services/home_api.dart'; +import 'package:syncrow_web/utils/color_manager.dart'; +import 'package:syncrow_web/utils/constants/assets.dart'; class HomeBloc extends Bloc { final Graph graph = Graph()..isTree = true; final BuchheimWalkerConfiguration builder = BuchheimWalkerConfiguration(); List sourcesList = []; List destinationsList = []; + static UserModel? user; HomeBloc() : super((HomeInitial())) { on(_createNode); + fetchUserInfo(); + } void _createNode(CreateNewNode event, Emitter emit) async { emit(HomeInitial()); sourcesList.add(event.sourceNode); destinationsList.add(event.destinationNode); - for (int i = 0; i < sourcesList.length; i++) { graph.addEdge(sourcesList[i], destinationsList[i]); } @@ -30,4 +41,87 @@ class HomeBloc extends Bloc { emit(HomeUpdateTree(graph: graph, builder: builder)); } + + + + Future fetchUserInfo() async { + try { + var uuid = + await const FlutterSecureStorage().read(key: UserModel.userUuidKey); + user = await HomeApi().fetchUserInfo(uuid); + emit(HomeUserInfoLoaded(user!)); // Emit state after fetching user info + } catch (e) { + return; + } + } + + List homeItems = [ + HomeItemModel( + title: 'Access', + icon: Assets.accessIcon, + active: true, + onPress: (context) { + Navigator.of(context).push( + MaterialPageRoute(builder: (context) => AccessManagementPage()), + ); + }, + color: null, + ), + HomeItemModel( + title: 'Space\nManagement', + icon: Assets.spaseManagementIcon, + active: true, + onPress: (context) { + }, + color: ColorsManager.primaryColor, + ), + HomeItemModel( + title: 'Devices', + icon: Assets.devicesIcon, + active: true, + onPress: (context) { + }, + color: ColorsManager.primaryColor, + ), + HomeItemModel( + title: 'Move in', + icon: Assets.moveinIcon, + active: false, + onPress: (context) { + }, + color: ColorsManager.primaryColor, + ), + HomeItemModel( + title: 'Construction', + icon: Assets.constructionIcon, + active: false, + onPress: (context) { + }, + color: ColorsManager.primaryColor, + ), + HomeItemModel( + title: 'Energy', + icon: Assets.energyIcon, + active: false, + onPress: (context) { + }, + color: ColorsManager.slidingBlueColor.withOpacity(0.2), + ), + HomeItemModel( + title: 'Integrations', + icon: Assets.integrationsIcon, + active: false, + onPress: (context) { + }, + color: ColorsManager.slidingBlueColor.withOpacity(0.2), + ), + HomeItemModel( + title: 'Asset', + icon: Assets.assetIcon, + active: false, + onPress: (context) { + }, + color: ColorsManager.slidingBlueColor.withOpacity(0.2), + ), + ]; } diff --git a/lib/pages/home/bloc/home_state.dart b/lib/pages/home/bloc/home_state.dart index 10c50486..7a1be0be 100644 --- a/lib/pages/home/bloc/home_state.dart +++ b/lib/pages/home/bloc/home_state.dart @@ -1,5 +1,6 @@ import 'package:equatable/equatable.dart'; import 'package:graphview/GraphView.dart'; +import 'package:syncrow_web/pages/auth/model/user_model.dart'; abstract class HomeState extends Equatable { const HomeState(); @@ -24,3 +25,8 @@ class HomeUpdateTree extends HomeState { @override List get props => [graph, builder]; } +class HomeUserInfoLoaded extends HomeState { + final UserModel user; + + HomeUserInfoLoaded(this.user); +} diff --git a/lib/pages/home/home_model/home_item_model.dart b/lib/pages/home/home_model/home_item_model.dart new file mode 100644 index 00000000..5626cd3f --- /dev/null +++ b/lib/pages/home/home_model/home_item_model.dart @@ -0,0 +1,22 @@ + + + + +import 'package:flutter/cupertino.dart'; + +class HomeItemModel { + final String? title; + final String? icon; + final Color? color; + final bool? active; + final void Function(BuildContext context) onPress; + + + HomeItemModel({ + this.title, + this.icon, + this.color, + this.active, + required this.onPress, + }); +} \ No newline at end of file diff --git a/lib/pages/home/view/home_page_web.dart b/lib/pages/home/view/home_page_web.dart index f7c4e988..0124acad 100644 --- a/lib/pages/home/view/home_page_web.dart +++ b/lib/pages/home/view/home_page_web.dart @@ -7,113 +7,75 @@ import 'package:syncrow_web/utils/color_manager.dart'; import 'package:syncrow_web/utils/constants/assets.dart'; import 'package:syncrow_web/web_layout/web_scaffold.dart'; +import '../bloc/home_state.dart'; + class HomeWebPage extends StatelessWidget { HomeWebPage({super.key}); @override Widget build(BuildContext context) { Size size = MediaQuery.of(context).size; return WebScaffold( - enableMenuSideba:false , - appBarTitle: Row( - children: [ - SvgPicture.asset( - Assets.loginLogo, - width: 150, - ), - ], - ), - scaffoldBody: BlocProvider( - create: (context) => HomeBloc(), - child: SizedBox( - height: size.height, - width: size.width, - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - SizedBox(height: size.height * 0.1), - const Text( - 'ACCESS YOUR APPS', - style: TextStyle(fontSize: 40, fontWeight: FontWeight.w700), - ), - const SizedBox(height: 30), - Expanded( - flex: 4, - child: SizedBox( - height: size.height * 0.6, - width: size.width * 0.68, - child: GridView.builder( - itemCount: 8, - gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount( - crossAxisCount: 4, - crossAxisSpacing: 20.0, - mainAxisSpacing: 20.0, - childAspectRatio: 1.5, - ), - itemBuilder: (context, index) { - return HomeCard( - index:index, - active:ceilingSensorButtons[index]['active'], - name: ceilingSensorButtons[index]['title'], - img:ceilingSensorButtons[index]['icon'] , - onTap: () { - - },); - }, - ), - ), - ), - ], - ), + enableMenuSideba: false, + appBarTitle: Row( + children: [ + SvgPicture.asset( + Assets.loginLogo, + width: 150, + ), + ], ), - ), - ); + scaffoldBody: BlocProvider( + create: (context) => HomeBloc(), + child: BlocConsumer( + listener: (BuildContext context, state) {}, + builder: (context, state) { + final homeBloc = BlocProvider.of(context); + return SizedBox( + height: size.height, + width: size.width, + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + SizedBox(height: size.height * 0.1), + Text( + 'ACCESS YOUR APPS', + style: Theme.of(context) + .textTheme.headlineLarge! + .copyWith(color: Colors.black, fontSize: 40), + ), + const SizedBox(height: 30), + Expanded( + flex: 4, + child: SizedBox( + height: size.height * 0.6, + width: size.width * 0.68, + child: GridView.builder( + itemCount: 8, + gridDelegate: + const SliverGridDelegateWithFixedCrossAxisCount( + crossAxisCount: 4, + crossAxisSpacing: 20.0, + mainAxisSpacing: 20.0, + childAspectRatio: 1.5, + ), + itemBuilder: (context, index) { + return HomeCard( + index: index, + active: homeBloc.homeItems[index].active!, + name: homeBloc.homeItems[index].title!, + img: homeBloc.homeItems[index].icon!, + onTap: () => homeBloc.homeItems[index].onPress(context), + ); + }, + ), + ), + ), + ], + ), + ); + }, + ), + )); } - - dynamic ceilingSensorButtons = - [ - { - 'title': 'Access', - 'icon': Assets.accessIcon, - 'active': true, - }, - { - 'title': 'Space\nManagement', - 'icon': Assets.spaseManagementIcon, - 'color': ColorsManager.primaryColor, - 'active': true, - }, - { - 'title': 'Devices', - 'icon':Assets.devicesIcon, - 'active': true, - }, - { - 'title': 'Move in', - 'icon': Assets.moveinIcon, - 'active': false, - }, - { - 'title': 'Construction', - 'icon': Assets.constructionIcon, - 'active': false, - }, - { - 'title': 'Energy', - 'icon': Assets.energyIcon, - 'color': ColorsManager.slidingBlueColor.withOpacity(0.2), - 'active': false, - }, - { - 'title': 'Integrations', - 'icon': Assets.integrationsIcon, - 'color': ColorsManager.slidingBlueColor.withOpacity(0.2), - 'active': false, - }, { - 'title': 'Asset', - 'icon': Assets.assetIcon, - 'color': ColorsManager.slidingBlueColor.withOpacity(0.2), - 'active': false, - }, - ]; } diff --git a/lib/services/home_api.dart b/lib/services/home_api.dart new file mode 100644 index 00000000..29ad30ab --- /dev/null +++ b/lib/services/home_api.dart @@ -0,0 +1,18 @@ + import 'package:syncrow_web/pages/auth/model/user_model.dart'; +import 'package:syncrow_web/services/api/http_service.dart'; +import 'package:syncrow_web/utils/constants/api_const.dart'; + +class HomeApi{ + + Future fetchUserInfo(userId) async { + final response = await HTTPService().get( + path: ApiEndpoints.getUser.replaceAll('{userUuid}', userId!), + showServerMessage: true, + expectedResponseModel: (json) { + print('user=$json'); + return UserModel.fromJson(json); + } + ); + return response; + } + } \ No newline at end of file diff --git a/lib/utils/color_manager.dart b/lib/utils/color_manager.dart index 46cb4383..492e5f8a 100644 --- a/lib/utils/color_manager.dart +++ b/lib/utils/color_manager.dart @@ -29,4 +29,5 @@ abstract class ColorsManager { static const Color textGray = Color(0xffD5D5D5); static const Color btnColor = Color(0xFF00008B); static const Color blueColor = Color(0xFF0036E6); + static const Color boxColor = Color(0xFFF5F6F7); } diff --git a/lib/utils/constants/api_const.dart b/lib/utils/constants/api_const.dart index 855d74e5..4eb6ceed 100644 --- a/lib/utils/constants/api_const.dart +++ b/lib/utils/constants/api_const.dart @@ -9,4 +9,5 @@ abstract class ApiEndpoints { static const String sendOtp = '$baseUrl/authentication/user/send-otp'; static const String verifyOtp = '$baseUrl/authentication/user/verify-otp'; static const String getRegion = '$baseUrl/region'; + static const String getUser = '$baseUrl/user/{userUuid}'; } diff --git a/lib/web_layout/web_app_bar.dart b/lib/web_layout/web_app_bar.dart index 4163e168..f9316f05 100644 --- a/lib/web_layout/web_app_bar.dart +++ b/lib/web_layout/web_app_bar.dart @@ -1,4 +1,7 @@ import 'package:flutter/material.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_state.dart'; import 'package:syncrow_web/utils/color_manager.dart'; class WebAppBar extends StatelessWidget { @@ -8,7 +11,8 @@ class WebAppBar extends StatelessWidget { @override Widget build(BuildContext context) { - return Container( + return BlocBuilder(builder: (context, state) { + return Container( height: 120, decoration: const BoxDecoration(color: ColorsManager.secondaryColor), padding: const EdgeInsets.all(10), @@ -48,8 +52,9 @@ class WebAppBar extends StatelessWidget { const SizedBox( width: 10, ), + if(HomeBloc.user!=null) Text( - 'mohamamd alnemer ', + '${HomeBloc.user!.firstName.toString() ?? ''} ${HomeBloc.user!.lastName.toString() ?? ''} ', style: Theme.of(context).textTheme.bodyLarge, ), ], @@ -58,5 +63,6 @@ class WebAppBar extends StatelessWidget { ), ), ); + }); } } diff --git a/pubspec.lock b/pubspec.lock index ba0d48bf..d22f2d41 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -65,6 +65,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.0.8" + data_table_2: + dependency: "direct main" + description: + name: data_table_2 + sha256: f02ec9b24f44420816a87370ff4f4e533e15b274f6267e4c9a88a585ad1a0473 + url: "https://pub.dev" + source: hosted + version: "2.5.15" dio: dependency: "direct main" description: diff --git a/pubspec.yaml b/pubspec.yaml index 7f55b18e..b4c3f411 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -43,6 +43,7 @@ dependencies: get_it: ^7.6.7 flutter_secure_storage: ^9.2.2 shared_preferences: ^2.3.0 + data_table_2: ^2.5.15 dev_dependencies: flutter_test: