mirror of
https://github.com/SyncrowIOT/web.git
synced 2025-07-10 07:07:19 +00:00
Bug fixes, and added a logout button
This commit is contained in:
@ -31,8 +31,7 @@ class AuthBloc extends Bloc<AuthEvent, AuthState> {
|
||||
|
||||
////////////////////////////// forget password //////////////////////////////////
|
||||
final TextEditingController forgetEmailController = TextEditingController();
|
||||
final TextEditingController forgetPasswordController =
|
||||
TextEditingController();
|
||||
final TextEditingController forgetPasswordController = TextEditingController();
|
||||
final TextEditingController forgetOtp = TextEditingController();
|
||||
final forgetFormKey = GlobalKey<FormState>();
|
||||
late bool checkValidate = false;
|
||||
@ -41,15 +40,13 @@ class AuthBloc extends Bloc<AuthEvent, AuthState> {
|
||||
int _remainingTime = 0;
|
||||
List<RegionModel>? regionList = [RegionModel(name: 'name', id: 'id')];
|
||||
|
||||
Future<void> _onStartTimer(
|
||||
StartTimerEvent event, Emitter<AuthState> emit) async {
|
||||
Future<void> _onStartTimer(StartTimerEvent event, Emitter<AuthState> 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<AuthEvent, AuthState> {
|
||||
_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<AuthEvent, AuthState> {
|
||||
emit(const TimerState(isButtonEnabled: true, remainingTime: 0));
|
||||
}
|
||||
|
||||
Future<void> changePassword(
|
||||
ChangePasswordEvent event, Emitter<AuthState> emit) async {
|
||||
Future<void> changePassword(ChangePasswordEvent event, Emitter<AuthState> 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<AuthEvent, AuthState> {
|
||||
}
|
||||
|
||||
void _onUpdateTimer(UpdateTimerEvent event, Emitter<AuthState> 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<AuthEvent, AuthState> {
|
||||
|
||||
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<AuthEvent, AuthState> {
|
||||
|
||||
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<AuthEvent, AuthState> {
|
||||
static Future<String> 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<AuthEvent, AuthState> {
|
||||
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<AuthEvent, AuthState> {
|
||||
forgetValidate = '';
|
||||
emit(LoginInitial());
|
||||
}
|
||||
|
||||
static logout() {
|
||||
const storage = FlutterSecureStorage();
|
||||
storage.deleteAll();
|
||||
}
|
||||
}
|
||||
|
@ -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<void> showCustomDialog({
|
||||
required BuildContext context,
|
||||
@ -13,7 +12,7 @@ Future<void> showCustomDialog({
|
||||
double? iconWidth,
|
||||
VoidCallback? onOkPressed,
|
||||
bool barrierDismissible = false,
|
||||
required actions,
|
||||
required List<Widget> actions,
|
||||
}) {
|
||||
return showDialog(
|
||||
context: context,
|
||||
@ -21,59 +20,43 @@ Future<void> 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: <Widget>[
|
||||
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);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
@ -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<DeviceManagementEvent, DeviceManagementState> {
|
||||
class DeviceManagementBloc extends Bloc<DeviceManagementEvent, DeviceManagementState> {
|
||||
int _selectedIndex = 0;
|
||||
List<AllDevicesModel> _devices = [];
|
||||
int _onlineCount = 0;
|
||||
int _offlineCount = 0;
|
||||
int _lowBatteryCount = 0;
|
||||
List<AllDevicesModel> _selectedDevices = [];
|
||||
String productName = '';
|
||||
|
||||
DeviceManagementBloc() : super(DeviceManagementInitial()) {
|
||||
on<FetchDevices>(_onFetchDevices);
|
||||
@ -23,8 +23,7 @@ class DeviceManagementBloc
|
||||
on<SelectDevice>(_onSelectDevice);
|
||||
}
|
||||
|
||||
Future<void> _onFetchDevices(
|
||||
FetchDevices event, Emitter<DeviceManagementState> emit) async {
|
||||
Future<void> _onFetchDevices(FetchDevices event, Emitter<DeviceManagementState> emit) async {
|
||||
emit(DeviceManagementLoading());
|
||||
try {
|
||||
final devices = await DevicesManagementApi().fetchDevices();
|
||||
@ -44,8 +43,7 @@ class DeviceManagementBloc
|
||||
}
|
||||
}
|
||||
|
||||
void _onFilterDevices(
|
||||
FilterDevices event, Emitter<DeviceManagementState> emit) {
|
||||
void _onFilterDevices(FilterDevices event, Emitter<DeviceManagementState> 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<DeviceManagementState> emit) {
|
||||
void _onSelectedFilterChanged(SelectedFilterChanged event, Emitter<DeviceManagementState> emit) {
|
||||
_selectedIndex = event.selectedIndex;
|
||||
add(FilterDevices(_getFilterFromIndex(_selectedIndex)));
|
||||
}
|
||||
|
||||
void _onSelectDevice(
|
||||
SelectDevice event, Emitter<DeviceManagementState> emit) {
|
||||
void _onSelectDevice(SelectDevice event, Emitter<DeviceManagementState> 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<DeviceManagementState> emit) {
|
||||
void _onSearchDevices(SearchDevices event, Emitter<DeviceManagementState> 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<AllDevicesModel> 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(
|
||||
|
@ -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<WebAppBar> 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,
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
],
|
||||
|
Reference in New Issue
Block a user