restrict_spaceMemberUser_and_change_SignUpModel

This commit is contained in:
mohammad
2025-01-23 18:35:01 +03:00
parent 827585815b
commit 790479effb
20 changed files with 643 additions and 322 deletions

View File

@ -7,10 +7,10 @@ import 'package:flutter_svg/flutter_svg.dart';
import 'package:onesignal_flutter/onesignal_flutter.dart';
import 'package:permission_handler/permission_handler.dart';
import 'package:share_plus/share_plus.dart';
import 'package:syncrow_app/features/app_layout/model/permission_model.dart';
import 'package:syncrow_app/features/app_layout/model/space_model.dart';
import 'package:syncrow_app/features/app_layout/view/widgets/app_bar_home_dropdown.dart';
import 'package:syncrow_app/features/auth/model/user_model.dart';
import 'package:syncrow_app/features/dashboard/view/dashboard_view.dart';
import 'package:syncrow_app/features/devices/bloc/devices_cubit.dart';
import 'package:syncrow_app/features/devices/model/subspace_model.dart';
import 'package:syncrow_app/features/devices/view/widgets/devices_view_body.dart';
@ -30,23 +30,30 @@ import 'package:syncrow_app/services/api/profile_api.dart';
import 'package:syncrow_app/services/api/spaces_api.dart';
import 'package:syncrow_app/utils/helpers/snack_bar.dart';
import 'package:syncrow_app/utils/resource_manager/color_manager.dart';
part 'home_state.dart';
class HomeCubit extends Cubit<HomeState> {
HomeCubit._() : super(HomeInitial()) {
// checkIfNotificationPermissionGranted();
fetchUserInfo();
fetchUserInfo().then(
(value) {
if (selectedSpace == null) {
fetchUnitsByUserId();
fetchPermissions();
// .then((value) {
// if (selectedSpace != null) {
// fetchRoomsByUnitId(selectedSpace!);
// }
// });
}
},
);
}
static UserModel? user;
List<PermissionModel>? permissionModel = [];
static HomeCubit? _instance;
static HomeCubit getInstance() {
// If an instance already exists, return it
@ -56,15 +63,87 @@ class HomeCubit extends Cubit<HomeState> {
Future fetchUserInfo() async {
try {
emit(HomeLoading());
var uuid =
await const FlutterSecureStorage().read(key: UserModel.userUuidKey);
user = await ProfileApi().fetchUserInfo(uuid);
emit(HomeUserInfoLoaded(user!)); // Emit state after fetching user info
emit(HomeUserInfoLoaded(user!));
} catch (e) {
return;
}
}
static bool manageSupSpace = false;
static bool manageScene = false;
static bool manageDeviceLocation = false;
static bool visitorPasswordManagement = false;
Future<void> fetchPermissions() async {
try {
emit(HomeLoading());
final response = await ProfileApi().fetchPermissions(user!.role!.uuid);
permissionModel = PermissionModel.fromJsonList(response);
hasViewPermission();
emit(PermissionsRoleLoaded(permissionModel!));
} catch (e) {
emit(HomeError(e.toString()));
}
}
void hasViewPermission() {
emit(HomeLoading());
manageSupSpace = hasPermission(
permissionModel!,
'SPACE_MANAGEMENT',
'MANAGE_SPACE',
'ASSIGN_USER_TO_SPACE',
);
manageScene = hasPermission(
permissionModel!,
'AUTOMATION_MANAGEMENT',
'MANAGE_SCENES',
'UPDATE',
);
manageDeviceLocation = hasPermission(
permissionModel!,
'DEVICE_MANAGEMENT',
'MANAGE_DEVICE',
'LOCATION_UPDATE',
);
visitorPasswordManagement = hasPermission(
permissionModel!,
'VISITOR_PASSWORD_MANAGEMENT',
'MANAGE_VISITOR_PASSWORD',
'VIEW',
);
emit(HomePermissionUpdated());
}
bool hasPermission(List<PermissionModel> permissions, String mainTitle,
String subTitle, String finalTitle) {
try {
final mainOption = permissions.firstWhere(
(perm) => perm.title == mainTitle,
);
final subOption = mainOption.subOptions.firstWhere(
(sub) => sub.title == subTitle,
);
if (subOption.subOptions == null) {
return false;
}
final finalOption = subOption.subOptions!.firstWhere(
(finalSub) => finalSub.title == finalTitle,
);
return finalOption.isChecked == true;
} catch (e) {
return false;
}
}
void emitSafe(HomeState newState) {
final cubit = this;
if (!cubit.isClosed) {
@ -89,7 +168,7 @@ class HomeCubit extends Cubit<HomeState> {
static HomeCubit get(context) => BlocProvider.of(context);
List<SpaceModel>? spaces;
List<SpaceModel>? spaces = [];
SpaceModel? selectedSpace;
@ -261,6 +340,7 @@ class HomeCubit extends Cubit<HomeState> {
emitSafe(GetSpacesLoading());
try {
spaces = await SpacesAPI.getSpacesByUserId();
emitSafe(GetSpacesSuccess(spaces!));
} catch (failure) {
emitSafe(GetSpacesError("No units found"));
return;
@ -298,11 +378,13 @@ class HomeCubit extends Cubit<HomeState> {
await const FlutterSecureStorage().read(key: UserModel.userUuidKey);
var res = await SpacesAPI.activationCodeSpace(
activationCode: activationCode, userUuid: uuid);
if (res['success'] == true) {
fetchUserInfo();
fetchUnitsByUserId();
}
emitSafe(GetSpacesSuccess(spaces!));
return res['success'];
} on DioException catch (e) {
final errorMessage = e.response?.data['error']['message'];
@ -378,6 +460,8 @@ class HomeCubit extends Cubit<HomeState> {
// ),
// onPressed: () {},
// ),
// HomeCubit.permissionModel!.scene!.visible == true
// ?
IconButton(
icon: const Icon(
Icons.add,
@ -413,7 +497,8 @@ class HomeCubit extends Cubit<HomeState> {
.read<CreateSceneBloc>()
.add(const ClearTabToRunSetting());
},
),
)
// : const SizedBox(),
// IconButton(
// icon: const Icon(
// Icons.more_vert,
@ -503,3 +588,28 @@ BottomNavigationBarItem defaultBottomNavBarItem(
label: label,
);
}
// class PermissionUtils {
// // Check if the "VIEW" permission exists in "MANAGE_SUBSPACE"
// static bool hasViewPermission(List<dynamic> permissions) {
// return _hasPermission(permissions, 'MANAGE_SUBSPACE', 'VIEW');
// }
// // Generalized permission checker
// static bool _hasPermission(
// List<dynamic> permissions, String mainTitle, String subTitle) {
// final mainOption = permissions.firstWhere(
// (perm) => perm['title'] == mainTitle,
// orElse: () => null,
// );
// if (mainOption != null) {
// final subOption = mainOption['subOptions'].firstWhere(
// (sub) => sub['title'] == subTitle,
// orElse: () => null,
// );
// return subOption != null && subOption['isChecked'] == true;
// }
// return false;
// }
// }

View File

@ -14,9 +14,9 @@ class HomeError extends HomeState {
}
class HomeSuccess extends HomeState {}
class ActivationSuccess extends HomeState {}
///specific states
//get spaces
class GetSpacesLoading extends HomeLoading {}
class GetSpacesSuccess extends HomeSuccess {
@ -64,10 +64,16 @@ class RoomSelected extends HomeState {
class RoomUnSelected extends HomeState {}
class NavChangePage extends HomeState {}
class HomePermissionUpdated extends HomeState {}
// Define new state classes
class HomeUserInfoLoaded extends HomeState {
final UserModel user;
HomeUserInfoLoaded(this.user);
}
class PermissionsRoleLoaded extends HomeState {
final List<PermissionModel> permissionModel;
PermissionsRoleLoaded(this.permissionModel);
}

View File

@ -0,0 +1,39 @@
class PermissionModel {
final String title;
final List<PermissionAttributes> subOptions;
PermissionModel({required this.title, required this.subOptions});
factory PermissionModel.fromJson(Map<String, dynamic> json) {
return PermissionModel(
title: json['title'],
subOptions: (json['subOptions'] as List)
.map((e) => PermissionAttributes.fromJson(e))
.toList(),
);
}
static List<PermissionModel> fromJsonList(List<dynamic> jsonList) {
return jsonList.map((json) => PermissionModel.fromJson(json)).toList();
}
}
class PermissionAttributes {
final String title;
final List<PermissionAttributes>? subOptions;
final bool? isChecked;
PermissionAttributes({required this.title, this.subOptions, this.isChecked});
factory PermissionAttributes.fromJson(Map<String, dynamic> json) {
return PermissionAttributes(
title: json['title'],
isChecked: json['isChecked'],
subOptions: json['subOptions'] != null
? (json['subOptions'] as List)
.map((e) => PermissionAttributes.fromJson(e))
.toList()
: null,
);
}
}

View File

@ -17,7 +17,10 @@ class AppLayout extends StatelessWidget {
child: BlocBuilder<HomeCubit, HomeState>(
builder: (context, state) {
return DefaultScaffold(
appBar: HomeCubit.getInstance().spaces != null ? const DefaultAppBar() : null,
appBar: HomeCubit.getInstance().spaces != null &&
HomeCubit.getInstance().spaces!.isNotEmpty
? const DefaultAppBar()
: null,
bottomNavBar: const DefaultNavBar(),
child: const AppBody(),
);

View File

@ -35,8 +35,10 @@ class DefaultAppBar extends StatelessWidget implements PreferredSizeWidget {
)
: null,
),
actions: HomeCubit.appBarActions[
HomeCubit.bottomNavItems[HomeCubit.pageIndex].label],
actions: HomeCubit.manageScene
? HomeCubit.appBarActions[
HomeCubit.bottomNavItems[HomeCubit.pageIndex].label]
: null,
));
},
);

View File

@ -221,6 +221,7 @@ class AuthCubit extends Cubit<AuthState> {
List<String> userFullName = fullName.split(' ');
response = await AuthenticationAPI.signUp(
model: SignUpModel(
hasAcceptedAppAgreement: true,
email: email.toLowerCase(),
password: signUpPassword,
firstName: userFullName[0],

View File

@ -3,10 +3,12 @@ class SignUpModel {
final String password;
final String firstName;
final String lastName;
final bool hasAcceptedAppAgreement;
SignUpModel(
{required this.email,
required this.password,
required this.hasAcceptedAppAgreement,
required this.firstName,
required this.lastName});
@ -15,7 +17,8 @@ class SignUpModel {
email: json['email'],
password: json['password'],
firstName: json['firstName'],
lastName: json['lastName']);
lastName: json['lastName'],
hasAcceptedAppAgreement: true);
}
Map<String, dynamic> toJson() {
@ -24,6 +27,7 @@ class SignUpModel {
'password': password,
'firstName': firstName,
'lastName': lastName,
"hasAcceptedAppAgreement": hasAcceptedAppAgreement
};
}
}

View File

@ -15,6 +15,11 @@ class UserModel {
final String? timeZone;
final String? regionUuid;
final bool? isAgreementAccepted;
final bool? hasAcceptedWebAgreement;
final DateTime? webAgreementAcceptedAt;
final bool? hasAcceptedAppAgreement;
final DateTime? appAgreementAcceptedAt;
final Role? role;
UserModel({
required this.uuid,
@ -28,7 +33,11 @@ class UserModel {
required this.isAgreementAccepted,
required this.regionName,
required this.timeZone,
// required this.role,
required this.hasAcceptedWebAgreement,
required this.webAgreementAcceptedAt,
required this.hasAcceptedAppAgreement,
required this.appAgreementAcceptedAt,
required this.role,
});
factory UserModel.fromJson(Map<String, dynamic> json) {
@ -44,6 +53,15 @@ class UserModel {
regionName: json['region']?['regionName'],
timeZone: json['timeZone']?['timeZoneOffset'],
regionUuid: json['region']?['uuid'],
hasAcceptedWebAgreement: json['hasAcceptedWebAgreement'],
webAgreementAcceptedAt: json['webAgreementAcceptedAt'] != null
? DateTime.parse(json['webAgreementAcceptedAt'])
: null,
hasAcceptedAppAgreement: json['hasAcceptedAppAgreement'],
appAgreementAcceptedAt: json['appAgreementAcceptedAt'] != null
? DateTime.parse(json['appAgreementAcceptedAt'])
: null,
role: json['role'] != null ? Role.fromJson(json['role']) : null,
);
}
@ -61,6 +79,15 @@ class UserModel {
regionUuid: null,
regionName: tempJson['region']?['regionName'],
timeZone: tempJson['timezone']?['timeZoneOffset'],
hasAcceptedWebAgreement: tempJson['hasAcceptedWebAgreement'],
webAgreementAcceptedAt: tempJson['webAgreementAcceptedAt'] != null
? DateTime.parse(tempJson['webAgreementAcceptedAt'])
: null,
hasAcceptedAppAgreement: tempJson['hasAcceptedAppAgreement'],
appAgreementAcceptedAt: tempJson['appAgreementAcceptedAt'] != null
? DateTime.parse(tempJson['appAgreementAcceptedAt'])
: null,
role: tempJson['role'] != null ? Role.fromJson(tempJson['role']) : null,
);
}
@ -85,6 +112,43 @@ class UserModel {
'isAgreementAccepted': isAgreementAccepted,
'regionName': regionName,
'timeZone': timeZone,
'hasAcceptedWebAgreement': hasAcceptedWebAgreement,
'webAgreementAcceptedAt': webAgreementAcceptedAt?.toIso8601String(),
'hasAcceptedAppAgreement': hasAcceptedAppAgreement,
'appAgreementAcceptedAt': appAgreementAcceptedAt?.toIso8601String(),
'role': role?.toJson(),
};
}
}
class Role {
final String? uuid;
final DateTime? createdAt;
final DateTime? updatedAt;
final String? type;
Role({
required this.uuid,
required this.createdAt,
required this.updatedAt,
required this.type,
});
factory Role.fromJson(Map<String, dynamic> json) {
return Role(
uuid: json['uuid'],
createdAt: json['createdAt'] != null ? DateTime.parse(json['createdAt']) : null,
updatedAt: json['updatedAt'] != null ? DateTime.parse(json['updatedAt']) : null,
type: json['type'],
);
}
Map<String, dynamic> toJson() {
return {
'uuid': uuid,
'createdAt': createdAt?.toIso8601String(),
'updatedAt': updatedAt?.toIso8601String(),
'type': type,
};
}
}

View File

@ -433,7 +433,9 @@ class ACsBloc extends Bloc<AcsEvent, AcsState> {
seconds = event.seconds;
final response = await DevicesAPI.controlDevice(
DeviceControlModel(
deviceId: acId, code: 'countdown_time', value: event.duration),
deviceId: acId,
code: 'countdown_time',
value: event.duration),
acId);
if (response['success'] ?? false) {

View File

@ -38,8 +38,10 @@ class SettingProfilePage extends StatelessWidget {
final _bloc = BlocProvider.of<DeviceSettingBloc>(context);
return state is DeviceSettingLoadingState
? const Center(
child:
DefaultContainer(width: 50, height: 50, child: CircularProgressIndicator()),
child: DefaultContainer(
width: 50,
height: 50,
child: CircularProgressIndicator()),
)
: RefreshIndicator(
onRefresh: () async {
@ -55,15 +57,18 @@ class SettingProfilePage extends StatelessWidget {
child: SvgPicture.asset(
Assets.sosHomeIcon,
fit: BoxFit.fitHeight,
height: MediaQuery.of(context).size.height * 0.13,
height:
MediaQuery.of(context).size.height * 0.13,
))
: CircleAvatar(
radius: 55,
backgroundColor: ColorsManager.graysColor,
child: ClipOval(
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment:
CrossAxisAlignment.center,
mainAxisAlignment:
MainAxisAlignment.center,
children: [
Center(
child: SvgPicture.asset(
@ -71,7 +76,10 @@ class SettingProfilePage extends StatelessWidget {
? Assets.fourSceneIcon
: Assets.sixSceneIcon,
fit: BoxFit.contain,
height: MediaQuery.of(context).size.height * 0.08,
height: MediaQuery.of(context)
.size
.height *
0.08,
),
),
],
@ -89,7 +97,8 @@ class SettingProfilePage extends StatelessWidget {
children: [
IntrinsicWidth(
child: ConstrainedBox(
constraints: const BoxConstraints(maxWidth: 200),
constraints:
const BoxConstraints(maxWidth: 200),
child: TextFormField(
maxLength: 30,
style: const TextStyle(
@ -122,7 +131,8 @@ class SettingProfilePage extends StatelessWidget {
Assets.sosEditProfile,
color: Colors.grey,
fit: BoxFit.contain,
height: MediaQuery.of(context).size.height * 0.02,
height: MediaQuery.of(context).size.height *
0.02,
),
),
),
@ -141,6 +151,7 @@ class SettingProfilePage extends StatelessWidget {
padding: const EdgeInsets.all(20),
child: InkWell(
onTap: () async {
if (HomeCubit.visitorPasswordManagement) {
bool? val = await Navigator.of(context).push(
MaterialPageRoute(
builder: (context) => LocationSettingPage(
@ -151,6 +162,7 @@ class SettingProfilePage extends StatelessWidget {
if (val != null && val == true) {
_bloc.add(const DeviceSettingInitialInfo());
}
}
},
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
@ -162,7 +174,8 @@ class SettingProfilePage extends StatelessWidget {
children: [
SizedBox(
child: BodyMedium(
text: _bloc.deviceInfo.subspace.subspaceName,
text: _bloc
.deviceInfo.subspace.subspaceName,
fontColor: ColorsManager.textGray,
),
),

View File

@ -1,5 +1,6 @@
import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:syncrow_app/features/app_layout/bloc/home_cubit.dart';
import 'package:syncrow_app/features/devices/view/widgets/smart_door/members_management_view.dart';
import 'package:syncrow_app/features/devices/view/widgets/smart_door/smart_linkage_view.dart';
import 'package:syncrow_app/features/devices/view/widgets/smart_door/temporary_password_page.dart';
@ -14,6 +15,8 @@ class DoorLockGrid extends StatelessWidget {
@override
Widget build(BuildContext context) {
final buttons = doorLockButtons(val: uuid);
return GridView.builder(
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
@ -23,14 +26,15 @@ class DoorLockGrid extends StatelessWidget {
crossAxisSpacing: 10,
childAspectRatio: 1.75 / 1,
),
itemCount: 4,
itemCount: buttons.length,
itemBuilder: (context, index) => DefaultContainer(
onTap: () {
//TODO: remove checking after adding the pages
doorLockButtons()[index]['page'] != null
? Navigator.of(context).push(
MaterialPageRoute(
builder: (context) => doorLockButtons(val: uuid)[index]['page'] as Widget,
builder: (context) =>
doorLockButtons(val: uuid)[index]['page'] as Widget,
),
)
: null;
@ -75,6 +79,7 @@ List<Map<String, dynamic>> doorLockButtons({val}) => [
'image': Assets.assetsIconsDoorlockAssetsMembersManagement,
'page': const MembersManagementView(),
},
if (HomeCubit.manageDeviceLocation)
{
'title': 'Temporary Password',
'image': Assets.assetsIconsDoorlockAssetsTemporaryPassword,

View File

@ -1,5 +1,15 @@
import 'dart:ui';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:syncrow_app/features/app_layout/bloc/home_cubit.dart';
import 'package:syncrow_app/features/menu/bloc/privacy_policy.dart';
import 'package:syncrow_app/features/menu/bloc/user_agreement.dart';
import 'package:syncrow_app/features/menu/view/widgets/join_home/join_home_view.dart';
import 'package:syncrow_app/features/menu/view/widgets/manage_home/manage_home_view.dart';
import 'package:syncrow_app/features/menu/view/widgets/securty/view/securty_view.dart';
import 'package:syncrow_app/generated/assets.dart';
import 'package:syncrow_app/services/api/profile_api.dart';
import 'package:syncrow_app/utils/resource_manager/color_manager.dart';
part 'menu_state.dart';
@ -32,4 +42,143 @@ class MenuCubit extends Cubit<MenuState> {
emit(MenuError(error.toString()));
}
}
List<Map<String, Object>> menuSections = [
//Home Management
{
'title': 'Home Management',
'color': ColorsManager.primaryColor,
'buttons': [
// {
// 'title': 'Create a Unit',
// 'Icon': Assets.assetsIconsMenuIconsHomeManagementIconsCreateHome,
// 'page': const CreateUnitView()
// },
{
'title': 'Join a Unit',
'Icon': Assets.assetsIconsMenuIconsHomeManagementIconsJoinAHome,
'page': const JoinHomeView()
},
if (HomeCubit.manageSupSpace)
{
'title': 'Manage Your Units',
'Icon':
Assets.assetsIconsMenuIconsHomeManagementIconsManageYourHome,
'page': const ManageHomeView()
},
],
},
//General Settings
// {
// 'title': 'General Settings',
// 'color': const Color(0xFF023DFE),
// 'buttons': [
// {
// 'title': 'Voice Assistant',
// 'Icon': Assets.assetsIconsMenuIconsGeneralSettingsIconsVoiceAssistant,
// 'page': null
// },
// {
// 'title': 'Temperature unit',
// 'Icon': Assets.assetsIconsMenuIconsGeneralSettingsIconsTemperatureUnit,
// 'page': null
// },
// {
// 'title': 'Touch tone on panel',
// 'Icon': Assets.assetsIconsMenuIconsGeneralSettingsIconsTouchTone,
// 'page': null
// },
// {
// 'title': 'Language',
// 'Icon': Assets.assetsIconsMenuIconsGeneralSettingsIconsLanguage,
// 'page': null
// },
// {
// 'title': 'Network Diagnosis',
// 'Icon': Assets.assetsIconsMenuIconsGeneralSettingsIconsNetworkDiagnosis,
// 'page': null
// },
// {
// 'title': 'Clear Cache',
// 'Icon': Assets.assetsIconsMenuIconsGeneralSettingsIconsClearCach,
// 'page': null
// },
// ],
// },
// //Messages Center
// {
// 'title': 'Messages Center',
// 'color': const Color(0xFF0088FF),
// 'buttons': [
// {
// 'title': 'Alerts',
// 'Icon': Assets.assetsIconsMenuIconsMessagesCenterIconsAlerts,
// 'page': null
// },
// {
// 'title': 'Messages',
// 'Icon': Assets.assetsIconsMenuIconsMessagesCenterIconsMessages,
// 'page': null
// },
// {
// 'title': 'FAQs',
// 'Icon': Assets.assetsIconsMenuIconsMessagesCenterIconsFAQs,
// 'page': null
// },
// {
// 'title': 'Help & Feedback',
// 'Icon': Assets.assetsIconsMenuIconsMessagesCenterIconsHelpAndFeedback,
// 'page': null
// },
// ],
// },
//Security And Privacy
{
'title': 'Security And Privacy',
'color': const Color(0xFF8AB9FF),
'buttons': [
{
'title': 'Security',
'Icon': Assets.assetsIconsMenuIconsSecurityAndPrivacyIconsSecurty,
'page': const SecurtyView()
},
// {
// 'title': 'Privacy',
// 'Icon': Assets.assetsIconsMenuIconsSecurityAndPrivacyIconsPrivacy,
// 'page': const PrivacyView()
// },
],
},
//Legal Information
{
'title': 'Legal Information',
'color': const Color(0xFF001B72),
'buttons': [
{
'title': 'About',
'Icon': Assets.assetsIconsMenuIconsLeagalInfoIconsAbout,
'page': null
},
{
'title': 'Privacy Policy',
'Icon': Assets.assetsIconsMenuIconsLeagalInfoIconsPrivacyPolicy,
'page': const PrivacyPolicy()
},
{
'title': 'User Agreement',
'Icon': Assets.assetsIconsMenuIconsLeagalInfoIconsUserAgreement,
'page': const UserAgreement()
},
],
},
];
Future<void> fetchMenuSections() async {
emit(MenuLoading());
try {
emit(MenuItemsLoaded(menuSections));
} catch (e) {
emit(MenuError(e.toString()));
}
}
}

View File

@ -11,6 +11,11 @@ class MenuLoaded extends MenuState {
MenuLoaded(this.userAgreementHtml);
}
class MenuItemsLoaded extends MenuState {
final List<Map<String, Object>> menuSections;
MenuItemsLoaded(this.menuSections);
}
class MenuError extends MenuState {
final String message;

View File

@ -1,43 +1,35 @@
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_dotenv/flutter_dotenv.dart';
import 'package:syncrow_app/features/auth/bloc/auth_cubit.dart';
import 'package:syncrow_app/features/menu/bloc/menu_cubit.dart';
import 'package:syncrow_app/features/menu/view/widgets/menu_list.dart';
import 'package:syncrow_app/features/menu/view/widgets/profile/profile_tab.dart';
import 'package:syncrow_app/features/shared_widgets/default_container.dart';
import 'package:syncrow_app/features/shared_widgets/text_widgets/body_large.dart';
import 'package:syncrow_app/features/shared_widgets/text_widgets/body_medium.dart';
import 'package:syncrow_app/utils/context_extension.dart';
import 'package:syncrow_app/utils/resource_manager/constants.dart';
class MenuView extends StatelessWidget {
const MenuView({super.key});
const MenuView({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return BlocProvider(
create: (BuildContext context) => MenuCubit(),
create: (BuildContext context) => MenuCubit()..fetchMenuSections(),
child: BlocBuilder<MenuCubit, MenuState>(
builder: (context, state) {
return BlocBuilder<AuthCubit, AuthState>(
builder: (context, state) {
builder: (context, menuState) {
if (menuState is MenuLoading) {
return const Center(child: CircularProgressIndicator());
} else if (menuState is MenuError) {
return Center(child: Text(menuState.message));
} else if (menuState is MenuItemsLoaded) {
final sections = menuState.menuSections;
return SingleChildScrollView(
physics: const BouncingScrollPhysics(),
child: Column(
children: [
const ProfileTab(),
for (var section in menuSections)
MenuList(
section: section,
),
const SizedBox(
height: 15,
),
BodyMedium(text: dotenv.env['ENV_NAME'] ?? ''),
const SizedBox(
height: 15,
),
for (var section in sections) MenuList(section: section),
const SizedBox(height: 15),
InkWell(
onTap: () {
AuthCubit.get(context).logout();
@ -58,12 +50,13 @@ class MenuView extends StatelessWidget {
),
],
),
)
),
],
),
);
},
);
}
// Fallback in case no states match
return const SizedBox.shrink();
},
),
);

View File

@ -36,17 +36,20 @@ class SceneItem extends StatelessWidget {
Widget build(BuildContext context) {
return DefaultContainer(
onTap: () {
context.read<SmartSceneSelectBloc>().add(const SmartSceneClearEvent());
if (HomeCubit.manageScene) {
context
.read<SmartSceneSelectBloc>()
.add(const SmartSceneClearEvent());
if (disablePlayButton == false) {
BlocProvider.of<CreateSceneBloc>(context)
.add(FetchSceneTasksEvent(sceneId: scene.id, isAutomation: false));
BlocProvider.of<CreateSceneBloc>(context).add(
FetchSceneTasksEvent(sceneId: scene.id, isAutomation: false));
/// the state to set the scene type must be after the fetch
BlocProvider.of<CreateSceneBloc>(context)
.add(const SceneTypeEvent(CreateSceneEnum.tabToRun));
} else {
BlocProvider.of<CreateSceneBloc>(context)
.add(FetchSceneTasksEvent(sceneId: scene.id, isAutomation: true));
BlocProvider.of<CreateSceneBloc>(context).add(
FetchSceneTasksEvent(sceneId: scene.id, isAutomation: true));
/// the state to set the scene type must be after the fetch
BlocProvider.of<CreateSceneBloc>(context)
@ -64,6 +67,7 @@ class SceneItem extends StatelessWidget {
sceneName: scene.name,
),
);
}
},
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
@ -79,8 +83,11 @@ class SceneItem extends StatelessWidget {
height: 32,
width: 32,
fit: BoxFit.fill,
errorBuilder: (context, error, stackTrace) =>
Image.asset(Assets.assetsIconsLogo, height: 32, width: 32, fit: BoxFit.fill),
errorBuilder: (context, error, stackTrace) => Image.asset(
Assets.assetsIconsLogo,
height: 32,
width: 32,
fit: BoxFit.fill),
),
if (disablePlayButton || scene.iconInBytes.isEmpty)
SvgPicture.asset(
@ -93,7 +100,9 @@ class SceneItem extends StatelessWidget {
? IconButton(
padding: EdgeInsets.zero,
onPressed: () {
context.read<SceneBloc>().add(SceneTrigger(scene.id, scene.name));
context
.read<SceneBloc>()
.add(SceneTrigger(scene.id, scene.name));
},
icon: isLoading
? const Center(
@ -115,10 +124,14 @@ class SceneItem extends StatelessWidget {
activeColor: ColorsManager.primaryColor,
value: scene.status == 'enable' ? true : false,
onChanged: (value) {
context.read<SceneBloc>().add(UpdateAutomationStatus(
automationStatusUpdate: AutomationStatusUpdate(
context.read<SceneBloc>().add(
UpdateAutomationStatus(
automationStatusUpdate:
AutomationStatusUpdate(
isEnable: value,
spaceUuid: HomeCubit.getInstance().selectedSpace!.id),
spaceUuid: HomeCubit.getInstance()
.selectedSpace!
.id),
automationId: scene.id));
},
),

View File

@ -16,7 +16,7 @@ class CreateUnitWidget extends StatelessWidget {
TextEditingController textEditingController = TextEditingController();
return BlocConsumer<HomeCubit, HomeState>(
listener: (context, state) {
if (state is ActivationError) {}
// if (state is ActivationError) {}
},
builder: (context, state) {
return SingleChildScrollView(
@ -96,7 +96,11 @@ class CreateUnitWidget extends StatelessWidget {
textEditingController.text)) {
CustomSnackBar.displaySnackBar(
'Done successfully');
Future.delayed(
const Duration(milliseconds: 500),
() {
Navigator.of(context).pop();
});
} else {
CustomSnackBar.displaySnackBar(
'Wrong code!');
@ -109,14 +113,14 @@ class CreateUnitWidget extends StatelessWidget {
],
),
),
state is ActivationError
? Text(
state.errMessage,
style: const TextStyle(
color: ColorsManager.red,
fontWeight: FontWeight.w400),
)
: const SizedBox()
// state is ActivationError
// ? Text(
// state.errMessage,
// style: const TextStyle(
// color: ColorsManager.red,
// fontWeight: FontWeight.w400),
// )
// : const SizedBox()
],
),
],

View File

@ -225,4 +225,7 @@ abstract class ApiEndpoints {
static const String getDeviceLogs = '/device/report-logs/{uuid}?code={code}';
static const String terms = '/terms';
static const String policy = '/policy';
static const String getPermission = '/permission/{roleUuid}';
///permission/roleUuid
}

View File

@ -88,7 +88,8 @@ class DevicesAPI {
static Future<Map<String, dynamic>> getDeviceStatus(String deviceId) async {
final response = await _httpService.get(
path: ApiEndpoints.deviceFunctionsStatus.replaceAll('{deviceUuid}', deviceId),
path: ApiEndpoints.deviceFunctionsStatus
.replaceAll('{deviceUuid}', deviceId),
showServerMessage: false,
expectedResponseModel: (json) {
return json;
@ -97,7 +98,8 @@ class DevicesAPI {
return response;
}
static Future<Map<String, dynamic>> getPowerClampStatus(String deviceId) async {
static Future<Map<String, dynamic>> getPowerClampStatus(
String deviceId) async {
final response = await _httpService.get(
path: ApiEndpoints.powerClamp.replaceAll('{powerClampUuid}', deviceId),
showServerMessage: false,
@ -109,7 +111,9 @@ class DevicesAPI {
}
static Future<Map<String, dynamic>> renamePass(
{required String name, required String doorLockUuid, required String passwordId}) async {
{required String name,
required String doorLockUuid,
required String passwordId}) async {
final response = await _httpService.put(
path: ApiEndpoints.renamePassword
.replaceAll('{doorLockUuid}', doorLockUuid)
@ -144,7 +148,8 @@ class DevicesAPI {
return response;
}
static Future getSceneBySwitchName({String? deviceId, String? switchName}) async {
static Future getSceneBySwitchName(
{String? deviceId, String? switchName}) async {
final response = await _httpService.get(
path: ApiEndpoints.fourSceneByName
.replaceAll('{deviceUuid}', deviceId!)
@ -165,7 +170,11 @@ class DevicesAPI {
final response = await _httpService.post(
path: ApiEndpoints.deviceScene.replaceAll('{deviceUuid}', deviceId!),
body: jsonEncode(
{"switchName": switchName, "sceneUuid": sceneUuid, "spaceUuid": spaceUuid},
{
"switchName": switchName,
"sceneUuid": sceneUuid,
"spaceUuid": spaceUuid
},
),
showServerMessage: false,
expectedResponseModel: (json) {
@ -185,7 +194,8 @@ class DevicesAPI {
return response;
}
static Future<List<DeviceModel>> getDeviceByGroupName(String unitId, String groupName) async {
static Future<List<DeviceModel>> getDeviceByGroupName(
String unitId, String groupName) async {
final response = await _httpService.get(
path: ApiEndpoints.devicesByGroupName
.replaceAll('{unitUuid}', unitId)
@ -230,7 +240,9 @@ class DevicesAPI {
if (json == null || json.isEmpty || json == []) {
return <DeviceModel>[];
}
return data.map<DeviceModel>((device) => DeviceModel.fromJson(device)).toList();
return data
.map<DeviceModel>((device) => DeviceModel.fromJson(device))
.toList();
},
);
@ -242,7 +254,8 @@ class DevicesAPI {
}
}
static Future<List<DeviceModel>> getDevicesByGatewayId(String gatewayId) async {
static Future<List<DeviceModel>> getDevicesByGatewayId(
String gatewayId) async {
final response = await _httpService.get(
path: ApiEndpoints.gatewayApi.replaceAll('{gatewayUuid}', gatewayId),
showServerMessage: false,
@ -264,7 +277,8 @@ class DevicesAPI {
String deviceId,
) async {
final response = await _httpService.get(
path: ApiEndpoints.getTemporaryPassword.replaceAll('{doorLockUuid}', deviceId),
path: ApiEndpoints.getTemporaryPassword
.replaceAll('{doorLockUuid}', deviceId),
showServerMessage: false,
expectedResponseModel: (json) {
return json;
@ -275,7 +289,8 @@ class DevicesAPI {
static Future getOneTimePasswords(String deviceId) async {
final response = await _httpService.get(
path: ApiEndpoints.getOneTimeTemporaryPassword.replaceAll('{doorLockUuid}', deviceId),
path: ApiEndpoints.getOneTimeTemporaryPassword
.replaceAll('{doorLockUuid}', deviceId),
showServerMessage: false,
expectedResponseModel: (json) {
return json;
@ -286,7 +301,8 @@ class DevicesAPI {
static Future getTimeLimitPasswords(String deviceId) async {
final response = await _httpService.get(
path: ApiEndpoints.getMultipleTimeTemporaryPassword.replaceAll('{doorLockUuid}', deviceId),
path: ApiEndpoints.getMultipleTimeTemporaryPassword
.replaceAll('{doorLockUuid}', deviceId),
showServerMessage: false,
expectedResponseModel: (json) {
return json;
@ -311,10 +327,12 @@ class DevicesAPI {
"invalidTime": invalidTime,
};
if (scheduleList != null) {
body["scheduleList"] = scheduleList.map((schedule) => schedule.toJson()).toList();
body["scheduleList"] =
scheduleList.map((schedule) => schedule.toJson()).toList();
}
final response = await _httpService.post(
path: ApiEndpoints.addTemporaryPassword.replaceAll('{doorLockUuid}', deviceId),
path: ApiEndpoints.addTemporaryPassword
.replaceAll('{doorLockUuid}', deviceId),
body: body,
showServerMessage: false,
expectedResponseModel: (json) => json,
@ -325,7 +343,8 @@ class DevicesAPI {
static Future generateOneTimePassword({deviceId}) async {
try {
final response = await _httpService.post(
path: ApiEndpoints.addOneTimeTemporaryPassword.replaceAll('{doorLockUuid}', deviceId),
path: ApiEndpoints.addOneTimeTemporaryPassword
.replaceAll('{doorLockUuid}', deviceId),
showServerMessage: false,
expectedResponseModel: (json) {
return json;
@ -337,10 +356,12 @@ class DevicesAPI {
}
}
static Future generateMultiTimePassword({deviceId, effectiveTime, invalidTime}) async {
static Future generateMultiTimePassword(
{deviceId, effectiveTime, invalidTime}) async {
try {
final response = await _httpService.post(
path: ApiEndpoints.addMultipleTimeTemporaryPassword.replaceAll('{doorLockUuid}', deviceId),
path: ApiEndpoints.addMultipleTimeTemporaryPassword
.replaceAll('{doorLockUuid}', deviceId),
showServerMessage: true,
body: {"effectiveTime": effectiveTime, "invalidTime": invalidTime},
expectedResponseModel: (json) {
@ -530,7 +551,9 @@ class DevicesAPI {
String code,
) async {
final response = await HTTPService().get(
path: ApiEndpoints.getDeviceLogs.replaceAll('{uuid}', uuid).replaceAll('{code}', code),
path: ApiEndpoints.getDeviceLogs
.replaceAll('{uuid}', uuid)
.replaceAll('{code}', code),
showServerMessage: false,
expectedResponseModel: (json) {
return DeviceReport.fromJson(json);

View File

@ -1,5 +1,4 @@
import 'dart:async';
import 'dart:developer';
import 'package:syncrow_app/features/app_layout/bloc/home_cubit.dart';
import 'package:syncrow_app/features/auth/model/user_model.dart';
import 'package:syncrow_app/features/menu/model/region_model.dart';
@ -95,6 +94,16 @@ class ProfileApi {
return response;
}
Future fetchPermissions(roleId) async {
final response = await _httpService.get(
path: ApiEndpoints.getPermission.replaceAll('{roleUuid}', roleId!),
showServerMessage: true,
expectedResponseModel: (json) {
return json;
});
return response;
}
static Future<List<RegionModel>> fetchRegion() async {
final response = await _httpService.get(
path: ApiEndpoints.getRegion,

View File

@ -1,5 +1,6 @@
//ignore_for_file: constant_identifier_names
import 'dart:ui';
import 'package:syncrow_app/features/app_layout/bloc/home_cubit.dart';
import 'package:syncrow_app/features/devices/model/function_model.dart';
import 'package:syncrow_app/features/menu/bloc/privacy_policy.dart';
import 'package:syncrow_app/features/menu/bloc/user_agreement.dart';
@ -677,134 +678,6 @@ K? getNextItem<K, V>(Map<K, V> map, V value) {
return null;
}
List<Map<String, Object>> menuSections = [
//Home Management
{
'title': 'Home Management',
'color': ColorsManager.primaryColor,
'buttons': [
// {
// 'title': 'Create a Unit',
// 'Icon': Assets.assetsIconsMenuIconsHomeManagementIconsCreateHome,
// 'page': const CreateUnitView()
// },
{
'title': 'Join a Unit',
'Icon': Assets.assetsIconsMenuIconsHomeManagementIconsJoinAHome,
'page': const JoinHomeView()
},
{
'title': 'Manage Your Units',
'Icon': Assets.assetsIconsMenuIconsHomeManagementIconsManageYourHome,
'page': const ManageHomeView()
},
],
},
//General Settings
// {
// 'title': 'General Settings',
// 'color': const Color(0xFF023DFE),
// 'buttons': [
// {
// 'title': 'Voice Assistant',
// 'Icon': Assets.assetsIconsMenuIconsGeneralSettingsIconsVoiceAssistant,
// 'page': null
// },
// {
// 'title': 'Temperature unit',
// 'Icon': Assets.assetsIconsMenuIconsGeneralSettingsIconsTemperatureUnit,
// 'page': null
// },
// {
// 'title': 'Touch tone on panel',
// 'Icon': Assets.assetsIconsMenuIconsGeneralSettingsIconsTouchTone,
// 'page': null
// },
// {
// 'title': 'Language',
// 'Icon': Assets.assetsIconsMenuIconsGeneralSettingsIconsLanguage,
// 'page': null
// },
// {
// 'title': 'Network Diagnosis',
// 'Icon': Assets.assetsIconsMenuIconsGeneralSettingsIconsNetworkDiagnosis,
// 'page': null
// },
// {
// 'title': 'Clear Cache',
// 'Icon': Assets.assetsIconsMenuIconsGeneralSettingsIconsClearCach,
// 'page': null
// },
// ],
// },
// //Messages Center
// {
// 'title': 'Messages Center',
// 'color': const Color(0xFF0088FF),
// 'buttons': [
// {
// 'title': 'Alerts',
// 'Icon': Assets.assetsIconsMenuIconsMessagesCenterIconsAlerts,
// 'page': null
// },
// {
// 'title': 'Messages',
// 'Icon': Assets.assetsIconsMenuIconsMessagesCenterIconsMessages,
// 'page': null
// },
// {
// 'title': 'FAQs',
// 'Icon': Assets.assetsIconsMenuIconsMessagesCenterIconsFAQs,
// 'page': null
// },
// {
// 'title': 'Help & Feedback',
// 'Icon': Assets.assetsIconsMenuIconsMessagesCenterIconsHelpAndFeedback,
// 'page': null
// },
// ],
// },
//Security And Privacy
{
'title': 'Security And Privacy',
'color': const Color(0xFF8AB9FF),
'buttons': [
{
'title': 'Security',
'Icon': Assets.assetsIconsMenuIconsSecurityAndPrivacyIconsSecurty,
'page': const SecurtyView()
},
// {
// 'title': 'Privacy',
// 'Icon': Assets.assetsIconsMenuIconsSecurityAndPrivacyIconsPrivacy,
// 'page': const PrivacyView()
// },
],
},
//Legal Information
{
'title': 'Legal Information',
'color': const Color(0xFF001B72),
'buttons': [
{
'title': 'About',
'Icon': Assets.assetsIconsMenuIconsLeagalInfoIconsAbout,
'page': null
},
{
'title': 'Privacy Policy',
'Icon': Assets.assetsIconsMenuIconsLeagalInfoIconsPrivacyPolicy,
'page': const PrivacyPolicy()
},
{
'title': 'User Agreement',
'Icon': Assets.assetsIconsMenuIconsLeagalInfoIconsUserAgreement,
'page': const UserAgreement()
},
],
},
];
enum MemberRole {
FamilyMember,
OtherMember,