push fetch devices and connecting the filters

This commit is contained in:
ashrafzarkanisala
2024-08-24 16:37:10 +03:00
parent 0c047de9c1
commit 2597cdc311
68 changed files with 1800 additions and 989 deletions

View File

@ -0,0 +1 @@

View File

@ -22,33 +22,36 @@ class AccessBloc extends Bloc<AccessEvent, AccessState> {
int? effectiveTimeTimeStamp; int? effectiveTimeTimeStamp;
int? expirationTimeTimeStamp; int? expirationTimeTimeStamp;
TextEditingController passwordName= TextEditingController(); TextEditingController passwordName = TextEditingController();
List<PasswordModel> filteredData = []; List<PasswordModel> filteredData = [];
List<PasswordModel> data=[]; List<PasswordModel> data = [];
Future<void> _onFetchTableData( Future<void> _onFetchTableData(
FetchTableData event, Emitter<AccessState> emit) async { FetchTableData event, Emitter<AccessState> emit) async {
try { try {
emit(AccessLoaded()); emit(AccessLoaded());
data = await AccessMangApi().fetchVisitorPassword(); data = await AccessMangApi().fetchVisitorPassword();
filteredData= data; filteredData = data;
updateTabsCount(); updateTabsCount();
emit(TableLoaded(data)); emit(TableLoaded(data));
} catch (e) { } catch (e) {
emit(FailedState(e.toString())); emit(FailedState(e.toString()));
} }
} }
void updateTabsCount() { void updateTabsCount() {
int toBeEffectiveCount = data.where((item) => item.passwordStatus.value== 'To Be Effective').length; int toBeEffectiveCount = data
int effectiveCount = data.where((item) => item.passwordStatus.value == 'Effective').length; .where((item) => item.passwordStatus.value == 'To Be Effective')
int expiredCount = data.where((item) => item.passwordStatus.value == 'Expired').length; .length;
int effectiveCount =
data.where((item) => item.passwordStatus.value == 'Effective').length;
int expiredCount =
data.where((item) => item.passwordStatus.value == 'Expired').length;
tabs[1] = 'To Be Effective ($toBeEffectiveCount)'; tabs[1] = 'To Be Effective ($toBeEffectiveCount)';
tabs[2] = 'Effective ($effectiveCount)'; tabs[2] = 'Effective ($effectiveCount)';
tabs[3] = 'Expired ($expiredCount)'; tabs[3] = 'Expired ($expiredCount)';
} }
int selectedIndex = 0; int selectedIndex = 0;
final List<String> tabs = [ final List<String> tabs = [
'All', 'All',
@ -57,20 +60,19 @@ class AccessBloc extends Bloc<AccessEvent, AccessState> {
'Expired' 'Expired'
]; ];
Future selectFilterTap(
Future selectFilterTap(TabChangedEvent event, Emitter<AccessState> emit) async { TabChangedEvent event, Emitter<AccessState> emit) async {
try { try {
emit(AccessLoaded()); emit(AccessLoaded());
selectedIndex= event.selectedIndex; selectedIndex = event.selectedIndex;
emit(AccessInitial()); emit(AccessInitial());
emit(TableLoaded(data)); emit(TableLoaded(data));
} catch (e) { } catch (e) {
emit(FailedState( e.toString())); emit(FailedState(e.toString()));
return; return;
} }
} }
Future<void> selectTime(SelectTime event, Emitter<AccessState> emit) async { Future<void> selectTime(SelectTime event, Emitter<AccessState> emit) async {
emit(AccessLoaded()); emit(AccessLoaded());
@ -84,7 +86,6 @@ class AccessBloc extends Bloc<AccessEvent, AccessState> {
final TimeOfDay? timePicked = await showTimePicker( final TimeOfDay? timePicked = await showTimePicker(
context: event.context, context: event.context,
initialTime: TimeOfDay.now(), initialTime: TimeOfDay.now(),
builder: (context, child) { builder: (context, child) {
return Theme( return Theme(
data: ThemeData.light().copyWith( data: ThemeData.light().copyWith(
@ -111,24 +112,35 @@ class AccessBloc extends Bloc<AccessEvent, AccessState> {
timePicked.minute, timePicked.minute,
); );
final selectedTimestamp = DateTime( final selectedTimestamp = DateTime(
selectedDateTime.year, selectedDateTime.year,
selectedDateTime.month, selectedDateTime.month,
selectedDateTime.day, selectedDateTime.day,
selectedDateTime.hour, selectedDateTime.hour,
selectedDateTime.minute, selectedDateTime.minute,
).millisecondsSinceEpoch ~/ 1000; // Divide by 1000 to remove milliseconds ).millisecondsSinceEpoch ~/
1000; // Divide by 1000 to remove milliseconds
if (event.isStart) { if (event.isStart) {
if (expirationTimeTimeStamp != null && selectedTimestamp > expirationTimeTimeStamp!) { if (expirationTimeTimeStamp != null &&
CustomSnackBar.displaySnackBar('Effective Time cannot be later than Expiration Time.'); selectedTimestamp > expirationTimeTimeStamp!) {
CustomSnackBar.displaySnackBar(
'Effective Time cannot be later than Expiration Time.');
} else { } else {
startTime = selectedDateTime.toString().split('.').first; // Remove seconds and milliseconds startTime = selectedDateTime
.toString()
.split('.')
.first; // Remove seconds and milliseconds
effectiveTimeTimeStamp = selectedTimestamp; effectiveTimeTimeStamp = selectedTimestamp;
} }
} else { } else {
if (effectiveTimeTimeStamp != null && selectedTimestamp < effectiveTimeTimeStamp!) { if (effectiveTimeTimeStamp != null &&
CustomSnackBar.displaySnackBar('Expiration Time cannot be earlier than Effective Time.'); selectedTimestamp < effectiveTimeTimeStamp!) {
CustomSnackBar.displaySnackBar(
'Expiration Time cannot be earlier than Effective Time.');
} else { } else {
endTime = selectedDateTime.toString().split('.').first; // Remove seconds and milliseconds endTime = selectedDateTime
.toString()
.split('.')
.first; // Remove seconds and milliseconds
expirationTimeTimeStamp = selectedTimestamp; expirationTimeTimeStamp = selectedTimestamp;
} }
} }
@ -137,8 +149,8 @@ class AccessBloc extends Bloc<AccessEvent, AccessState> {
emit(ChangeTimeState()); emit(ChangeTimeState());
} }
Future<void> _filterData(
Future<void> _filterData(FilterDataEvent event, Emitter<AccessState> emit) async { FilterDataEvent event, Emitter<AccessState> emit) async {
emit(AccessLoaded()); emit(AccessLoaded());
try { try {
filteredData = data.where((item) { filteredData = data.where((item) {
@ -151,7 +163,8 @@ class AccessBloc extends Bloc<AccessEvent, AccessState> {
} }
} }
if (event.startTime != null && event.endTime != null) { if (event.startTime != null && event.endTime != null) {
final int? effectiveTime = int.tryParse(item.effectiveTime.toString()); final int? effectiveTime =
int.tryParse(item.effectiveTime.toString());
final int? invalidTime = int.tryParse(item.invalidTime.toString()); final int? invalidTime = int.tryParse(item.invalidTime.toString());
if (effectiveTime == null || invalidTime == null) { if (effectiveTime == null || invalidTime == null) {
matchesCriteria = false; matchesCriteria = false;
@ -163,11 +176,14 @@ class AccessBloc extends Bloc<AccessEvent, AccessState> {
} }
} }
} }
if (event.selectedTabIndex == 1 && item.passwordStatus.value != 'To Be Effective') { if (event.selectedTabIndex == 1 &&
item.passwordStatus.value != 'To Be Effective') {
matchesCriteria = false; matchesCriteria = false;
} else if (event.selectedTabIndex == 2 && item.passwordStatus.value != 'Effective') { } else if (event.selectedTabIndex == 2 &&
item.passwordStatus.value != 'Effective') {
matchesCriteria = false; matchesCriteria = false;
} else if (event.selectedTabIndex == 3 && item.passwordStatus.value != 'Expired') { } else if (event.selectedTabIndex == 3 &&
item.passwordStatus.value != 'Expired') {
matchesCriteria = false; matchesCriteria = false;
} }
return matchesCriteria; return matchesCriteria;
@ -178,23 +194,25 @@ class AccessBloc extends Bloc<AccessEvent, AccessState> {
} }
} }
resetSearch(ResetSearch event, Emitter<AccessState> emit) async{ resetSearch(ResetSearch event, Emitter<AccessState> emit) async {
emit(AccessLoaded()); emit(AccessLoaded());
startTime = 'Start Time'; startTime = 'Start Time';
endTime = 'End Time'; endTime = 'End Time';
passwordName.clear(); passwordName.clear();
selectedIndex=0; selectedIndex = 0;
effectiveTimeTimeStamp=null; effectiveTimeTimeStamp = null;
expirationTimeTimeStamp=null; expirationTimeTimeStamp = null;
add(FetchTableData()); add(FetchTableData());
} }
String timestampToDate(dynamic timestamp) { String timestampToDate(dynamic timestamp) {
DateTime dateTime = DateTime.fromMillisecondsSinceEpoch(int.parse(timestamp) * 1000); DateTime dateTime =
DateTime.fromMillisecondsSinceEpoch(int.parse(timestamp) * 1000);
return "${dateTime.year}/${dateTime.month.toString().padLeft(2, '0')}/${dateTime.day.toString().padLeft(2, '0')}"; return "${dateTime.year}/${dateTime.month.toString().padLeft(2, '0')}/${dateTime.day.toString().padLeft(2, '0')}";
} }
Future<void> onTabChanged(TabChangedEvent event, Emitter<AccessState> emit) async { Future<void> onTabChanged(
TabChangedEvent event, Emitter<AccessState> emit) async {
try { try {
emit(AccessLoaded()); emit(AccessLoaded());
selectedIndex = event.selectedIndex; selectedIndex = event.selectedIndex;
@ -203,13 +221,19 @@ class AccessBloc extends Bloc<AccessEvent, AccessState> {
filteredData = data; filteredData = data;
break; break;
case 1: // To Be Effective case 1: // To Be Effective
filteredData = data.where((item) => item.passwordStatus.value == "To Be Effective").toList(); filteredData = data
.where((item) => item.passwordStatus.value == "To Be Effective")
.toList();
break; break;
case 2: // Effective case 2: // Effective
filteredData = data.where((item) => item.passwordStatus.value == "Effective").toList(); filteredData = data
.where((item) => item.passwordStatus.value == "Effective")
.toList();
break; break;
case 3: // Expired case 3: // Expired
filteredData = data.where((item) => item.passwordStatus.value == "Expired").toList(); filteredData = data
.where((item) => item.passwordStatus.value == "Expired")
.toList();
break; break;
default: default:
filteredData = data; filteredData = data;
@ -218,12 +242,10 @@ class AccessBloc extends Bloc<AccessEvent, AccessState> {
selectedTabIndex: selectedIndex, selectedTabIndex: selectedIndex,
passwordName: passwordName.text.toLowerCase(), passwordName: passwordName.text.toLowerCase(),
startTime: effectiveTimeTimeStamp, startTime: effectiveTimeTimeStamp,
endTime: expirationTimeTimeStamp endTime: expirationTimeTimeStamp));
));
emit(TableLoaded(filteredData)); emit(TableLoaded(filteredData));
} catch (e) { } catch (e) {
emit(FailedState(e.toString())); emit(FailedState(e.toString()));
} }
} }
} }

View File

@ -1,14 +1,15 @@
import 'package:equatable/equatable.dart'; import 'package:equatable/equatable.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
abstract class AccessEvent extends Equatable { abstract class AccessEvent extends Equatable {
const AccessEvent(); const AccessEvent();
@override @override
List<Object> get props => []; List<Object> get props => [];
} }
class FetchTableData extends AccessEvent {} class FetchTableData extends AccessEvent {}
class ResetSearch extends AccessEvent {} class ResetSearch extends AccessEvent {}
class TabChangedEvent extends AccessEvent { class TabChangedEvent extends AccessEvent {
@ -17,16 +18,14 @@ class TabChangedEvent extends AccessEvent {
const TabChangedEvent(this.selectedIndex); const TabChangedEvent(this.selectedIndex);
} }
class SelectTime extends AccessEvent { class SelectTime extends AccessEvent {
final BuildContext context; final BuildContext context;
final bool isStart; final bool isStart;
const SelectTime({required this.context,required this.isStart}); const SelectTime({required this.context, required this.isStart});
@override @override
List<Object> get props => [context,isStart]; List<Object> get props => [context, isStart];
} }
class FilterDataEvent extends AccessEvent { class FilterDataEvent extends AccessEvent {
final String? passwordName; final String? passwordName;
final int? startTime; final int? startTime;
@ -34,13 +33,9 @@ class FilterDataEvent extends AccessEvent {
final int selectedTabIndex; // Add this field final int selectedTabIndex; // Add this field
const FilterDataEvent({ const FilterDataEvent({
this.passwordName, this.passwordName,
this.startTime, this.startTime,
this.endTime, this.endTime,
required this.selectedTabIndex, // Initialize this field required this.selectedTabIndex, // Initialize this field
}); });
} }

View File

@ -11,6 +11,7 @@ abstract class AccessState extends Equatable {
class AccessInitial extends AccessState {} class AccessInitial extends AccessState {}
class AccessLoaded extends AccessState {} class AccessLoaded extends AccessState {}
class FailedState extends AccessState { class FailedState extends AccessState {
final String message; final String message;
@ -29,7 +30,7 @@ class TableLoaded extends AccessState {
List<Object> get props => [data]; List<Object> get props => [data];
} }
class TabState extends AccessState { class TabState extends AccessState {
final int selectedIndex; final int selectedIndex;
const TabState({required this.selectedIndex}); const TabState({required this.selectedIndex});

View File

@ -30,9 +30,9 @@ class PasswordModel {
effectiveTime: json['effectiveTime'], effectiveTime: json['effectiveTime'],
passwordCreated: json['passwordCreated'], passwordCreated: json['passwordCreated'],
createdTime: json['createdTime'], createdTime: json['createdTime'],
passwordName: json['passwordName']??'No name', // New field passwordName: json['passwordName'] ?? 'No name', // New field
passwordStatus:AccessStatusExtension.fromString(json['passwordStatus']), passwordStatus: AccessStatusExtension.fromString(json['passwordStatus']),
passwordType:AccessTypeExtension.fromString(json['passwordType']), passwordType: AccessTypeExtension.fromString(json['passwordType']),
deviceUuid: json['deviceUuid'], deviceUuid: json['deviceUuid'],
); );
} }
@ -50,5 +50,4 @@ class PasswordModel {
'deviceUuid': deviceUuid, 'deviceUuid': deviceUuid,
}; };
} }
} }

View File

@ -6,7 +6,6 @@ import 'package:syncrow_web/pages/access_management/bloc/access_state.dart';
import 'package:syncrow_web/pages/common/custom_table.dart'; import 'package:syncrow_web/pages/common/custom_table.dart';
import 'package:syncrow_web/pages/common/date_time_widget.dart'; import 'package:syncrow_web/pages/common/date_time_widget.dart';
import 'package:syncrow_web/pages/common/buttons/default_button.dart'; import 'package:syncrow_web/pages/common/buttons/default_button.dart';
import 'package:syncrow_web/pages/common/filter/filter_widget.dart';
import 'package:syncrow_web/pages/visitor_password/view/visitor_password_dialog.dart'; import 'package:syncrow_web/pages/visitor_password/view/visitor_password_dialog.dart';
import 'package:syncrow_web/utils/color_manager.dart'; import 'package:syncrow_web/utils/color_manager.dart';
import 'package:syncrow_web/utils/constants/assets.dart'; import 'package:syncrow_web/utils/constants/assets.dart';

View File

@ -84,7 +84,7 @@ class AuthBloc extends Bloc<AuthEvent, AuthState> {
} else if (response == "You entered wrong otp") { } else if (response == "You entered wrong otp") {
forgetValidate = 'Wrong one time password.'; forgetValidate = 'Wrong one time password.';
emit(AuthInitialState()); emit(AuthInitialState());
}else if (response == "OTP expired") { } else if (response == "OTP expired") {
forgetValidate = 'One time password has been expired.'; forgetValidate = 'One time password has been expired.';
emit(AuthInitialState()); emit(AuthInitialState());
} }
@ -94,6 +94,7 @@ class AuthBloc extends Bloc<AuthEvent, AuthState> {
// emit(FailureForgetState(error: failure.toString())); // emit(FailureForgetState(error: failure.toString()));
} }
} }
//925207 //925207
String? validateCode(String? value) { String? validateCode(String? value) {
if (value == null || value.isEmpty) { if (value == null || value.isEmpty) {

View File

@ -13,47 +13,60 @@ class LoginButtonPressed extends AuthEvent {
final String password; final String password;
final String regionUuid; final String regionUuid;
const LoginButtonPressed({required this.username, required this.password, required this.regionUuid, }); const LoginButtonPressed({
required this.username,
required this.password,
required this.regionUuid,
});
@override @override
List<Object> get props => [username, password,regionUuid]; List<Object> get props => [username, password, regionUuid];
} }
class CheckBoxEvent extends AuthEvent { class CheckBoxEvent extends AuthEvent {
final bool? newValue; final bool? newValue;
const CheckBoxEvent({required this.newValue,}); const CheckBoxEvent({
required this.newValue,
});
@override @override
List<Object> get props => [newValue!,]; List<Object> get props => [
newValue!,
];
} }
class GetCodeEvent extends AuthEvent{} class GetCodeEvent extends AuthEvent {}
class SubmitEvent extends AuthEvent{} class SubmitEvent extends AuthEvent {}
class StartTimerEvent extends AuthEvent{} class StartTimerEvent extends AuthEvent {}
class StopTimerEvent extends AuthEvent{} class StopTimerEvent extends AuthEvent {}
class UpdateTimerEvent extends AuthEvent { class UpdateTimerEvent extends AuthEvent {
final int remainingTime; final int remainingTime;
final bool isButtonEnabled; final bool isButtonEnabled;
const UpdateTimerEvent({required this.remainingTime, required this.isButtonEnabled}); const UpdateTimerEvent(
{required this.remainingTime, required this.isButtonEnabled});
} }
class ChangePasswordEvent extends AuthEvent{} class ChangePasswordEvent extends AuthEvent {}
class SendOtpEvent extends AuthEvent{} class SendOtpEvent extends AuthEvent {}
class PasswordVisibleEvent extends AuthEvent{ class PasswordVisibleEvent extends AuthEvent {
final bool? newValue; final bool? newValue;
const PasswordVisibleEvent({required this.newValue,}); const PasswordVisibleEvent({
required this.newValue,
});
} }
class RegionInitialEvent extends AuthEvent {} class RegionInitialEvent extends AuthEvent {}
class CheckEnableEvent extends AuthEvent {} class CheckEnableEvent extends AuthEvent {}
class ChangeValidateEvent extends AuthEvent {} class ChangeValidateEvent extends AuthEvent {}
class SelectRegionEvent extends AuthEvent { class SelectRegionEvent extends AuthEvent {
@ -62,4 +75,3 @@ class SelectRegionEvent extends AuthEvent {
@override @override
List<Object> get props => [val]; List<Object> get props => [val];
} }

View File

@ -12,6 +12,7 @@ class LoginInitial extends AuthState {}
class AuthTokenLoading extends AuthState {} class AuthTokenLoading extends AuthState {}
class AuthLoading extends AuthState {} class AuthLoading extends AuthState {}
class AuthInitialState extends AuthState {} class AuthInitialState extends AuthState {}
class LoginSuccess extends AuthState {} class LoginSuccess extends AuthState {}
@ -55,7 +56,8 @@ class TimerState extends AuthState {
final bool isButtonEnabled; final bool isButtonEnabled;
final int remainingTime; final int remainingTime;
const TimerState({required this.isButtonEnabled, required this.remainingTime}); const TimerState(
{required this.isButtonEnabled, required this.remainingTime});
@override @override
List<Object> get props => [isButtonEnabled, remainingTime]; List<Object> get props => [isButtonEnabled, remainingTime];
@ -74,6 +76,7 @@ class AuthTokenError extends AuthError {
class AuthSuccess extends AuthState {} class AuthSuccess extends AuthState {}
class AuthTokenSuccess extends AuthSuccess {} class AuthTokenSuccess extends AuthSuccess {}
class TimerUpdated extends AuthState { class TimerUpdated extends AuthState {
final String formattedTime; final String formattedTime;
final bool isButtonEnabled; final bool isButtonEnabled;

View File

@ -1,5 +1,3 @@
class RegionModel { class RegionModel {
final String name; final String name;
final String id; final String id;

View File

@ -42,14 +42,11 @@ class Token {
//save token to secure storage //save token to secure storage
var storage = const FlutterSecureStorage(); var storage = const FlutterSecureStorage();
storage.write( storage.write(
key: loginAccessTokenKey, key: loginAccessTokenKey, value: json[loginAccessTokenKey] ?? '');
value: json[loginAccessTokenKey] ?? '');
storage.write( storage.write(
key: loginRefreshTokenKey, key: loginRefreshTokenKey, value: json[loginRefreshTokenKey] ?? '');
value: json[loginRefreshTokenKey] ?? '');
//create token object ? //create token object ?
return Token( return Token(json[loginAccessTokenKey] ?? '',
json[loginAccessTokenKey] ?? '',
json[loginRefreshTokenKey] ?? '', '', 0, 0); json[loginRefreshTokenKey] ?? '', '', 0, 0);
} }

View File

@ -1,5 +1,3 @@
import 'package:syncrow_web/pages/auth/model/token.dart'; import 'package:syncrow_web/pages/auth/model/token.dart';
class UserModel { class UserModel {

View File

@ -10,7 +10,10 @@ class VerifyPassCode {
final String deviceId; final String deviceId;
VerifyPassCode( VerifyPassCode(
{required this.phone, required this.passCode, required this.agent, required this.deviceId}); {required this.phone,
required this.passCode,
required this.agent,
required this.deviceId});
factory VerifyPassCode.fromJson(Map<String, dynamic> json) => VerifyPassCode( factory VerifyPassCode.fromJson(Map<String, dynamic> json) => VerifyPassCode(
phone: json[verificationPhone], phone: json[verificationPhone],

View File

@ -1,4 +1,3 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
class ForgetPasswordMobilePage extends StatelessWidget { class ForgetPasswordMobilePage extends StatelessWidget {

View File

@ -1,19 +1,15 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:syncrow_web/pages/auth/view/forget_password_mobile_page.dart'; import 'package:syncrow_web/pages/auth/view/forget_password_mobile_page.dart';
import 'package:syncrow_web/pages/auth/view/forget_password_web_page.dart'; import 'package:syncrow_web/pages/auth/view/forget_password_web_page.dart';
import 'package:syncrow_web/utils/responsive_layout.dart'; import 'package:syncrow_web/utils/responsive_layout.dart';
class ForgetPasswordPage extends StatelessWidget { class ForgetPasswordPage extends StatelessWidget {
const ForgetPasswordPage({super.key}); const ForgetPasswordPage({super.key});
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return const ResponsiveLayout( return const ResponsiveLayout(
desktopBody: ForgetPasswordWebPage(), desktopBody: ForgetPasswordWebPage(),
mobileBody:ForgetPasswordWebPage() mobileBody: ForgetPasswordWebPage());
);
} }
} }

View File

@ -36,7 +36,7 @@ class ForgetPasswordWebPage extends StatelessWidget {
} }
}, },
builder: (context, state) { builder: (context, state) {
return _buildForm(context, state); return _buildForm(context, state);
}, },
), ),
), ),
@ -66,7 +66,7 @@ class ForgetPasswordWebPage extends StatelessWidget {
child: Stack( child: Stack(
children: [ children: [
if (state is AuthLoading) if (state is AuthLoading)
const Center(child: CircularProgressIndicator()), const Center(child: CircularProgressIndicator()),
ListView( ListView(
shrinkWrap: true, shrinkWrap: true,
controller: _scrollController, controller: _scrollController,
@ -99,7 +99,8 @@ class ForgetPasswordWebPage extends StatelessWidget {
borderRadius: borderRadius:
const BorderRadius.all(Radius.circular(30)), const BorderRadius.all(Radius.circular(30)),
border: Border.all( border: Border.all(
color: ColorsManager.graysColor.withOpacity(0.2)), color:
ColorsManager.graysColor.withOpacity(0.2)),
), ),
child: Form( child: Form(
key: forgetBloc.forgetFormKey, key: forgetBloc.forgetFormKey,
@ -108,7 +109,8 @@ class ForgetPasswordWebPage extends StatelessWidget {
horizontal: size.width * 0.02, horizontal: size.width * 0.02,
vertical: size.width * 0.003), vertical: size.width * 0.003),
child: Column( child: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly, mainAxisAlignment:
MainAxisAlignment.spaceEvenly,
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[ children: <Widget>[
const SizedBox(height: 10), const SizedBox(height: 10),
@ -122,21 +124,27 @@ class ForgetPasswordWebPage extends StatelessWidget {
const SizedBox(height: 10), const SizedBox(height: 10),
Text( Text(
'Please fill in your account information to\nretrieve your password', 'Please fill in your account information to\nretrieve your password',
style: Theme.of(context).textTheme.bodySmall!.copyWith( style: Theme.of(context)
fontSize: 14, .textTheme
fontWeight: FontWeight.w400), .bodySmall!
.copyWith(
fontSize: 14,
fontWeight: FontWeight.w400),
), ),
const SizedBox(height: 10), const SizedBox(height: 10),
Column( Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment:
CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.start,
children: [ children: [
Text( Text(
"Country/Region", "Country/Region",
style: Theme.of(context).textTheme.bodySmall!.copyWith( style: Theme.of(context)
fontSize: 14, .textTheme
fontWeight: FontWeight.w400), .bodySmall!
.copyWith(
fontSize: 14,
fontWeight: FontWeight.w400),
), ),
const SizedBox(height: 10), const SizedBox(height: 10),
SizedBox( SizedBox(
@ -145,7 +153,8 @@ class ForgetPasswordWebPage extends StatelessWidget {
icon: const Icon( icon: const Icon(
Icons.keyboard_arrow_down_outlined, Icons.keyboard_arrow_down_outlined,
), ),
decoration: textBoxDecoration()!.copyWith( decoration:
textBoxDecoration()!.copyWith(
hintText: null, hintText: null,
), ),
hint: SizedBox( hint: SizedBox(
@ -160,15 +169,14 @@ class ForgetPasswordWebPage extends StatelessWidget {
), ),
), ),
isDense: true, isDense: true,
style: style: const TextStyle(
const TextStyle(color: Colors.black), color: Colors.black),
items: forgetBloc.regionList! items: forgetBloc.regionList!
.map((RegionModel region) { .map((RegionModel region) {
return DropdownMenuItem<String>( return DropdownMenuItem<String>(
value: region.id, value: region.id,
child: SizedBox( child: SizedBox(
width: size.width*0.06, width: size.width * 0.06,
child: Text(region.name)), child: Text(region.name)),
); );
}).toList(), }).toList(),
@ -183,13 +191,18 @@ class ForgetPasswordWebPage extends StatelessWidget {
), ),
const SizedBox(height: 20), const SizedBox(height: 20),
Column( Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment:
CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.start,
children: [ children: [
Text( Text(
"Account", "Account",
style: Theme.of(context).textTheme.bodySmall!.copyWith(fontSize: 14,fontWeight: FontWeight.w400), style: Theme.of(context)
.textTheme
.bodySmall!
.copyWith(
fontSize: 14,
fontWeight: FontWeight.w400),
), ),
const SizedBox(height: 10), const SizedBox(height: 10),
SizedBox( SizedBox(
@ -197,22 +210,29 @@ class ForgetPasswordWebPage extends StatelessWidget {
validator: forgetBloc.validateEmail, validator: forgetBloc.validateEmail,
controller: controller:
forgetBloc.forgetEmailController, forgetBloc.forgetEmailController,
decoration: textBoxDecoration()!.copyWith( decoration: textBoxDecoration()!
hintText: 'Enter your email'), .copyWith(
style: hintText: 'Enter your email'),
const TextStyle(color: Colors.black), style: const TextStyle(
color: Colors.black),
), ),
), ),
], ],
), ),
const SizedBox(height: 20.0), const SizedBox(height: 20.0),
Column( Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment:
CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.start,
children: [ children: [
Text( Text(
"One Time Password", "One Time Password",
style: Theme.of(context).textTheme.bodySmall!.copyWith(fontSize: 14,fontWeight: FontWeight.w400), style: Theme.of(context)
.textTheme
.bodySmall!
.copyWith(
fontSize: 14,
fontWeight: FontWeight.w400),
), ),
const SizedBox(height: 10), const SizedBox(height: 10),
SizedBox( SizedBox(
@ -221,65 +241,88 @@ class ForgetPasswordWebPage extends StatelessWidget {
keyboardType: keyboardType:
TextInputType.visiblePassword, TextInputType.visiblePassword,
controller: forgetBloc.forgetOtp, controller: forgetBloc.forgetOtp,
decoration: textBoxDecoration()!.copyWith( decoration:
textBoxDecoration()!.copyWith(
hintText: 'Enter Code', hintText: 'Enter Code',
suffixIcon: SizedBox( suffixIcon: SizedBox(
width: 100, width: 100,
child: Center( child: Center(
child: InkWell( child: InkWell(
onTap:state is TimerState && !state.isButtonEnabled && state.remainingTime!=1?null: () { onTap: state is TimerState &&
forgetBloc.add(StartTimerEvent()); !state
}, .isButtonEnabled &&
state.remainingTime !=
1
? null
: () {
forgetBloc.add(
StartTimerEvent());
},
child: Text( child: Text(
'Get Code ${state is TimerState && !state.isButtonEnabled && state.remainingTime!=1? "(${forgetBloc.formattedTime(state.remainingTime)}) " : ""}', 'Get Code ${state is TimerState && !state.isButtonEnabled && state.remainingTime != 1 ? "(${forgetBloc.formattedTime(state.remainingTime)}) " : ""}',
style: TextStyle( style: TextStyle(
color: state is TimerState && color: state
!state.isButtonEnabled is TimerState &&
!state
.isButtonEnabled
? Colors.grey ? Colors.grey
: ColorsManager.btnColor, : ColorsManager
.btnColor,
), ),
), ),
), ),
), ),
), ),
), ),
style: style: const TextStyle(
const TextStyle(color: Colors.black), color: Colors.black),
), ),
), ),
if (forgetBloc.forgetValidate != '') // Check if there is a validation message if (forgetBloc.forgetValidate !=
'') // Check if there is a validation message
Padding( Padding(
padding: const EdgeInsets.only(top: 8.0), padding:
const EdgeInsets.only(top: 8.0),
child: Text( child: Text(
forgetBloc.forgetValidate, forgetBloc.forgetValidate,
style: const TextStyle( style: const TextStyle(
color: ColorsManager.red, color: ColorsManager.red,
fontSize: 10, fontSize: 10,
fontWeight: FontWeight.w700 fontWeight: FontWeight.w700),
),
), ),
), ),
], ],
), ),
const SizedBox(height: 20.0), const SizedBox(height: 20.0),
Column( Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment:
CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.start,
children: [ children: [
Text( Text(
"Password", "Password",
style: Theme.of(context).textTheme.bodySmall!.copyWith(fontSize: 14,fontWeight: FontWeight.w400), style: Theme.of(context)
.textTheme
.bodySmall!
.copyWith(
fontSize: 14,
fontWeight: FontWeight.w400),
), ),
const SizedBox(height: 10), const SizedBox(height: 10),
SizedBox( SizedBox(
child: TextFormField( child: TextFormField(
validator: forgetBloc.passwordValidator, validator:
keyboardType: TextInputType.visiblePassword, forgetBloc.passwordValidator,
controller: forgetBloc.forgetPasswordController, keyboardType:
decoration: textBoxDecoration()!.copyWith( TextInputType.visiblePassword,
controller: forgetBloc
.forgetPasswordController,
decoration:
textBoxDecoration()!.copyWith(
hintText: 'At least 8 characters', hintText: 'At least 8 characters',
), ),
style: const TextStyle(color: Colors.black), style: const TextStyle(
color: Colors.black),
), ),
), ),
], ],
@ -289,17 +332,22 @@ class ForgetPasswordWebPage extends StatelessWidget {
), ),
const SizedBox(height: 20.0), const SizedBox(height: 20.0),
Row( Row(
crossAxisAlignment: CrossAxisAlignment.center, crossAxisAlignment:
CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center,
children: [ children: [
SizedBox( SizedBox(
width: size.width * 0.2, width: size.width * 0.2,
child: DefaultButton( child: DefaultButton(
backgroundColor: ColorsManager.btnColor, backgroundColor:
ColorsManager.btnColor,
child: const Text('Submit'), child: const Text('Submit'),
onPressed: () { onPressed: () {
if (forgetBloc.forgetFormKey.currentState!.validate()) { if (forgetBloc
forgetBloc.add(ChangePasswordEvent()); .forgetFormKey.currentState!
.validate()) {
forgetBloc
.add(ChangePasswordEvent());
} }
}, },
), ),
@ -321,8 +369,10 @@ class ForgetPasswordWebPage extends StatelessWidget {
SizedBox( SizedBox(
width: size.width * 0.2, width: size.width * 0.2,
child: Row( child: Row(
mainAxisAlignment: MainAxisAlignment.center, mainAxisAlignment:
crossAxisAlignment: CrossAxisAlignment.center, MainAxisAlignment.center,
crossAxisAlignment:
CrossAxisAlignment.center,
children: [ children: [
const Flexible( const Flexible(
child: Text( child: Text(

View File

@ -27,7 +27,7 @@ class LoginMobilePage extends StatelessWidget {
// Navigate to home screen after successful login // Navigate to home screen after successful login
Navigator.pushReplacement( Navigator.pushReplacement(
context, context,
MaterialPageRoute(builder: (context) => HomePage()), MaterialPageRoute(builder: (context) => HomePage()),
); );
} else if (state is LoginFailure) { } else if (state is LoginFailure) {
// Show error message // Show error message
@ -148,14 +148,14 @@ class LoginMobilePage extends StatelessWidget {
), ),
isDense: true, isDense: true,
style: const TextStyle(color: Colors.black), style: const TextStyle(color: Colors.black),
items:loginBloc.regionList!.map((RegionModel region) { items: loginBloc.regionList!
.map((RegionModel region) {
return DropdownMenuItem<String>( return DropdownMenuItem<String>(
value: region.name, value: region.name,
child: Text(region.name), child: Text(region.name),
); );
}).toList(), }).toList(),
onChanged: (String? value) { onChanged: (String? value) {},
},
), ),
) )
], ],
@ -194,7 +194,8 @@ class LoginMobilePage extends StatelessWidget {
validator: loginBloc.validatePassword, validator: loginBloc.validatePassword,
obscureText: loginBloc.obscureText, obscureText: loginBloc.obscureText,
keyboardType: TextInputType.visiblePassword, keyboardType: TextInputType.visiblePassword,
controller: loginBloc.loginPasswordController, controller:
loginBloc.loginPasswordController,
decoration: textBoxDecoration()!.copyWith( decoration: textBoxDecoration()!.copyWith(
hintText: 'At least 8 characters', hintText: 'At least 8 characters',
), ),
@ -220,7 +221,8 @@ class LoginMobilePage extends StatelessWidget {
}, },
child: Text( child: Text(
"Forgot Password?", "Forgot Password?",
style: Theme.of(context).textTheme.bodySmall, style:
Theme.of(context).textTheme.bodySmall,
), ),
), ),
], ],
@ -295,12 +297,15 @@ class LoginMobilePage extends StatelessWidget {
: ColorsManager.grayColor, : ColorsManager.grayColor,
child: const Text('Sign in'), child: const Text('Sign in'),
onPressed: () { onPressed: () {
if (loginBloc.loginFormKey.currentState!.validate()) { if (loginBloc.loginFormKey.currentState!
.validate()) {
loginBloc.add( loginBloc.add(
LoginButtonPressed( LoginButtonPressed(
regionUuid:'' , regionUuid: '',
username: loginBloc.loginEmailController.text, username:
password: loginBloc.loginPasswordController.text, loginBloc.loginEmailController.text,
password: loginBloc
.loginPasswordController.text,
), ),
); );
} }

View File

@ -9,8 +9,6 @@ class LoginPage extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return const ResponsiveLayout( return const ResponsiveLayout(
desktopBody: LoginWebPage(), desktopBody: LoginWebPage(), mobileBody: LoginWebPage());
mobileBody:LoginWebPage()
);
} }
} }

View File

@ -24,7 +24,6 @@ class LoginWebPage extends StatefulWidget {
} }
class _LoginWebPageState extends State<LoginWebPage> { class _LoginWebPageState extends State<LoginWebPage> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Scaffold( return Scaffold(
@ -36,7 +35,7 @@ class _LoginWebPageState extends State<LoginWebPage> {
// Navigate to home screen after successful login // Navigate to home screen after successful login
Navigator.pushReplacement( Navigator.pushReplacement(
context, context,
MaterialPageRoute(builder: (context) => HomePage()), MaterialPageRoute(builder: (context) => HomePage()),
); );
} else if (state is LoginFailure) { } else if (state is LoginFailure) {
// Show error message // Show error message
@ -48,26 +47,28 @@ class _LoginWebPageState extends State<LoginWebPage> {
} }
}, },
builder: (context, state) { builder: (context, state) {
return _buildLoginForm(context,state); return _buildLoginForm(context, state);
}, },
), ),
), ),
); );
} }
Widget _buildLoginForm(BuildContext context,AuthState state) { Widget _buildLoginForm(BuildContext context, AuthState state) {
final loginBloc = BlocProvider.of<AuthBloc>(context); final loginBloc = BlocProvider.of<AuthBloc>(context);
Size size = MediaQuery.of(context).size; Size size = MediaQuery.of(context).size;
late ScrollController _scrollController; late ScrollController _scrollController;
_scrollController = ScrollController(); _scrollController = ScrollController();
void _scrollToCenter() { void _scrollToCenter() {
final double middlePosition = _scrollController.position.maxScrollExtent / 2; final double middlePosition =
_scrollController.position.maxScrollExtent / 2;
_scrollController.animateTo( _scrollController.animateTo(
middlePosition, middlePosition,
duration: const Duration(seconds: 1), duration: const Duration(seconds: 1),
curve: Curves.easeInOut, curve: Curves.easeInOut,
); );
} }
WidgetsBinding.instance.addPostFrameCallback((_) { WidgetsBinding.instance.addPostFrameCallback((_) {
_scrollToCenter(); _scrollToCenter();
}); });
@ -80,14 +81,14 @@ class _LoginWebPageState extends State<LoginWebPage> {
shrinkWrap: true, shrinkWrap: true,
children: [ children: [
Container( Container(
padding: EdgeInsets.all(size.width*0.02) , padding: EdgeInsets.all(size.width * 0.02),
margin: EdgeInsets.all(size.width*0.09), margin: EdgeInsets.all(size.width * 0.09),
decoration: BoxDecoration( decoration: BoxDecoration(
color: Colors.black.withOpacity(0.3), color: Colors.black.withOpacity(0.3),
borderRadius: const BorderRadius.all(Radius.circular(20)), borderRadius: const BorderRadius.all(Radius.circular(20)),
), ),
child: Center( child: Center(
child:Row( child: Row(
crossAxisAlignment: CrossAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center,
children: [ children: [
@ -101,92 +102,140 @@ class _LoginWebPageState extends State<LoginWebPage> {
const Spacer(), const Spacer(),
Expanded( Expanded(
flex: 3, flex: 3,
child: Container( child: Container(
decoration: BoxDecoration( decoration: BoxDecoration(
color: Colors.white.withOpacity(0.1), color: Colors.white.withOpacity(0.1),
borderRadius: const BorderRadius.all(Radius.circular(30)), borderRadius: const BorderRadius.all(
border: Border.all(color: ColorsManager.graysColor.withOpacity(0.2))), Radius.circular(30)),
border: Border.all(
color: ColorsManager.graysColor
.withOpacity(0.2))),
child: Form( child: Form(
key: loginBloc.loginFormKey, key: loginBloc.loginFormKey,
child: Padding( child: Padding(
padding: EdgeInsets.symmetric( padding: EdgeInsets.symmetric(
horizontal: size.width*0.02, horizontal: size.width * 0.02,
vertical: size.width*0.003), vertical: size.width * 0.003),
child: Column( child: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly, mainAxisAlignment:
crossAxisAlignment: CrossAxisAlignment.start, MainAxisAlignment.spaceEvenly,
crossAxisAlignment:
CrossAxisAlignment.start,
children: <Widget>[ children: <Widget>[
const SizedBox(height: 40), const SizedBox(height: 40),
Text( Text('Login',
'Login', style: Theme.of(context)
style:Theme.of(context).textTheme.headlineLarge), .textTheme
SizedBox(height: size.height*0.03), .headlineLarge),
SizedBox(height: size.height * 0.03),
Column( Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment:
mainAxisAlignment: MainAxisAlignment.start, CrossAxisAlignment.start,
mainAxisAlignment:
MainAxisAlignment.start,
children: [ children: [
Text( Text(
"Country/Region", "Country/Region",
style: Theme.of(context).textTheme.bodySmall!.copyWith(fontSize: 14,fontWeight: FontWeight.w400), style: Theme.of(context)
.textTheme
.bodySmall!
.copyWith(
fontSize: 14,
fontWeight:
FontWeight.w400),
),
const SizedBox(
height: 10,
), ),
const SizedBox(height: 10,),
SizedBox( SizedBox(
child: DropdownButtonFormField<String>( child: DropdownButtonFormField<
String>(
padding: EdgeInsets.zero, padding: EdgeInsets.zero,
value: loginBloc.regionList!.any((region) => region.id == loginBloc.regionUuid) value: loginBloc.regionList!
.any((region) =>
region.id ==
loginBloc
.regionUuid)
? loginBloc.regionUuid ? loginBloc.regionUuid
: null, : null,
validator:
validator: loginBloc.validateRegion, loginBloc.validateRegion,
icon: const Icon( icon: const Icon(
Icons.keyboard_arrow_down_outlined, Icons
.keyboard_arrow_down_outlined,
), ),
decoration: textBoxDecoration()!.copyWith( decoration: textBoxDecoration()!
errorStyle: const TextStyle(height: 0), .copyWith(
errorStyle: const TextStyle(
height: 0),
hintText: null, hintText: null,
), ),
hint: SizedBox( hint: SizedBox(
width: size.width * 0.12, width: size.width * 0.12,
child: Align( child: Align(
alignment: Alignment.centerLeft, alignment:
Alignment.centerLeft,
child: Text( child: Text(
'Select your region/country', 'Select your region/country',
textAlign: TextAlign.center, textAlign:
style: Theme.of(context).textTheme.bodySmall!.copyWith( TextAlign.center,
color: ColorsManager.grayColor, style: Theme.of(context)
fontWeight: FontWeight.w400), .textTheme
overflow: TextOverflow.ellipsis, .bodySmall!
.copyWith(
color:
ColorsManager
.grayColor,
fontWeight:
FontWeight
.w400),
overflow:
TextOverflow.ellipsis,
), ),
), ),
), ),
isDense: true, isDense: true,
style: const TextStyle(color: Colors.black), style: const TextStyle(
items: loginBloc.regionList!.map((RegionModel region) { color: Colors.black),
return DropdownMenuItem<String>( items: loginBloc.regionList!
.map((RegionModel region) {
return DropdownMenuItem<
String>(
value: region.id, value: region.id,
child: SizedBox( child: SizedBox(
width: size.width*0.08, width:
child: Text(region.name)), size.width * 0.08,
child:
Text(region.name)),
); );
}).toList(), }).toList(),
onChanged: (String? value) { onChanged: (String? value) {
loginBloc.add(CheckEnableEvent()); loginBloc
loginBloc.add(SelectRegionEvent(val: value!)); .add(CheckEnableEvent());
loginBloc.add(
SelectRegionEvent(
val: value!));
}, },
), ),
) )
], ],
), ),
const SizedBox(height: 20.0), const SizedBox(height: 20.0),
Column( Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment:
mainAxisAlignment: MainAxisAlignment.start, CrossAxisAlignment.start,
mainAxisAlignment:
MainAxisAlignment.start,
children: [ children: [
Text("Email", Text(
style: Theme.of(context).textTheme.bodySmall!.copyWith(fontSize: 14,fontWeight: FontWeight.w400), "Email",
style: Theme.of(context)
.textTheme
.bodySmall!
.copyWith(
fontSize: 14,
fontWeight:
FontWeight.w400),
), ),
const SizedBox( const SizedBox(
height: 10, height: 10,
@ -194,30 +243,53 @@ class _LoginWebPageState extends State<LoginWebPage> {
SizedBox( SizedBox(
child: TextFormField( child: TextFormField(
onChanged: (value) { onChanged: (value) {
loginBloc.add(CheckEnableEvent()); loginBloc
// print(loginBloc.checkEnable()); .add(CheckEnableEvent());
// print(loginBloc.checkEnable());
}, },
validator:loginBloc.loginValidateEmail , validator: loginBloc
controller:loginBloc.loginEmailController, .loginValidateEmail,
decoration: textBoxDecoration()!.copyWith( controller: loginBloc
errorStyle: const TextStyle(height: 0), // Hide the error text space .loginEmailController,
hintText: 'Enter your email address', decoration: textBoxDecoration()!
hintStyle: Theme.of(context).textTheme.bodySmall!.copyWith( .copyWith(
color: ColorsManager.grayColor, errorStyle: const TextStyle(
fontWeight: FontWeight.w400) height:
), 0), // Hide the error text space
style: const TextStyle(color: Colors.black), hintText:
'Enter your email address',
hintStyle: Theme.of(
context)
.textTheme
.bodySmall!
.copyWith(
color: ColorsManager
.grayColor,
fontWeight:
FontWeight
.w400)),
style: const TextStyle(
color: Colors.black),
), ),
), ),
], ],
), ),
const SizedBox(height: 20.0), const SizedBox(height: 20.0),
Column( Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment:
mainAxisAlignment: MainAxisAlignment.start, CrossAxisAlignment.start,
mainAxisAlignment:
MainAxisAlignment.start,
children: [ children: [
Text("Password", Text(
style: Theme.of(context).textTheme.bodySmall!.copyWith(fontSize: 14,fontWeight: FontWeight.w400), "Password",
style: Theme.of(context)
.textTheme
.bodySmall!
.copyWith(
fontSize: 14,
fontWeight:
FontWeight.w400),
), ),
const SizedBox( const SizedBox(
height: 10, height: 10,
@ -225,33 +297,54 @@ class _LoginWebPageState extends State<LoginWebPage> {
SizedBox( SizedBox(
child: TextFormField( child: TextFormField(
onChanged: (value) { onChanged: (value) {
loginBloc.add(CheckEnableEvent()); loginBloc
}, .add(CheckEnableEvent());
validator:loginBloc.validatePassword, },
obscureText:loginBloc.obscureText, validator:
keyboardType: TextInputType.visiblePassword, loginBloc.validatePassword,
controller:loginBloc.loginPasswordController, obscureText:
decoration: textBoxDecoration()!.copyWith( loginBloc.obscureText,
hintText: 'At least 8 characters', keyboardType: TextInputType
hintStyle: Theme.of(context).textTheme.bodySmall!.copyWith( .visiblePassword,
color: ColorsManager.grayColor, controller: loginBloc
fontWeight: FontWeight.w400), .loginPasswordController,
suffixIcon: IconButton(onPressed: () { decoration: textBoxDecoration()!
loginBloc.add(PasswordVisibleEvent(newValue: loginBloc.obscureText)); .copyWith(
hintText:
'At least 8 characters',
hintStyle: Theme.of(context)
.textTheme
.bodySmall!
.copyWith(
color: ColorsManager
.grayColor,
fontWeight:
FontWeight.w400),
suffixIcon: IconButton(
onPressed: () {
loginBloc.add(
PasswordVisibleEvent(
newValue: loginBloc
.obscureText));
}, },
icon: SizedBox( icon: SizedBox(
child: SvgPicture.asset( child: SvgPicture.asset(
loginBloc.obscureText? loginBloc.obscureText
Assets.visiblePassword : ? Assets
Assets.invisiblePassword, .visiblePassword
height: 15, : Assets
width: 15, .invisiblePassword,
), height: 15,
width: 15,
), ),
), ),
errorStyle: const TextStyle(height: 0), // Hide the error text space ),
errorStyle: const TextStyle(
height:
0), // Hide the error text space
), ),
style: const TextStyle(color: Colors.black), style: const TextStyle(
color: Colors.black),
), ),
), ),
], ],
@ -261,15 +354,27 @@ class _LoginWebPageState extends State<LoginWebPage> {
), ),
SizedBox( SizedBox(
child: Row( child: Row(
mainAxisAlignment: MainAxisAlignment.end, mainAxisAlignment:
MainAxisAlignment.end,
children: [ children: [
InkWell( InkWell(
onTap: () { onTap: () {
Navigator.of(context).push(MaterialPageRoute(builder: (context) => const ForgetPasswordPage(),)); Navigator.of(context)
.push(MaterialPageRoute(
builder: (context) =>
const ForgetPasswordPage(),
));
}, },
child: Text( child: Text(
"Forgot Password?", "Forgot Password?",
style: Theme.of(context).textTheme.bodySmall!.copyWith(color: Colors.black,fontSize: 14,fontWeight: FontWeight.w400), style: Theme.of(context)
.textTheme
.bodySmall!
.copyWith(
color: Colors.black,
fontSize: 14,
fontWeight:
FontWeight.w400),
), ),
), ),
], ],
@ -281,53 +386,65 @@ class _LoginWebPageState extends State<LoginWebPage> {
Row( Row(
children: [ children: [
Transform.scale( Transform.scale(
scale: 1.2, // Adjust the scale as needed scale:
1.2, // Adjust the scale as needed
child: Checkbox( child: Checkbox(
fillColor: MaterialStateProperty.all<Color>(Colors.white), fillColor: MaterialStateProperty
.all<Color>(Colors.white),
activeColor: Colors.white, activeColor: Colors.white,
value:loginBloc.isChecked, value: loginBloc.isChecked,
checkColor: Colors.black, checkColor: Colors.black,
shape: const CircleBorder(), shape: const CircleBorder(),
onChanged: (bool? newValue) { onChanged: (bool? newValue) {
loginBloc.add(CheckBoxEvent(newValue: newValue)); loginBloc.add(CheckBoxEvent(
newValue: newValue));
}, },
), ),
), ),
SizedBox( SizedBox(
width:size.width * 0.14, width: size.width * 0.14,
child: RichText( child: RichText(
text: TextSpan( text: TextSpan(
text: 'Agree to ', text: 'Agree to ',
style: const TextStyle(color: Colors.white), style: const TextStyle(
color: Colors.white),
children: [ children: [
TextSpan( TextSpan(
text: '(Terms of Service)', text:
'(Terms of Service)',
style: const TextStyle( style: const TextStyle(
color: Colors.black,), color: Colors.black,
recognizer: TapGestureRecognizer() ),
..onTap = () { recognizer:
loginBloc.launchURL( TapGestureRecognizer()
'https://example.com/terms'); ..onTap = () {
}, loginBloc.launchURL(
'https://example.com/terms');
},
), ),
TextSpan( TextSpan(
text: ' (Legal Statement)', text:
style: const TextStyle(color: Colors.black), ' (Legal Statement)',
recognizer: TapGestureRecognizer()
..onTap = () {
loginBloc.launchURL(
'https://example.com/legal');
},
),
TextSpan(
text: ' (Privacy Statement)',
style: const TextStyle( style: const TextStyle(
color: Colors.black), color: Colors.black),
recognizer: TapGestureRecognizer() recognizer:
..onTap = () { TapGestureRecognizer()
loginBloc.launchURL( ..onTap = () {
'https://example.com/privacy'); 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');
},
), ),
], ],
), ),
@ -337,57 +454,87 @@ class _LoginWebPageState extends State<LoginWebPage> {
), ),
const SizedBox(height: 20.0), const SizedBox(height: 20.0),
Row( Row(
crossAxisAlignment: CrossAxisAlignment.center, crossAxisAlignment:
mainAxisAlignment: MainAxisAlignment.center, CrossAxisAlignment.center,
mainAxisAlignment:
MainAxisAlignment.center,
children: [ children: [
SizedBox( SizedBox(
width:size.width * 0.2, width: size.width * 0.2,
child: DefaultButton( child: DefaultButton(
enabled: loginBloc.checkValidate, enabled:
child:Text('Sign in', loginBloc.checkValidate,
style: Theme.of(context).textTheme.labelLarge !.copyWith( child: Text('Sign in',
fontSize: 14, style: Theme.of(context)
color: .textTheme
loginBloc.checkValidate ? .labelLarge!
ColorsManager.whiteColors:ColorsManager.whiteColors.withOpacity(0.2), .copyWith(
) fontSize: 14,
), color: loginBloc
.checkValidate
? ColorsManager
.whiteColors
: ColorsManager
.whiteColors
.withOpacity(
0.2),
)),
onPressed: () { onPressed: () {
if(loginBloc.loginFormKey.currentState!.validate() ){ if (loginBloc.loginFormKey
loginBloc.add(LoginButtonPressed( .currentState!
regionUuid:loginBloc.regionUuid, .validate()) {
username: loginBloc.loginEmailController.text, loginBloc
password: loginBloc.loginPasswordController.text, .add(LoginButtonPressed(
regionUuid:
loginBloc.regionUuid,
username: loginBloc
.loginEmailController
.text,
password: loginBloc
.loginPasswordController
.text,
)); ));
}else{ } else {
loginBloc.add(ChangeValidateEvent()); loginBloc.add(
ChangeValidateEvent());
} }
}, },
), ),
), ),
], ],
), ),
const SizedBox(height: 15.0), const SizedBox(height: 15.0),
Row( Row(
crossAxisAlignment: CrossAxisAlignment.center, crossAxisAlignment:
mainAxisAlignment: MainAxisAlignment.center, CrossAxisAlignment.center,
children: [ SizedBox(child: Text(loginBloc.validate, mainAxisAlignment:
style: const TextStyle(fontWeight: FontWeight.w700,color: ColorsManager.red ),),)],) MainAxisAlignment.center,
children: [
SizedBox(
child: Text(
loginBloc.validate,
style: const TextStyle(
fontWeight: FontWeight.w700,
color: ColorsManager.red),
),
)
],
)
], ],
), ),
), ),
) ))),
)),
const Spacer(), const Spacer(),
], ],
),), ),
),
), ),
], ],
), ),
), ),
), ),
if (state is AuthLoading) if (state is AuthLoading)
const Center(child: CircularProgressIndicator()) const Center(child: CircularProgressIndicator())
], ],
); );
} }

View File

@ -38,60 +38,58 @@ class DefaultButton extends StatelessWidget {
style: isSecondary style: isSecondary
? null ? null
: customButtonStyle ?? : customButtonStyle ??
ButtonStyle( ButtonStyle(
textStyle: MaterialStateProperty.all( textStyle: MaterialStateProperty.all(
customTextStyle customTextStyle ??
?? Theme.of(context).textTheme.bodySmall!.copyWith( Theme.of(context).textTheme.bodySmall!.copyWith(
fontSize: 13, fontSize: 13,
color: foregroundColor, color: foregroundColor,
fontWeight: FontWeight.normal fontWeight: FontWeight.normal),
), ),
), foregroundColor: MaterialStateProperty.all(
isSecondary
foregroundColor: MaterialStateProperty.all( ? Colors.black
isSecondary : enabled
? Colors.black ? foregroundColor ?? Colors.white
: enabled : Colors.black,
? foregroundColor ?? Colors.white ),
: Colors.black, backgroundColor: MaterialStateProperty.resolveWith<Color>(
),
backgroundColor: MaterialStateProperty.resolveWith<Color>(
(Set<MaterialState> states) { (Set<MaterialState> states) {
return enabled return enabled
? backgroundColor ?? ColorsManager.primaryColor ? backgroundColor ?? ColorsManager.primaryColor
: Colors.black.withOpacity(0.2); : Colors.black.withOpacity(0.2);
}), }),
shape: MaterialStateProperty.all( shape: MaterialStateProperty.all(
RoundedRectangleBorder( RoundedRectangleBorder(
borderRadius: BorderRadius.circular(borderRadius ?? 20), borderRadius: BorderRadius.circular(borderRadius ?? 20),
),
),
fixedSize: MaterialStateProperty.all(
const Size.fromHeight(50),
),
padding: MaterialStateProperty.all(
EdgeInsets.all(padding ?? 10),
),
minimumSize: MaterialStateProperty.all(
const Size.fromHeight(50),
),
), ),
),
fixedSize: MaterialStateProperty.all(
const Size.fromHeight(50),
),
padding: MaterialStateProperty.all(
EdgeInsets.all(padding ?? 10),
),
minimumSize: MaterialStateProperty.all(
const Size.fromHeight(50),
),
),
child: SizedBox( child: SizedBox(
height: height ?? 50, height: height ?? 50,
child: Center( child: Center(
child: isLoading child: isLoading
? const SizedBox.square( ? const SizedBox.square(
dimension: 24, dimension: 24,
child: CircularProgressIndicator( child: CircularProgressIndicator(
color: Colors.white, color: Colors.white,
), ),
) )
: isDone : isDone
? const Icon( ? const Icon(
Icons.check_circle_outline, Icons.check_circle_outline,
color: Colors.white, color: Colors.white,
) )
: child, : child,
), ),
), ),
); );

View File

@ -11,7 +11,8 @@ Future<void> showCustomDialog({
double? iconHeight, double? iconHeight,
double? iconWidth, double? iconWidth,
VoidCallback? onOkPressed, VoidCallback? onOkPressed,
bool barrierDismissible = false, required actions, bool barrierDismissible = false,
required actions,
}) { }) {
return showDialog( return showDialog(
context: context, context: context,

View File

@ -224,8 +224,14 @@ class _DynamicTableState extends State<DynamicTable> {
alignment: Alignment.centerLeft, alignment: Alignment.centerLeft,
child: Text( child: Text(
content, content,
style: const TextStyle( style: TextStyle(
color: Colors.black, fontSize: 10, fontWeight: FontWeight.w400), color: content == 'Online'
? ColorsManager.green
: content == 'Offline'
? ColorsManager.red
: Colors.black,
fontSize: 12,
fontWeight: FontWeight.w400),
), ),
), ),
); );

View File

@ -1,7 +1,6 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart'; import 'package:flutter_svg/flutter_svg.dart';
import 'package:syncrow_web/utils/color_manager.dart'; import 'package:syncrow_web/utils/color_manager.dart';
import 'package:syncrow_web/utils/constants/assets.dart';
import 'package:syncrow_web/utils/style.dart'; import 'package:syncrow_web/utils/style.dart';
class DateTimeWebWidget extends StatelessWidget { class DateTimeWebWidget extends StatelessWidget {
@ -23,8 +22,8 @@ class DateTimeWebWidget extends StatelessWidget {
final String firstString; final String firstString;
final String secondString; final String secondString;
final String icon; final String icon;
final Function()? startTime; final Function()? startTime;
final Function()? endTime; final Function()? endTime;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
@ -32,61 +31,84 @@ class DateTimeWebWidget extends StatelessWidget {
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.start,
children: [ children: [
Row( Row(
children: [ children: [
if(isRequired) if (isRequired)
Text( Text(
'* ', '* ',
style: Theme.of(context) style: Theme.of(context)
.textTheme .textTheme
.bodyMedium! .bodyMedium!
.copyWith(color: Colors.red), .copyWith(color: Colors.red),
), ),
Text(title??'' , Text(
style: Theme.of(context).textTheme.bodySmall!.copyWith( title,
color: Colors.black,fontSize: 13),), style: Theme.of(context)
], .textTheme
), .bodySmall!
const SizedBox(height: 8,), .copyWith(color: Colors.black, fontSize: 13),
),
],
),
const SizedBox(
height: 8,
),
Container( Container(
height:size.height * 0.055 , height: size.height * 0.055,
padding: EdgeInsets.only(top: 10,bottom: 10,right: 30,left: 10), padding:
const EdgeInsets.only(top: 10, bottom: 10, right: 30, left: 10),
decoration: containerDecoration, decoration: containerDecoration,
child: FittedBox( child: FittedBox(
child: Column( child: Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [ children: [
Row( InkWell(
mainAxisAlignment: MainAxisAlignment.spaceBetween, onTap: startTime,
children: [ child: FittedBox(
InkWell( child: Text(
onTap: startTime, firstString,
child: FittedBox( style: Theme.of(context)
child: Text(firstString, .textTheme
style: Theme.of(context).textTheme.bodySmall!.copyWith( .bodySmall!
color: ColorsManager.grayColor,fontSize: 12,fontWeight: FontWeight.w400),), .copyWith(
) color: ColorsManager.grayColor,
), fontSize: 12,
SizedBox(width: 30,), fontWeight: FontWeight.w400),
const Icon(Icons.arrow_right_alt), ),
SizedBox(width: 30,), )),
const SizedBox(
InkWell( width: 30,
onTap:endTime, ),
child: FittedBox( const Icon(Icons.arrow_right_alt),
child: Text(secondString, const SizedBox(
style: Theme.of(context).textTheme.bodySmall!.copyWith( width: 30,
color: ColorsManager.grayColor,fontSize: 12,fontWeight: FontWeight.w400), ),
), InkWell(
)), onTap: endTime,
SizedBox(width: 30,), child: FittedBox(
child: Text(
SvgPicture.asset( secondString,
icon, style: Theme.of(context)
), .textTheme
], .bodySmall!
.copyWith(
color: ColorsManager.grayColor,
fontSize: 12,
fontWeight: FontWeight.w400),
),
)),
const SizedBox(
width: 30,
),
SvgPicture.asset(
icon,
), ),
], ],
)), ),
],
)),
), ),
], ],
); );

View File

@ -4,7 +4,7 @@ import 'package:syncrow_web/utils/constants/assets.dart';
class FirstLayer extends StatelessWidget { class FirstLayer extends StatelessWidget {
final Widget? second; final Widget? second;
const FirstLayer({super.key,this.second}); const FirstLayer({super.key, this.second});
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {

View File

@ -1,5 +1,3 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
class HourPickerDialog extends StatefulWidget { class HourPickerDialog extends StatefulWidget {
@ -17,7 +15,9 @@ class _HourPickerDialogState extends State<HourPickerDialog> {
@override @override
void initState() { void initState() {
super.initState(); super.initState();
_selectedHour = widget.initialTime.hour > 12 ? widget.initialTime.hour - 12 : widget.initialTime.hour; _selectedHour = widget.initialTime.hour > 12
? widget.initialTime.hour - 12
: widget.initialTime.hour;
_isPm = widget.initialTime.period == DayPeriod.pm; _isPm = widget.initialTime.period == DayPeriod.pm;
} }

View File

@ -35,7 +35,6 @@ class InfoDialog extends StatelessWidget {
width: 35, width: 35,
), ),
), ),
Text( Text(
title, title,
style: Theme.of(context).textTheme.headlineLarge!.copyWith( style: Theme.of(context).textTheme.headlineLarge!.copyWith(

View File

@ -8,37 +8,25 @@ class StatefulTextField extends StatefulWidget {
this.hintText = 'Please enter', this.hintText = 'Please enter',
required this.width, required this.width,
this.elevation = 0, this.elevation = 0,
required this.controller, // Add the controller
}); });
final String title; final String title;
final String hintText; final String hintText;
final double width; final double width;
final double elevation; final double elevation;
final TextEditingController controller;
@override @override
State<StatefulTextField> createState() => _StatefulTextFieldState(); State<StatefulTextField> createState() => _StatefulTextFieldState();
} }
class _StatefulTextFieldState extends State<StatefulTextField> { class _StatefulTextFieldState extends State<StatefulTextField> {
late TextEditingController _controller;
@override
void initState() {
super.initState();
_controller = TextEditingController();
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return CustomTextField( return CustomTextField(
title: widget.title, title: widget.title,
controller: _controller, controller: widget.controller,
hintText: widget.hintText, hintText: widget.hintText,
width: widget.width, width: widget.width,
elevation: widget.elevation, elevation: widget.elevation,

View File

@ -1,23 +1,22 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:syncrow_web/utils/color_manager.dart'; import 'package:syncrow_web/utils/color_manager.dart';
import 'package:syncrow_web/utils/style.dart'; import 'package:syncrow_web/utils/style.dart';
class CustomWebTextField extends StatelessWidget { class CustomWebTextField extends StatelessWidget {
const CustomWebTextField({ const CustomWebTextField({
super.key, super.key,
required this.isRequired, required this.isRequired,
required this.textFieldName, required this.textFieldName,
required this.controller, required this.controller,
this.description, this.description,
this.validator, this.validator,
}); });
final bool isRequired; final bool isRequired;
final String textFieldName; final String textFieldName;
final String? description; final String? description;
final TextEditingController? controller; final TextEditingController? controller;
final String? Function(String?)? validator; final String? Function(String?)? validator;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
@ -28,51 +27,59 @@ class CustomWebTextField extends StatelessWidget {
Row( Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [ children: [
if(isRequired) if (isRequired)
Row( Row(
children: [ children: [
Text('* ', Text(
style: Theme.of(context) '* ',
.textTheme.bodyMedium! style: Theme.of(context)
.copyWith(color: Colors.red), .textTheme
), .bodyMedium!
Text(textFieldName, style: Theme.of(context).textTheme.bodySmall!.copyWith( .copyWith(color: Colors.red),
color: Colors.black,fontSize: 13),), ),
], Text(
textFieldName,
style: Theme.of(context)
.textTheme
.bodySmall!
.copyWith(color: Colors.black, fontSize: 13),
),
],
),
const SizedBox(
width: 10,
), ),
const SizedBox(width: 10,),
Expanded( Expanded(
child: Text( child: Text(
description??'', description ?? '',
style: Theme.of(context) style: Theme.of(context).textTheme.bodySmall!.copyWith(
.textTheme.bodySmall! fontSize: 9,
.copyWith(fontSize: 9,
fontWeight: FontWeight.w400, fontWeight: FontWeight.w400,
color: ColorsManager.textGray), color: ColorsManager.textGray),
), ),
), ),
], ],
), ),
const SizedBox(height: 7,), const SizedBox(
height: 7,
),
Container( Container(
decoration: containerDecoration.copyWith( decoration: containerDecoration
color: const Color(0xFFF5F6F7), .copyWith(color: const Color(0xFFF5F6F7), boxShadow: [
boxShadow: [ BoxShadow(
BoxShadow( color: Colors.grey.withOpacity(0.3),
color: Colors.grey.withOpacity(0.3), spreadRadius: 2,
spreadRadius:2, blurRadius: 3,
blurRadius: 3, offset: const Offset(1, 1), // changes position of shadow
offset: const Offset(1, 1), // changes position of shadow ),
), ]),
]
),
child: TextFormField( child: TextFormField(
validator: validator, validator: validator,
controller: controller, controller: controller,
style: const TextStyle(color: Colors.black), style: const TextStyle(color: Colors.black),
decoration: textBoxDecoration()! decoration: textBoxDecoration()!.copyWith(
.copyWith( errorStyle:
errorStyle: const TextStyle(height: 0), // Hide the error text space const TextStyle(height: 0), // Hide the error text space
hintText: 'Please enter'), hintText: 'Please enter'),
), ),

View File

@ -1,13 +1,130 @@
import 'package:bloc/bloc.dart'; import 'package:bloc/bloc.dart';
import 'package:equatable/equatable.dart'; import 'package:equatable/equatable.dart';
import 'package:syncrow_web/pages/device_managment/models/devices_model.dart';
import 'package:syncrow_web/services/devices_mang_api.dart';
part 'device_managment_event.dart'; part 'device_managment_event.dart';
part 'device_managment_state.dart'; part 'device_managment_state.dart';
class DeviceManagmentBloc extends Bloc<DeviceManagmentEvent, DeviceManagmentState> { class DeviceManagementBloc
DeviceManagmentBloc() : super(DeviceManagmentInitial()) { extends Bloc<DeviceManagementEvent, DeviceManagementState> {
on<DeviceManagmentEvent>((event, emit) { int _selectedIndex = 0;
// TODO: implement event handler List<AllDevicesModel> _devices = [];
}); int _onlineCount = 0;
int _offlineCount = 0;
int _lowBatteryCount = 0;
DeviceManagementBloc() : super(DeviceManagementInitial()) {
on<FetchDevices>(_onFetchDevices);
on<FilterDevices>(_onFilterDevices);
on<SelectedFilterChanged>(_onSelectedFilterChanged);
on<SearchDevices>(_onSearchDevices);
}
Future<void> _onFetchDevices(
FetchDevices event, Emitter<DeviceManagementState> emit) async {
emit(DeviceManagementLoading());
try {
final devices = await DevicesManagementApi().fetchDevices();
_devices = devices;
_calculateDeviceCounts();
emit(DeviceManagementLoaded(
devices: devices,
selectedIndex: _selectedIndex,
onlineCount: _onlineCount,
offlineCount: _offlineCount,
lowBatteryCount: _lowBatteryCount,
));
} catch (e) {
emit(DeviceManagementInitial());
}
}
void _onFilterDevices(
FilterDevices event, Emitter<DeviceManagementState> emit) {
if (_devices.isNotEmpty) {
final filteredDevices = _devices.where((device) {
switch (event.filter) {
case 'Online':
return device.online == true;
case 'Offline':
return device.online == false;
case 'Low Battery':
return device.batteryLevel != null && device.batteryLevel! < 20;
default:
return true;
}
}).toList();
emit(DeviceManagementFiltered(
filteredDevices: filteredDevices,
selectedIndex: _selectedIndex,
onlineCount: _onlineCount,
offlineCount: _offlineCount,
lowBatteryCount: _lowBatteryCount,
));
}
}
void _onSelectedFilterChanged(
SelectedFilterChanged event, Emitter<DeviceManagementState> emit) {
_selectedIndex = event.selectedIndex;
add(FilterDevices(_getFilterFromIndex(_selectedIndex)));
}
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;
}
String _getFilterFromIndex(int index) {
switch (index) {
case 1:
return 'Online';
case 2:
return 'Offline';
case 3:
return 'Low Battery';
default:
return 'All';
}
}
void _onSearchDevices(
SearchDevices event, Emitter<DeviceManagementState> emit) {
if (_devices.isNotEmpty) {
final filteredDevices = _devices.where((device) {
final matchesCommunity = event.community == null ||
event.community!.isEmpty ||
(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);
final matchesProductName = event.productName == null ||
event.productName!.isEmpty ||
(device.name
?.toLowerCase()
.contains(event.productName!.toLowerCase()) ??
false);
return matchesCommunity && matchesUnit && matchesProductName;
}).toList();
emit(DeviceManagementFiltered(
filteredDevices: filteredDevices,
selectedIndex: _selectedIndex,
onlineCount: _onlineCount,
offlineCount: _offlineCount,
lowBatteryCount: _lowBatteryCount,
));
}
} }
} }

View File

@ -1,8 +1,43 @@
part of 'device_managment_bloc.dart'; part of 'device_managment_bloc.dart';
sealed class DeviceManagmentEvent extends Equatable { abstract class DeviceManagementEvent extends Equatable {
const DeviceManagmentEvent(); const DeviceManagementEvent();
@override @override
List<Object> get props => []; List<Object?> get props => [];
}
class FetchDevices extends DeviceManagementEvent {}
class FilterDevices extends DeviceManagementEvent {
final String filter;
const FilterDevices(this.filter);
@override
List<Object?> get props => [filter];
}
class SelectedFilterChanged extends DeviceManagementEvent {
final int selectedIndex;
const SelectedFilterChanged(this.selectedIndex);
@override
List<Object?> get props => [selectedIndex];
}
class SearchDevices extends DeviceManagementEvent {
final String? community;
final String? unitName;
final String? productName;
const SearchDevices({
this.community,
this.unitName,
this.productName,
});
@override
List<Object?> get props => [community, unitName, productName];
} }

View File

@ -1,10 +1,57 @@
part of 'device_managment_bloc.dart'; part of 'device_managment_bloc.dart';
sealed class DeviceManagmentState extends Equatable { abstract class DeviceManagementState extends Equatable {
const DeviceManagmentState(); const DeviceManagementState();
@override @override
List<Object> get props => []; List<Object?> get props => [];
} }
final class DeviceManagmentInitial extends DeviceManagmentState {} class DeviceManagementInitial extends DeviceManagementState {}
class DeviceManagementLoading extends DeviceManagementState {}
class DeviceManagementLoaded extends DeviceManagementState {
final List<AllDevicesModel> devices;
final int selectedIndex;
final int onlineCount;
final int offlineCount;
final int lowBatteryCount;
const DeviceManagementLoaded({
required this.devices,
required this.selectedIndex,
required this.onlineCount,
required this.offlineCount,
required this.lowBatteryCount,
});
@override
List<Object?> get props =>
[devices, selectedIndex, onlineCount, offlineCount, lowBatteryCount];
}
class DeviceManagementFiltered extends DeviceManagementState {
final List<AllDevicesModel> filteredDevices;
final int selectedIndex;
final int onlineCount;
final int offlineCount;
final int lowBatteryCount;
const DeviceManagementFiltered({
required this.filteredDevices,
required this.selectedIndex,
required this.onlineCount,
required this.offlineCount,
required this.lowBatteryCount,
});
@override
List<Object?> get props => [
filteredDevices,
selectedIndex,
onlineCount,
offlineCount,
lowBatteryCount
];
}

View File

@ -0,0 +1,157 @@
import 'package:syncrow_web/pages/device_managment/models/room.dart';
import 'package:syncrow_web/pages/device_managment/models/unit.dart';
class AllDevicesModel {
/*
{
"room": {
"uuid": "75ea7d60-5104-4726-b5f8-ea426c0c6a1b",
"name": "Room 1"
},
"unit": {
"uuid": "04fd1dcf-f24a-40db-970d-d0be884ed30f",
"name": "unit 1"
},
"productUuid": "894aad5c-ce03-423a-9d61-2fd0c3f67ebf",
"productType": "3G",
"permissionType": "CONTROLLABLE",
"activeTime": 1722173778,
"category": "kg",
"categoryName": "Switch",
"createTime": 1722173778,
"gatewayId": "bf0294123ed2c19067skrk",
"icon": "smart/icon/bay1642572935385vcsA/2b1f5efbaa5bbf81c3164fa312cf2032.png",
"ip": "",
"lat": "31.97",
"localKey": "T/39+<l/![iv>:9M",
"lon": "35.89",
"model": "S01ZLSWBSA3",
"name": "3 Gang Button Switch L-L",
"nodeId": "60a423fffed5a7f6",
"online": true,
"ownerId": "199200732",
"sub": true,
"timeZone": "+03:00",
"updateTime": 1723626515,
"uuid": "5b31dae4-ce9c-4c70-b52b-7e15011163bf"
}
*/
DevicesModelRoom? room;
DevicesModelUnit? unit;
String? productUuid;
String? productType;
String? permissionType;
int? activeTime;
String? category;
String? categoryName;
int? createTime;
String? gatewayId;
String? icon;
String? ip;
String? lat;
String? localKey;
String? lon;
String? model;
String? name;
String? nodeId;
bool? online;
String? ownerId;
bool? sub;
String? timeZone;
int? updateTime;
String? uuid;
int? batteryLevel;
AllDevicesModel({
this.room,
this.unit,
this.productUuid,
this.productType,
this.permissionType,
this.activeTime,
this.category,
this.categoryName,
this.createTime,
this.gatewayId,
this.icon,
this.ip,
this.lat,
this.localKey,
this.lon,
this.model,
this.name,
this.nodeId,
this.online,
this.ownerId,
this.sub,
this.timeZone,
this.updateTime,
this.uuid,
this.batteryLevel,
});
AllDevicesModel.fromJson(Map<String, dynamic> json) {
room = (json['room'] != null && (json['room'] is Map))
? DevicesModelRoom.fromJson(json['room'])
: null;
unit = (json['unit'] != null && (json['unit'] is Map))
? DevicesModelUnit.fromJson(json['unit'])
: null;
productUuid = json['productUuid']?.toString();
productType = json['productType']?.toString();
permissionType = json['permissionType']?.toString();
activeTime = int.tryParse(json['activeTime']?.toString() ?? '');
category = json['category']?.toString();
categoryName = json['categoryName']?.toString();
createTime = int.tryParse(json['createTime']?.toString() ?? '');
gatewayId = json['gatewayId']?.toString();
icon = json['icon']?.toString();
ip = json['ip']?.toString();
lat = json['lat']?.toString();
localKey = json['localKey']?.toString();
lon = json['lon']?.toString();
model = json['model']?.toString();
name = json['name']?.toString();
nodeId = json['nodeId']?.toString();
online = json['online'];
ownerId = json['ownerId']?.toString();
sub = json['sub'];
timeZone = json['timeZone']?.toString();
updateTime = int.tryParse(json['updateTime']?.toString() ?? '');
uuid = json['uuid']?.toString();
batteryLevel = int.tryParse(json['batteryLevel']?.toString() ?? '');
}
Map<String, dynamic> toJson() {
final data = <String, dynamic>{};
if (room != null) {
data['room'] = room!.toJson();
}
if (unit != null) {
data['unit'] = unit!.toJson();
}
data['productUuid'] = productUuid;
data['productType'] = productType;
data['permissionType'] = permissionType;
data['activeTime'] = activeTime;
data['category'] = category;
data['categoryName'] = categoryName;
data['createTime'] = createTime;
data['gatewayId'] = gatewayId;
data['icon'] = icon;
data['ip'] = ip;
data['lat'] = lat;
data['localKey'] = localKey;
data['lon'] = lon;
data['model'] = model;
data['name'] = name;
data['nodeId'] = nodeId;
data['online'] = online;
data['ownerId'] = ownerId;
data['sub'] = sub;
data['timeZone'] = timeZone;
data['updateTime'] = updateTime;
data['uuid'] = uuid;
data['batteryLevel'] = batteryLevel;
return data;
}
}

View File

@ -0,0 +1,26 @@
class DevicesModelRoom {
/*
{
"uuid": "75ea7d60-5104-4726-b5f8-ea426c0c6a1b",
"name": "Room 1"
}
*/
String? uuid;
String? name;
DevicesModelRoom({
this.uuid,
this.name,
});
DevicesModelRoom.fromJson(Map<String, dynamic> json) {
uuid = json['uuid']?.toString();
name = json['name']?.toString();
}
Map<String, dynamic> toJson() {
final data = <String, dynamic>{};
data['uuid'] = uuid;
data['name'] = name;
return data;
}
}

View File

@ -0,0 +1,26 @@
class DevicesModelUnit {
/*
{
"uuid": "04fd1dcf-f24a-40db-970d-d0be884ed30f",
"name": "unit 1"
}
*/
String? uuid;
String? name;
DevicesModelUnit({
this.uuid,
this.name,
});
DevicesModelUnit.fromJson(Map<String, dynamic> json) {
uuid = json['uuid']?.toString();
name = json['name']?.toString();
}
Map<String, dynamic> toJson() {
final data = <String, dynamic>{};
data['uuid'] = uuid;
data['name'] = name;
return data;
}
}

View File

@ -1,4 +1,6 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:syncrow_web/pages/device_managment/bloc/device_managment_bloc.dart';
import 'package:syncrow_web/pages/device_managment/widgets/device_managment_body.dart'; import 'package:syncrow_web/pages/device_managment/widgets/device_managment_body.dart';
import 'package:syncrow_web/web_layout/web_scaffold.dart'; import 'package:syncrow_web/web_layout/web_scaffold.dart';
@ -7,17 +9,31 @@ class DeviceManagementPage extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return WebScaffold( return BlocProvider(
enableMenuSideba: true, create: (context) => DeviceManagementBloc()..add(FetchDevices()),
appBarTitle: Row( child: WebScaffold(
children: [ appBarTitle: Text(
Text( 'Device Management',
'Device Management', style: Theme.of(context).textTheme.headlineLarge,
style: Theme.of(context).textTheme.headlineLarge, ),
) enableMenuSideba: true,
], scaffoldBody: BlocBuilder<DeviceManagementBloc, DeviceManagementState>(
builder: (context, state) {
if (state is DeviceManagementLoading) {
return const Center(child: CircularProgressIndicator());
} else if (state is DeviceManagementLoaded ||
state is DeviceManagementFiltered) {
final devices = state is DeviceManagementLoaded
? state.devices
: (state as DeviceManagementFiltered).filteredDevices;
return DeviceManagementBody(devices: devices);
} else {
return const Center(child: Text('No Devices Found'));
}
},
),
), ),
scaffoldBody: const DeviceManagementBody(),
); );
} }
} }

View File

@ -1,92 +1,94 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:syncrow_web/core/extension/build_context_x.dart'; import 'package:syncrow_web/core/extension/build_context_x.dart';
import 'package:syncrow_web/pages/common/buttons/search_reset_buttons.dart'; import 'package:syncrow_web/pages/common/buttons/default_button.dart';
import 'package:syncrow_web/pages/common/custom_table.dart'; import 'package:syncrow_web/pages/common/custom_table.dart';
import 'package:syncrow_web/pages/common/filter/filter_widget.dart'; import 'package:syncrow_web/pages/common/filter/filter_widget.dart';
import 'package:syncrow_web/pages/common/text_field/custom_text_field.dart'; import 'package:syncrow_web/pages/device_managment/bloc/device_managment_bloc.dart';
import 'package:syncrow_web/pages/device_managment/models/devices_model.dart';
import 'package:syncrow_web/pages/device_managment/widgets/device_search_filters.dart';
import 'package:syncrow_web/utils/format_date_time.dart';
import 'package:syncrow_web/utils/helpers/responsice_layout_helper/responsive_layout_helper.dart'; import 'package:syncrow_web/utils/helpers/responsice_layout_helper/responsive_layout_helper.dart';
import 'package:syncrow_web/utils/style.dart'; import 'package:syncrow_web/utils/style.dart';
class DeviceManagementBody extends StatelessWidget with HelperResponsiveLayout { class DeviceManagementBody extends StatelessWidget with HelperResponsiveLayout {
const DeviceManagementBody({super.key}); const DeviceManagementBody({super.key, required this.devices});
final List<AllDevicesModel> devices;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Container( return BlocBuilder<DeviceManagementBloc, DeviceManagementState>(
padding: const EdgeInsets.all(30), builder: (context, state) {
height: context.screenHeight, List<AllDevicesModel> devicesToShow = [];
width: context.screenWidth, int selectedIndex = 0;
child: Column( int onlineCount = 0;
crossAxisAlignment: CrossAxisAlignment.start, int offlineCount = 0;
children: [ int lowBatteryCount = 0;
FilterWidget(
size: context.screenSize, if (state is DeviceManagementLoaded) {
tabs: ['All', 'Online', 'Offline', 'Low Battery'], devicesToShow = state.devices;
selectedIndex: 0, selectedIndex = state.selectedIndex;
onTabChanged: (index) {}, onlineCount = state.onlineCount;
), offlineCount = state.offlineCount;
const SizedBox( lowBatteryCount = state.lowBatteryCount;
height: 20, } else if (state is DeviceManagementFiltered) {
), devicesToShow = state.filteredDevices;
if (isLargeScreenSize(context)) ...[ selectedIndex = state.selectedIndex;
Row( onlineCount = state.onlineCount;
children: [ offlineCount = state.offlineCount;
const StatefulTextField( lowBatteryCount = state.lowBatteryCount;
title: "Community", }
width: 200,
elevation: 2, // Create tab labels with counts
final tabs = [
'All (${devices.length})',
'Online ($onlineCount)',
'Offline ($offlineCount)',
'Low Battery ($lowBatteryCount)',
];
return Container(
padding: const EdgeInsets.all(30),
height: context.screenHeight,
width: context.screenWidth,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
FilterWidget(
size: MediaQuery.of(context).size,
tabs: tabs,
selectedIndex: selectedIndex,
onTabChanged: (index) {
context
.read<DeviceManagementBloc>()
.add(SelectedFilterChanged(index));
},
),
const SizedBox(
height: 20,
),
const DeviceSearchFilters(),
const SizedBox(
height: 12,
),
Container(
height: 43,
width: 100,
decoration: containerDecoration,
child: Center(
child: DefaultButton(
onPressed: () {},
borderRadius: 9,
child: const Text('Control'),
),
), ),
const SizedBox(width: 20), ),
const StatefulTextField( const SizedBox(
title: "Unit Name", height: 12,
width: 200, ),
elevation: 2, Expanded(
), child: DynamicTable(
const SizedBox(width: 20),
const StatefulTextField(
title: "Device Name / Product Name",
width: 300,
elevation: 2,
),
const SizedBox(width: 20),
SearchResetButtons(
onSearch: () {},
onReset: () {},
),
],
),
] else ...[
Wrap(
spacing: 20,
runSpacing: 10,
children: [
const StatefulTextField(
title: "Community",
width: 200,
elevation: 2,
),
const StatefulTextField(
title: "Unit Name",
width: 200,
elevation: 2,
),
const StatefulTextField(
title: "Device Name / Product Name",
width: 300,
elevation: 2,
),
SearchResetButtons(
onSearch: () {},
onReset: () {},
),
],
),
],
const SizedBox(
height: 12,
),
Expanded(
child: DynamicTable(
cellDecoration: containerDecoration, cellDecoration: containerDecoration,
selectAll: (p0) { selectAll: (p0) {
// visitorBloc.selectedDeviceIds.clear(); // visitorBloc.selectedDeviceIds.clear();
@ -101,29 +103,38 @@ class DeviceManagementBody extends StatelessWidget with HelperResponsiveLayout {
withCheckBox: true, withCheckBox: true,
size: context.screenSize, size: context.screenSize,
headers: const [ headers: const [
'Device Name', 'Device Name',
'Product Name', 'Product Name',
'Device ID', 'Device ID',
'Unit Name', 'Unit Name',
'Room', 'Room',
'Battery Level', 'Battery Level',
'Installation Date and Time', 'Installation Date and Time',
'Status', 'Status',
'Last Offline Date and Time', 'Last Offline Date and Time',
], ],
data: [] data: devicesToShow.map((device) {
// state.data.map((item) { return [
// return [ device.categoryName ?? '',
// item.name.toString(), device.name ?? '',
// item.uuid.toString(), device.uuid ?? '',
// item.productType.toString(), device.unit?.name ?? '',
// '', device.room?.name ?? '',
// item.online.value.toString(), device.batteryLevel?.toString() ?? '',
// ]; formatDateTime(DateTime.fromMillisecondsSinceEpoch(
// }).toList(), device.createTime ?? 0)),
)), device.online == true ? 'Online' : 'Offline',
], formatDateTime(DateTime.fromMillisecondsSinceEpoch(
), device.updateTime ?? 0)),
];
}).toList(),
isEmpty: devicesToShow.isEmpty,
),
),
],
),
);
},
); );
} }
} }

View File

@ -0,0 +1,84 @@
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:syncrow_web/pages/common/text_field/custom_text_field.dart';
import 'package:syncrow_web/pages/device_managment/bloc/device_managment_bloc.dart';
import 'package:syncrow_web/pages/common/buttons/search_reset_buttons.dart';
import 'package:syncrow_web/utils/helpers/responsice_layout_helper/responsive_layout_helper.dart';
class DeviceSearchFilters extends StatefulWidget {
const DeviceSearchFilters({super.key});
@override
State<DeviceSearchFilters> createState() => _DeviceSearchFiltersState();
}
class _DeviceSearchFiltersState extends State<DeviceSearchFilters>
with HelperResponsiveLayout {
final TextEditingController communityController = TextEditingController();
final TextEditingController unitNameController = TextEditingController();
final TextEditingController productNameController = TextEditingController();
@override
void dispose() {
communityController.dispose();
unitNameController.dispose();
productNameController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return isLargeScreenSize(context)
? Row(
children: [
_buildSearchField("Community", communityController, 200),
const SizedBox(width: 20),
_buildSearchField("Unit Name", unitNameController, 200),
const SizedBox(width: 20),
_buildSearchField(
"Device Name / Product Name", productNameController, 300),
const SizedBox(width: 20),
_buildSearchResetButtons(),
],
)
: Wrap(
spacing: 20,
runSpacing: 10,
children: [
_buildSearchField("Community", communityController, 200),
_buildSearchField("Unit Name", unitNameController, 200),
_buildSearchField(
"Device Name / Product Name", productNameController, 300),
_buildSearchResetButtons(),
],
);
}
Widget _buildSearchField(
String title, TextEditingController controller, double width) {
return StatefulTextField(
title: title,
width: width,
elevation: 2,
controller: controller,
);
}
Widget _buildSearchResetButtons() {
return SearchResetButtons(
onSearch: () {
context.read<DeviceManagementBloc>().add(SearchDevices(
community: communityController.text,
unitName: unitNameController.text,
productName: productNameController.text,
));
},
onReset: () {
communityController.clear();
unitNameController.clear();
productNameController.clear();
context.read<DeviceManagementBloc>().add(FetchDevices());
},
);
}
}

View File

@ -25,6 +25,7 @@ class HomeUpdateTree extends HomeState {
@override @override
List<Object> get props => [graph, builder]; List<Object> get props => [graph, builder];
} }
class HomeUserInfoLoaded extends HomeState { class HomeUserInfoLoaded extends HomeState {
final UserModel user; final UserModel user;

View File

@ -1,7 +1,3 @@
import 'package:flutter/cupertino.dart'; import 'package:flutter/cupertino.dart';
class HomeItemModel { class HomeItemModel {
@ -11,12 +7,11 @@ class HomeItemModel {
final bool? active; final bool? active;
final void Function(BuildContext context) onPress; final void Function(BuildContext context) onPress;
HomeItemModel({ HomeItemModel({
this.title, this.title,
this.icon, this.icon,
this.color, this.color,
this.active, this.active,
required this.onPress, required this.onPress,
}); });
} }

View File

@ -22,11 +22,12 @@ class HomeCard extends StatelessWidget {
return InkWell( return InkWell(
onTap: active ? onTap : null, onTap: active ? onTap : null,
child: Container( child: Container(
padding: const EdgeInsets.only(left: 10,right: 10,bottom: 10), padding: const EdgeInsets.only(left: 10, right: 10, bottom: 10),
decoration: BoxDecoration( decoration: BoxDecoration(
color: evenNumbers && active? color: evenNumbers && active
ColorsManager.blueColor.withOpacity(0.8) : ? ColorsManager.blueColor.withOpacity(0.8)
(active ?ColorsManager.blueColor : (active
? ColorsManager.blueColor
: ColorsManager.blueColor.withOpacity(0.2)), : ColorsManager.blueColor.withOpacity(0.2)),
borderRadius: BorderRadius.circular(30), borderRadius: BorderRadius.circular(30),
), ),

View File

@ -8,9 +8,7 @@ class HomePage extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return ResponsiveLayout( return ResponsiveLayout(
desktopBody: HomeWebPage(), desktopBody: HomeWebPage(), mobileBody: HomeMobilePage());
mobileBody:HomeMobilePage()
);
} }
} }

View File

@ -44,7 +44,8 @@ class HomeMobilePage extends StatelessWidget {
width: size.width * 0.68, width: size.width * 0.68,
child: GridView.builder( child: GridView.builder(
itemCount: 8, itemCount: 8,
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount( gridDelegate:
const SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2, crossAxisCount: 2,
crossAxisSpacing: 20.0, crossAxisSpacing: 20.0,
mainAxisSpacing: 20.0, mainAxisSpacing: 20.0,

View File

@ -39,7 +39,8 @@ class HomeWebPage extends StatelessWidget {
Text( Text(
'ACCESS YOUR APPS', 'ACCESS YOUR APPS',
style: Theme.of(context) style: Theme.of(context)
.textTheme.headlineLarge! .textTheme
.headlineLarge!
.copyWith(color: Colors.black, fontSize: 40), .copyWith(color: Colors.black, fontSize: 40),
), ),
const SizedBox(height: 30), const SizedBox(height: 30),
@ -63,7 +64,8 @@ class HomeWebPage extends StatelessWidget {
active: homeBloc.homeItems[index].active!, active: homeBloc.homeItems[index].active!,
name: homeBloc.homeItems[index].title!, name: homeBloc.homeItems[index].title!,
img: homeBloc.homeItems[index].icon!, img: homeBloc.homeItems[index].icon!,
onTap: () => homeBloc.homeItems[index].onPress(context), onTap: () =>
homeBloc.homeItems[index].onPress(context),
); );
}, },
), ),

View File

@ -32,8 +32,8 @@ class TreeWidget extends StatelessWidget {
SizedBox( SizedBox(
width: 100, width: 100,
child: TextFormField( child: TextFormField(
decoration: decoration: const InputDecoration(
const InputDecoration(labelText: "Subtree separation"), labelText: "Subtree separation"),
onChanged: (text) { onChanged: (text) {
firstNodeName = text; firstNodeName = text;
}, },
@ -73,9 +73,7 @@ class TreeWidget extends StatelessWidget {
child: GraphView( child: GraphView(
graph: state.graph, graph: state.graph,
algorithm: BuchheimWalkerAlgorithm( algorithm: BuchheimWalkerAlgorithm(
state.builder, state.builder, TreeEdgeRenderer(state.builder)),
TreeEdgeRenderer(state.builder)
),
paint: Paint() paint: Paint()
..color = Colors.green ..color = Colors.green
..strokeWidth = 1 ..strokeWidth = 1
@ -138,7 +136,7 @@ Widget rectangleWidget(String text, Node node, BuildContext blocContext) {
); );
}, },
child: Container( child: Container(
width: MediaQuery.of(blocContext).size.width*0.2, width: MediaQuery.of(blocContext).size.width * 0.2,
margin: EdgeInsets.symmetric(vertical: 10.0, horizontal: 20.0), margin: EdgeInsets.symmetric(vertical: 10.0, horizontal: 20.0),
padding: EdgeInsets.all(20.0), padding: EdgeInsets.all(20.0),
decoration: BoxDecoration( decoration: BoxDecoration(
@ -163,7 +161,7 @@ Widget rectangleWidget(String text, Node node, BuildContext blocContext) {
), ),
), ),
const SizedBox(width: 10.0), const SizedBox(width: 10.0),
SizedBox( SizedBox(
child: Text( child: Text(
text, text,
style: const TextStyle( style: const TextStyle(

View File

@ -220,7 +220,6 @@ class VisitorPasswordBloc
} }
} }
Future<void> postOnlineMultipleTimePassword( Future<void> postOnlineMultipleTimePassword(
OnlineMultipleTimePasswordEvent event, OnlineMultipleTimePasswordEvent event,
Emitter<VisitorPasswordState> emit) async { Emitter<VisitorPasswordState> emit) async {
@ -246,11 +245,10 @@ class VisitorPasswordBloc
passwordName: event.passwordName); passwordName: event.passwordName);
if (res == true) { if (res == true) {
emit(SuccessState()); emit(SuccessState());
}else { } else {
throw Exception('Failed to create password'); throw Exception('Failed to create password');
} }
emit(TableLoaded(data)); emit(TableLoaded(data));
} catch (e) { } catch (e) {
emit(FailedState(e.toString())); emit(FailedState(e.toString()));
Navigator.pop(event.context!); Navigator.pop(event.context!);
@ -267,17 +265,16 @@ class VisitorPasswordBloc
try { try {
emit(LoadingInitialState()); emit(LoadingInitialState());
await generate7DigitNumber(); await generate7DigitNumber();
bool res = await AccessMangApi().postOffLineOneTime( bool res = await AccessMangApi().postOffLineOneTime(
email: event.email, email: event.email,
devicesUuid: selectedDevices, devicesUuid: selectedDevices,
passwordName: event.passwordName); passwordName: event.passwordName);
if (res == true) { if (res == true) {
emit(SuccessState()); emit(SuccessState());
}else { } else {
throw Exception('Failed to create password'); throw Exception('Failed to create password');
} }
emit(TableLoaded(data)); emit(TableLoaded(data));
} catch (e) { } catch (e) {
emit(FailedState(e.toString())); emit(FailedState(e.toString()));
Navigator.pop(event.context!); Navigator.pop(event.context!);
@ -294,7 +291,7 @@ class VisitorPasswordBloc
try { try {
emit(LoadingInitialState()); emit(LoadingInitialState());
await generate7DigitNumber(); await generate7DigitNumber();
bool res = await AccessMangApi().postOffLineMultipleTime( bool res = await AccessMangApi().postOffLineMultipleTime(
email: event.email, email: event.email,
devicesUuid: selectedDevices, devicesUuid: selectedDevices,
passwordName: event.passwordName, passwordName: event.passwordName,
@ -303,18 +300,18 @@ class VisitorPasswordBloc
); );
if (res == true) { if (res == true) {
emit(SuccessState()); emit(SuccessState());
}else { } else {
throw Exception('Failed to create password'); throw Exception('Failed to create password');
} }
emit(TableLoaded(data)); emit(TableLoaded(data));
} catch (e) { } catch (e) {
emit(FailedState(e.toString())); emit(FailedState(e.toString()));
Navigator.pop(event.context!); Navigator.pop(event.context!);
stateDialog( stateDialog(
context: event.context!, context: event.context!,
message: e.toString(), message: e.toString(),
title: 'Something Wrong'); } title: 'Something Wrong');
}
} }
void selectDevice( void selectDevice(

View File

@ -26,33 +26,32 @@ class SelectUsageFrequency extends VisitorPasswordEvent {
@override @override
List<Object> get props => [usageType]; List<Object> get props => [usageType];
} }
class SelectTimeVisitorPassword extends VisitorPasswordEvent { class SelectTimeVisitorPassword extends VisitorPasswordEvent {
final BuildContext context; final BuildContext context;
final bool isStart; final bool isStart;
final bool isRepeat; final bool isRepeat;
const SelectTimeVisitorPassword({ required this.context,required this.isStart,required this.isRepeat}); const SelectTimeVisitorPassword(
{required this.context, required this.isStart, required this.isRepeat});
@override @override
List<Object> get props => [context,isStart,isRepeat]; List<Object> get props => [context, isStart, isRepeat];
} }
class ToggleDaySelectionEvent extends VisitorPasswordEvent { class ToggleDaySelectionEvent extends VisitorPasswordEvent {
final String key; final String key;
const ToggleDaySelectionEvent({required this.key}); const ToggleDaySelectionEvent({required this.key});
@override @override
List<Object> get props => [key]; List<Object> get props => [key];
} }
class ToggleRepeatEvent extends VisitorPasswordEvent {} class ToggleRepeatEvent extends VisitorPasswordEvent {}
class GeneratePasswordEvent extends VisitorPasswordEvent {} class GeneratePasswordEvent extends VisitorPasswordEvent {}
class FetchDevice extends VisitorPasswordEvent { class FetchDevice extends VisitorPasswordEvent {}
}
//online password //online password
class OnlineOneTimePasswordEvent extends VisitorPasswordEvent { class OnlineOneTimePasswordEvent extends VisitorPasswordEvent {
@ -60,20 +59,31 @@ class OnlineOneTimePasswordEvent extends VisitorPasswordEvent {
final String? passwordName; final String? passwordName;
final BuildContext? context; final BuildContext? context;
const OnlineOneTimePasswordEvent({this.email,this.passwordName,this.context}); const OnlineOneTimePasswordEvent(
{this.email, this.passwordName, this.context});
@override @override
List<Object> get props => [email!,passwordName!,]; List<Object> get props => [
email!,
passwordName!,
];
} }
class OnlineMultipleTimePasswordEvent extends VisitorPasswordEvent { class OnlineMultipleTimePasswordEvent extends VisitorPasswordEvent {
final String? email; final String? email;
final String? passwordName; final String? passwordName;
final String? invalidTime; final String? invalidTime;
final String? effectiveTime; final String? effectiveTime;
final BuildContext? context; final BuildContext? context;
const OnlineMultipleTimePasswordEvent({this.email,this.passwordName,this.invalidTime,this.effectiveTime,this.context}); const OnlineMultipleTimePasswordEvent(
{this.email,
this.passwordName,
this.invalidTime,
this.effectiveTime,
this.context});
@override @override
List<Object> get props => [email!,passwordName!,invalidTime!,effectiveTime!,context!]; List<Object> get props =>
[email!, passwordName!, invalidTime!, effectiveTime!, context!];
} }
//offline password //offline password
@ -81,9 +91,14 @@ class OfflineOneTimePasswordEvent extends VisitorPasswordEvent {
final BuildContext? context; final BuildContext? context;
final String? email; final String? email;
final String? passwordName; final String? passwordName;
const OfflineOneTimePasswordEvent({this.email,this.passwordName,this.context}); const OfflineOneTimePasswordEvent(
{this.email, this.passwordName, this.context});
@override @override
List<Object> get props => [email!,passwordName!,context!,]; List<Object> get props => [
email!,
passwordName!,
context!,
];
} }
class OfflineMultipleTimePasswordEvent extends VisitorPasswordEvent { class OfflineMultipleTimePasswordEvent extends VisitorPasswordEvent {
@ -93,13 +108,18 @@ class OfflineMultipleTimePasswordEvent extends VisitorPasswordEvent {
final String? effectiveTime; final String? effectiveTime;
final BuildContext? context; final BuildContext? context;
const OfflineMultipleTimePasswordEvent({this.context,this.email,this.passwordName,this.invalidTime,this.effectiveTime}); const OfflineMultipleTimePasswordEvent(
{this.context,
this.email,
this.passwordName,
this.invalidTime,
this.effectiveTime});
@override @override
List<Object> get props => [email!,passwordName!,invalidTime!,effectiveTime!,context!]; List<Object> get props =>
[email!, passwordName!, invalidTime!, effectiveTime!, context!];
} }
class SelectDeviceEvent extends VisitorPasswordEvent { class SelectDeviceEvent extends VisitorPasswordEvent {
final String deviceId; final String deviceId;
const SelectDeviceEvent(this.deviceId); const SelectDeviceEvent(this.deviceId);
@ -116,22 +136,26 @@ class FilterDataEvent extends VisitorPasswordEvent {
this.endTime, this.endTime,
}); });
} }
class UpdateFilteredDevicesEvent extends VisitorPasswordEvent { class UpdateFilteredDevicesEvent extends VisitorPasswordEvent {
final List<DeviceModel> filteredData; final List<DeviceModel> filteredData;
UpdateFilteredDevicesEvent(this.filteredData); UpdateFilteredDevicesEvent(this.filteredData);
}class SelectTimeEvent extends VisitorPasswordEvent {
final BuildContext context;
final bool isEffective;
const SelectTimeEvent({required this.context,required this.isEffective});
@override
List<Object> get props => [context,isEffective];
} }
class SelectTimeEvent extends VisitorPasswordEvent {
final BuildContext context;
final bool isEffective;
const SelectTimeEvent({required this.context, required this.isEffective});
@override
List<Object> get props => [context, isEffective];
}
class ChangeTimeEvent extends VisitorPasswordEvent { class ChangeTimeEvent extends VisitorPasswordEvent {
final dynamic val; final dynamic val;
final bool isStartEndTime; final bool isStartEndTime;
const ChangeTimeEvent({required this.val,required this.isStartEndTime}); const ChangeTimeEvent({required this.val, required this.isStartEndTime});
@override @override
List<Object> get props => [val,isStartEndTime]; List<Object> get props => [val, isStartEndTime];
} }

View File

@ -1,5 +1,3 @@
import 'package:equatable/equatable.dart'; import 'package:equatable/equatable.dart';
import 'package:syncrow_web/pages/visitor_password/model/device_model.dart'; import 'package:syncrow_web/pages/visitor_password/model/device_model.dart';
@ -12,8 +10,6 @@ abstract class VisitorPasswordState extends Equatable {
class VisitorPasswordInitial extends VisitorPasswordState {} class VisitorPasswordInitial extends VisitorPasswordState {}
class PasswordTypeSelected extends VisitorPasswordState { class PasswordTypeSelected extends VisitorPasswordState {
final String selectedType; final String selectedType;
const PasswordTypeSelected(this.selectedType); const PasswordTypeSelected(this.selectedType);
@ -36,13 +32,16 @@ class IsRepeatState extends VisitorPasswordState {
@override @override
List<Object> get props => [repeat]; List<Object> get props => [repeat];
} }
class LoadingInitialState extends VisitorPasswordState {} class LoadingInitialState extends VisitorPasswordState {}
class ChangeTimeState extends VisitorPasswordState {} class ChangeTimeState extends VisitorPasswordState {}
class TimeSelectedState extends VisitorPasswordState {} class TimeSelectedState extends VisitorPasswordState {}
class DeviceLoaded extends VisitorPasswordState {} class DeviceLoaded extends VisitorPasswordState {}
class SuccessState extends VisitorPasswordState {} class SuccessState extends VisitorPasswordState {}
class FailedState extends VisitorPasswordState { class FailedState extends VisitorPasswordState {
@ -62,4 +61,4 @@ class TableLoaded extends VisitorPasswordState {
class DeviceSelectionUpdated extends VisitorPasswordState { class DeviceSelectionUpdated extends VisitorPasswordState {
final List<String> selectedDeviceIds; final List<String> selectedDeviceIds;
const DeviceSelectionUpdated(this.selectedDeviceIds); const DeviceSelectionUpdated(this.selectedDeviceIds);
} }

View File

@ -1,28 +1,26 @@
import 'package:syncrow_web/utils/constants/const.dart'; import 'package:syncrow_web/utils/constants/const.dart';
class DeviceModel { class DeviceModel {
dynamic productUuid; dynamic productUuid;
dynamic productType; dynamic productType;
dynamic activeTime; dynamic activeTime;
dynamic category; dynamic category;
dynamic categoryName; dynamic categoryName;
dynamic createTime; dynamic createTime;
dynamic gatewayId; dynamic gatewayId;
dynamic icon; dynamic icon;
dynamic ip; dynamic ip;
dynamic lat; dynamic lat;
dynamic localKey; dynamic localKey;
dynamic lon; dynamic lon;
dynamic model; dynamic model;
dynamic name; dynamic name;
DeviseStatus online; DeviseStatus online;
dynamic ownerId; dynamic ownerId;
dynamic sub; dynamic sub;
dynamic timeZone; dynamic timeZone;
dynamic updateTime; dynamic updateTime;
dynamic uuid; dynamic uuid;
DeviceModel({ DeviceModel({
required this.productUuid, required this.productUuid,
@ -50,25 +48,25 @@ class DeviceModel {
// Deserialize from JSON // Deserialize from JSON
factory DeviceModel.fromJson(Map<String, dynamic> json) { factory DeviceModel.fromJson(Map<String, dynamic> json) {
return DeviceModel( return DeviceModel(
productUuid: json['productUuid'] , productUuid: json['productUuid'],
productType: json['productType'], productType: json['productType'],
activeTime: json['activeTime'], activeTime: json['activeTime'],
category: json['category'] , category: json['category'],
categoryName: json['categoryName'] , categoryName: json['categoryName'],
createTime: json['createTime'] , createTime: json['createTime'],
gatewayId: json['gatewayId'], gatewayId: json['gatewayId'],
icon: json['icon'], icon: json['icon'],
ip: json['ip'] , ip: json['ip'],
lat: json['lat'] , lat: json['lat'],
localKey: json['localKey'] , localKey: json['localKey'],
lon: json['lon'] , lon: json['lon'],
model: json['model'] , model: json['model'],
name: json['name'], name: json['name'],
online: OnlineTypeExtension.fromString(json['online']), online: OnlineTypeExtension.fromString(json['online']),
ownerId: json['ownerId'] , ownerId: json['ownerId'],
sub: json['sub'], sub: json['sub'],
timeZone: json['timeZone'], timeZone: json['timeZone'],
updateTime: json['updateTime'] , updateTime: json['updateTime'],
uuid: json['uuid'], uuid: json['uuid'],
); );
} }

View File

@ -24,4 +24,4 @@ class Schedule {
'workingDay': workingDay, 'workingDay': workingDay,
}; };
} }
} }

View File

@ -17,70 +17,72 @@ class RepeatWidget extends StatelessWidget {
Size size = MediaQuery.of(context).size; Size size = MediaQuery.of(context).size;
return BlocBuilder<VisitorPasswordBloc, VisitorPasswordState>( return BlocBuilder<VisitorPasswordBloc, VisitorPasswordState>(
builder: (context, state) { builder: (context, state) {
final visitorBloc = BlocProvider.of<VisitorPasswordBloc>(context); final visitorBloc = BlocProvider.of<VisitorPasswordBloc>(context);
return Column( return Column(
mainAxisAlignment: MainAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[ children: <Widget>[
// Wrap the Row in a SingleChildScrollView to handle overflow // Wrap the Row in a SingleChildScrollView to handle overflow
SingleChildScrollView( SingleChildScrollView(
scrollDirection: Axis.horizontal, scrollDirection: Axis.horizontal,
child: Row( child: Row(
children: visitorBloc.days.map((day) { children: visitorBloc.days.map((day) {
return Container( return Container(
width: 70, // Adjust width as needed width: 70, // Adjust width as needed
margin: EdgeInsets.all(5), margin: EdgeInsets.all(5),
child: CheckboxListTile( child: CheckboxListTile(
contentPadding: EdgeInsets.zero, contentPadding: EdgeInsets.zero,
title: Text( title: Text(
day['day']!, day['day']!,
style: TextStyle( style: TextStyle(
fontSize: 10, fontSize: 10,
color: visitorBloc.selectedDays.contains(day['key']) color: visitorBloc.selectedDays.contains(day['key'])
? Colors.black ? Colors.black
: ColorsManager.blackColor, : ColorsManager.blackColor,
),
),
value: visitorBloc.selectedDays.contains(day['key']),
onChanged: (bool? value) {
if (value != null) {
visitorBloc.add(ToggleDaySelectionEvent(key: day['key']!));
}
},
), ),
); ),
}).toList(), value: visitorBloc.selectedDays.contains(day['key']),
), onChanged: (bool? value) {
), if (value != null) {
Padding( visitorBloc
padding: const EdgeInsets.all(8.0), .add(ToggleDaySelectionEvent(key: day['key']!));
child: DateTimeWebWidget( }
icon: Assets.timeIcon, },
isRequired: false, ),
title: '', );
size: size, }).toList(),
endTime: () { ),
visitorBloc.add(SelectTimeEvent( ),
context: context, Padding(
isEffective: false)); padding: const EdgeInsets.all(8.0),
Future.delayed(const Duration(milliseconds: 500), () { child: DateTimeWebWidget(
visitorBloc.add(ChangeTimeEvent(val: visitorBloc.endTime, isStartEndTime: true)); icon: Assets.timeIcon,
}); isRequired: false,
}, title: '',
startTime: () { size: size,
Future.delayed(const Duration(milliseconds: 500), () { endTime: () {
visitorBloc.add(ChangeTimeEvent(val: visitorBloc.endTime, isStartEndTime: true)); visitorBloc
}); .add(SelectTimeEvent(context: context, isEffective: false));
visitorBloc.add(SelectTimeEvent(context: context, isEffective: true)); Future.delayed(const Duration(milliseconds: 500), () {
}, visitorBloc.add(ChangeTimeEvent(
firstString: visitorBloc.effectiveTime, val: visitorBloc.endTime, isStartEndTime: true));
secondString: visitorBloc.expirationTime, });
), },
), startTime: () {
const SizedBox(height: 20), Future.delayed(const Duration(milliseconds: 500), () {
], visitorBloc.add(ChangeTimeEvent(
); val: visitorBloc.endTime, isStartEndTime: true));
} });
); visitorBloc
.add(SelectTimeEvent(context: context, isEffective: true));
},
firstString: visitorBloc.effectiveTime,
secondString: visitorBloc.expirationTime,
),
),
const SizedBox(height: 20),
],
);
});
} }
} }

View File

@ -7,8 +7,7 @@ import 'package:syncrow_web/pages/visitor_password/model/schedule_model.dart';
import 'package:syncrow_web/services/api/http_service.dart'; import 'package:syncrow_web/services/api/http_service.dart';
import 'package:syncrow_web/utils/constants/api_const.dart'; import 'package:syncrow_web/utils/constants/api_const.dart';
class AccessMangApi{ class AccessMangApi {
Future<List<PasswordModel>> fetchVisitorPassword() async { Future<List<PasswordModel>> fetchVisitorPassword() async {
try { try {
final response = await HTTPService().get( final response = await HTTPService().get(
@ -23,7 +22,7 @@ class AccessMangApi{
}, },
); );
return response; return response;
} catch (e) { } catch (e) {
debugPrint('Error fetching visitor passwords: $e'); debugPrint('Error fetching visitor passwords: $e');
return []; return [];
} }
@ -43,19 +42,19 @@ class AccessMangApi{
}, },
); );
return response; return response;
} catch (e) { } catch (e) {
debugPrint('Error fetching $e'); debugPrint('Error fetching $e');
return []; return [];
} }
} }
Future<bool> postOnlineOneTime({ Future<bool> postOnlineOneTime(
String? email, {String? email,
String? passwordName, String? passwordName,
String? password, String? password,
String? effectiveTime, String? effectiveTime,
String? invalidTime, String? invalidTime,
List<String>? devicesUuid}) async { List<String>? devicesUuid}) async {
try { try {
final response = await HTTPService().post( final response = await HTTPService().post(
path: ApiEndpoints.sendOnlineOneTime, path: ApiEndpoints.sendOnlineOneTime,
@ -64,34 +63,34 @@ class AccessMangApi{
"passwordName": passwordName, "passwordName": passwordName,
"password": password, "password": password,
"devicesUuid": devicesUuid, "devicesUuid": devicesUuid,
"effectiveTime":effectiveTime , "effectiveTime": effectiveTime,
"invalidTime": invalidTime "invalidTime": invalidTime
}), }),
showServerMessage: true, showServerMessage: true,
expectedResponseModel: (json) { expectedResponseModel: (json) {
if(json['statusCode'].toString()=='201'){ if (json['statusCode'].toString() == '201') {
return true; return true;
}else{ } else {
return false; return false;
} }
}, },
); );
return response; return response;
} on DioException catch (e) { } on DioException catch (e) {
debugPrint('Error: ${e.message}'); debugPrint('Error: ${e.message}');
debugPrint('Error fetching ${e.response!.statusMessage}'); debugPrint('Error fetching ${e.response!.statusMessage}');
return false; return false;
} }
} }
Future postOnlineMultipleTime({ Future postOnlineMultipleTime(
String? effectiveTime, {String? effectiveTime,
String? invalidTime, String? invalidTime,
String? email, String? email,
String? password, String? password,
String? passwordName, String? passwordName,
List<Schedule>? scheduleList, List<Schedule>? scheduleList,
List<String>? devicesUuid}) async { List<String>? devicesUuid}) async {
try { try {
Map<String, dynamic> body = { Map<String, dynamic> body = {
"email": email, "email": email,
@ -102,22 +101,24 @@ class AccessMangApi{
"invalidTime": invalidTime, "invalidTime": invalidTime,
}; };
if (scheduleList != null) { if (scheduleList != null) {
body["scheduleList"] = scheduleList.map((schedule) => schedule.toJson()).toList(); body["scheduleList"] =
scheduleList.map((schedule) => schedule.toJson()).toList();
} }
final response = await HTTPService().post( final response = await HTTPService().post(
path: ApiEndpoints.sendOnlineMultipleTime, path: ApiEndpoints.sendOnlineMultipleTime,
body: jsonEncode(body), body: jsonEncode(body),
showServerMessage: true, showServerMessage: true,
expectedResponseModel: (json) { expectedResponseModel: (json) {
if(json['data']['successOperations'][0]['success'].toString()=='true'){ if (json['data']['successOperations'][0]['success'].toString() ==
'true') {
return true; return true;
}else{ } else {
return false; return false;
} }
}, },
); );
return response; return response;
} on DioException catch (e){ } on DioException catch (e) {
debugPrint('Error fetching ${e.type.name}'); debugPrint('Error fetching ${e.type.name}');
debugPrint('Error fetching ${e.response!.statusMessage}'); debugPrint('Error fetching ${e.response!.statusMessage}');
return false; return false;
@ -126,16 +127,17 @@ class AccessMangApi{
// OffLine One Time Password // OffLine One Time Password
Future postOffLineOneTime({String? email,String? passwordName,List<String>? devicesUuid}) async { Future postOffLineOneTime(
{String? email, String? passwordName, List<String>? devicesUuid}) async {
try { try {
final response = await HTTPService().post( final response = await HTTPService().post(
path: ApiEndpoints.sendOffLineOneTime, path: ApiEndpoints.sendOffLineOneTime,
body: jsonEncode({ body: jsonEncode({
"email": email, "email": email,
"passwordName": passwordName, "passwordName": passwordName,
"devicesUuid": devicesUuid "devicesUuid": devicesUuid
}), }),
showServerMessage: true, showServerMessage: true,
expectedResponseModel: (json) { expectedResponseModel: (json) {
if (json['data']['successOperations'][0]['success'].toString() == if (json['data']['successOperations'][0]['success'].toString() ==
'true') { 'true') {
@ -143,8 +145,7 @@ class AccessMangApi{
} else { } else {
return false; return false;
} }
} });
);
return response; return response;
} catch (e) { } catch (e) {
debugPrint('Error fetching $e'); debugPrint('Error fetching $e');
@ -152,25 +153,23 @@ class AccessMangApi{
} }
} }
Future postOffLineMultipleTime({ Future postOffLineMultipleTime(
String? email, {String? email,
String? passwordName, String? passwordName,
String? effectiveTime, String? effectiveTime,
String? invalidTime, String? invalidTime,
List<String>? devicesUuid List<String>? devicesUuid}) async {
}) async {
try { try {
final response = await HTTPService().post( final response = await HTTPService().post(
path: ApiEndpoints.sendOffLineOneTime, path: ApiEndpoints.sendOffLineOneTime,
body: jsonEncode({ body: jsonEncode({
"email": email, "email": email,
"devicesUuid":devicesUuid, "devicesUuid": devicesUuid,
"passwordName": passwordName, "passwordName": passwordName,
"effectiveTime": effectiveTime, "effectiveTime": effectiveTime,
"invalidTime": invalidTime "invalidTime": invalidTime
}), }),
showServerMessage: true, showServerMessage: true,
expectedResponseModel: (json) { expectedResponseModel: (json) {
if (json['data']['successOperations'][0]['success'].toString() == if (json['data']['successOperations'][0]['success'].toString() ==
'true') { 'true') {
@ -178,12 +177,11 @@ class AccessMangApi{
} else { } else {
return false; return false;
} }
} });
);
return response; return response;
} catch (e) { } catch (e) {
debugPrint('Error fetching $e'); debugPrint('Error fetching $e');
return []; return [];
} }
} }
} }

View File

@ -22,16 +22,19 @@ class HTTPInterceptor extends InterceptorsWrapper {
if (await validateResponse(response)) { if (await validateResponse(response)) {
super.onResponse(response, handler); super.onResponse(response, handler);
} else { } else {
handler.reject(DioException(requestOptions: response.requestOptions, response: response)); handler.reject(DioException(
requestOptions: response.requestOptions, response: response));
} }
} }
@override @override
void onRequest(RequestOptions options, RequestInterceptorHandler handler) async { void onRequest(
RequestOptions options, RequestInterceptorHandler handler) async {
var storage = const FlutterSecureStorage(); var storage = const FlutterSecureStorage();
var token = await storage.read(key: Token.loginAccessTokenKey); var token = await storage.read(key: Token.loginAccessTokenKey);
if (checkHeaderExclusionListOfAddedParameters(options.path)) { if (checkHeaderExclusionListOfAddedParameters(options.path)) {
options.headers.putIfAbsent(HttpHeaders.authorizationHeader, () => "Bearer $token"); options.headers
.putIfAbsent(HttpHeaders.authorizationHeader, () => "Bearer $token");
} }
// options.headers['Authorization'] = 'Bearer ${'${token!}123'}'; // options.headers['Authorization'] = 'Bearer ${'${token!}123'}';
super.onRequest(options, handler); super.onRequest(options, handler);

View File

@ -31,8 +31,7 @@ class ServerFailure extends Failure {
{ {
// var document = parser.parse(dioError.response!.data.toString()); // var document = parser.parse(dioError.response!.data.toString());
// var message = document.body!.text; // var message = document.body!.text;
return ServerFailure.fromResponse( return ServerFailure.fromResponse(dioError.response!.statusCode!,
dioError.response!.statusCode!,
dioError.response?.data['message'] ?? "Error"); dioError.response?.data['message'] ?? "Error");
} }
case DioExceptionType.cancel: case DioExceptionType.cancel:

View File

@ -1,4 +1,3 @@
import 'package:dio/dio.dart'; import 'package:dio/dio.dart';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:syncrow_web/pages/auth/model/region_model.dart'; import 'package:syncrow_web/pages/auth/model/region_model.dart';
@ -18,48 +17,43 @@ class AuthenticationAPI {
return response; return response;
} }
static Future forgetPassword( static Future forgetPassword({
{required var email, required var password,}) async { required var email,
required var password,
}) async {
final response = await HTTPService().post( final response = await HTTPService().post(
path: ApiEndpoints.forgetPassword, path: ApiEndpoints.forgetPassword,
body: { body: {"email": email, "password": password},
"email": email,
"password": password
},
showServerMessage: true, showServerMessage: true,
expectedResponseModel: (json) {}); expectedResponseModel: (json) {});
return response; return response;
} }
static Future<int?> sendOtp(
static Future<int?> sendOtp({required String email, required String regionUuid}) async { {required String email, required String regionUuid}) async {
try { try {
final response = await HTTPService().post( final response = await HTTPService().post(
path: ApiEndpoints.sendOtp, path: ApiEndpoints.sendOtp,
body: { body: {"email": email, "type": "PASSWORD", "regionUuid": regionUuid},
"email": email,
"type": "PASSWORD",
"regionUuid": regionUuid
},
showServerMessage: true, showServerMessage: true,
expectedResponseModel: (json) { expectedResponseModel: (json) {
return 30; return 30;
} });
);
return 30; return 30;
} on DioException catch (e) { } on DioException catch (e) {
if (e.response != null) { if (e.response != null) {
if (e.response!.statusCode == 400) { if (e.response!.statusCode == 400) {
final errorData = e.response!.data; final errorData = e.response!.data;
String errorMessage = errorData['message']; String errorMessage = errorData['message'];
if(errorMessage=='User not found'){ if (errorMessage == 'User not found') {
return 1; return 1;
}else{ } else {
int cooldown = errorData['data']['cooldown'] ?? 1; int cooldown = errorData['data']['cooldown'] ?? 1;
return cooldown; return cooldown;
} }
} else { } else {
debugPrint('Error: ${e.response!.statusCode} - ${e.response!.statusMessage}'); debugPrint(
'Error: ${e.response!.statusCode} - ${e.response!.statusMessage}');
return 1; return 1;
} }
} else { } else {
@ -74,7 +68,7 @@ class AuthenticationAPI {
static Future verifyOtp( static Future verifyOtp(
{required String email, required String otpCode}) async { {required String email, required String otpCode}) async {
try{ try {
final response = await HTTPService().post( final response = await HTTPService().post(
path: ApiEndpoints.verifyOtp, path: ApiEndpoints.verifyOtp,
body: {"email": email, "type": "PASSWORD", "otpCode": otpCode}, body: {"email": email, "type": "PASSWORD", "otpCode": otpCode},
@ -87,12 +81,12 @@ class AuthenticationAPI {
} }
}); });
return response; return response;
}on DioException catch (e){ } on DioException catch (e) {
if (e.response != null) { if (e.response != null) {
if (e.response!.statusCode == 400) { if (e.response!.statusCode == 400) {
final errorData = e.response!.data; final errorData = e.response!.data;
String errorMessage = errorData['message']; String errorMessage = errorData['message'];
return errorMessage; return errorMessage;
} }
} else { } else {
debugPrint('Error: ${e.message}'); debugPrint('Error: ${e.message}');
@ -105,10 +99,10 @@ class AuthenticationAPI {
path: ApiEndpoints.getRegion, path: ApiEndpoints.getRegion,
showServerMessage: true, showServerMessage: true,
expectedResponseModel: (json) { expectedResponseModel: (json) {
return (json as List).map((zone) => RegionModel.fromJson(zone)).toList(); return (json as List)
} .map((zone) => RegionModel.fromJson(zone))
); .toList();
});
return response as List<RegionModel>; return response as List<RegionModel>;
} }
} }

View File

@ -0,0 +1,26 @@
import 'package:flutter/material.dart';
import 'package:syncrow_web/pages/device_managment/models/devices_model.dart';
import 'package:syncrow_web/services/api/http_service.dart';
import 'package:syncrow_web/utils/constants/api_const.dart';
class DevicesManagementApi {
Future<List<AllDevicesModel>> fetchDevices() async {
try {
final response = await HTTPService().get(
path: ApiEndpoints.getAllDevices,
showServerMessage: true,
expectedResponseModel: (json) {
List<dynamic> jsonData = json;
List<AllDevicesModel> devicesList = jsonData.map((jsonItem) {
return AllDevicesModel.fromJson(jsonItem);
}).toList();
return devicesList;
},
);
return response;
} catch (e) {
debugPrint('Error fetching $e');
return [];
}
}
}

View File

@ -1,16 +1,15 @@
import 'package:syncrow_web/pages/auth/model/user_model.dart'; import 'package:syncrow_web/pages/auth/model/user_model.dart';
import 'package:syncrow_web/services/api/http_service.dart'; import 'package:syncrow_web/services/api/http_service.dart';
import 'package:syncrow_web/utils/constants/api_const.dart'; import 'package:syncrow_web/utils/constants/api_const.dart';
class HomeApi{ class HomeApi {
Future fetchUserInfo(userId) async { Future fetchUserInfo(userId) async {
final response = await HTTPService().get( final response = await HTTPService().get(
path: ApiEndpoints.getUser.replaceAll('{userUuid}', userId!), path: ApiEndpoints.getUser.replaceAll('{userUuid}', userId!),
showServerMessage: true, showServerMessage: true,
expectedResponseModel: (json) { expectedResponseModel: (json) {
return UserModel.fromJson(json); return UserModel.fromJson(json);
} });
); return response;
return response; }
} }
}

View File

@ -4,7 +4,7 @@ import 'package:syncrow_web/services/api/http_interceptor.dart';
import 'package:syncrow_web/services/api/http_service.dart'; import 'package:syncrow_web/services/api/http_service.dart';
final GetIt serviceLocator = GetIt.instance; final GetIt serviceLocator = GetIt.instance;
//setupLocator() // to search for dependency injection in flutter //setupLocator() // to search for dependency injection in flutter
initialSetup() { initialSetup() {
serviceLocator.registerSingleton<HTTPInterceptor>(HTTPInterceptor()); serviceLocator.registerSingleton<HTTPInterceptor>(HTTPInterceptor());
//Base classes //Base classes

View File

@ -25,6 +25,7 @@ abstract class ColorsManager {
static const Color slidingBlueColor = Color(0x99023DFE); static const Color slidingBlueColor = Color(0x99023DFE);
static const Color blackColor = Color(0xFF000000); static const Color blackColor = Color(0xFF000000);
static const Color lightGreen = Color(0xFF00FF0A); static const Color lightGreen = Color(0xFF00FF0A);
static const Color green = Color(0xFF008905);
static const Color grayColor = Color(0xFF999999); static const Color grayColor = Color(0xFF999999);
static const Color red = Color(0xFFFF0000); static const Color red = Color(0xFFFF0000);
static const Color graysColor = Color(0xffEBEBEB); static const Color graysColor = Color(0xffEBEBEB);

View File

@ -5,21 +5,28 @@ abstract class ApiEndpoints {
////////////////////////////////////// Authentication /////////////////////////////// ////////////////////////////////////// Authentication ///////////////////////////////
static const String signUp = '$baseUrl/authentication/user/signup'; static const String signUp = '$baseUrl/authentication/user/signup';
static const String login = '$baseUrl/authentication/user/login'; static const String login = '$baseUrl/authentication/user/login';
static const String forgetPassword = '$baseUrl/authentication/user/forget-password'; static const String forgetPassword =
'$baseUrl/authentication/user/forget-password';
static const String sendOtp = '$baseUrl/authentication/user/send-otp'; static const String sendOtp = '$baseUrl/authentication/user/send-otp';
static const String verifyOtp = '$baseUrl/authentication/user/verify-otp'; static const String verifyOtp = '$baseUrl/authentication/user/verify-otp';
static const String getRegion = '$baseUrl/region'; static const String getRegion = '$baseUrl/region';
static const String visitorPassword = '$baseUrl/visitor-password'; static const String visitorPassword = '$baseUrl/visitor-password';
static const String getDevices = '$baseUrl/visitor-password/devices'; static const String getDevices = '$baseUrl/visitor-password/devices';
static const String sendOnlineOneTime =
static const String sendOnlineOneTime = '$baseUrl/visitor-password/temporary-password/online/one-time'; '$baseUrl/visitor-password/temporary-password/online/one-time';
static const String sendOnlineMultipleTime = '$baseUrl/visitor-password/temporary-password/online/multiple-time'; static const String sendOnlineMultipleTime =
'$baseUrl/visitor-password/temporary-password/online/multiple-time';
//offline Password //offline Password
static const String sendOffLineOneTime = '$baseUrl/visitor-password/temporary-password/offline/one-time'; static const String sendOffLineOneTime =
static const String sendOffLineMultipleTime = '$baseUrl/visitor-password/temporary-password/offline/multiple-time'; '$baseUrl/visitor-password/temporary-password/offline/one-time';
static const String sendOffLineMultipleTime =
'$baseUrl/visitor-password/temporary-password/offline/multiple-time';
static const String getUser = '$baseUrl/user/{userUuid}'; static const String getUser = '$baseUrl/user/{userUuid}';
////// Devices Management ////////////////
static const String getAllDevices = '$baseUrl/device';
} }

View File

@ -13,10 +13,12 @@ class Assets {
static const String rightLine = "assets/images/right_line.png"; static const String rightLine = "assets/images/right_line.png";
static const String google = "assets/images/google.svg"; static const String google = "assets/images/google.svg";
static const String facebook = "assets/images/facebook.svg"; static const String facebook = "assets/images/facebook.svg";
static const String invisiblePassword = "assets/images/Password_invisible.svg"; static const String invisiblePassword =
"assets/images/Password_invisible.svg";
static const String visiblePassword = "assets/images/Password_visible.svg"; static const String visiblePassword = "assets/images/Password_visible.svg";
static const String accessIcon = "assets/images/access_icon.svg"; static const String accessIcon = "assets/images/access_icon.svg";
static const String spaseManagementIcon = "assets/images/spase_management_icon.svg"; static const String spaseManagementIcon =
"assets/images/spase_management_icon.svg";
static const String devicesIcon = "assets/images/devices_icon.svg"; static const String devicesIcon = "assets/images/devices_icon.svg";
static const String moveinIcon = "assets/images/movein_icon.svg"; static const String moveinIcon = "assets/images/movein_icon.svg";
static const String constructionIcon = "assets/images/construction_icon.svg"; static const String constructionIcon = "assets/images/construction_icon.svg";

View File

@ -1,4 +1,3 @@
enum AccessType { enum AccessType {
onlineOnetime, onlineOnetime,
onlineMultiple, onlineMultiple,
@ -36,11 +35,6 @@ extension AccessTypeExtension on AccessType {
} }
} }
enum DeviseStatus { enum DeviseStatus {
online, online,
offline, offline,
@ -53,7 +47,6 @@ extension OnlineTypeExtension on DeviseStatus {
return "Online"; return "Online";
case DeviseStatus.offline: case DeviseStatus.offline:
return "Offline"; return "Offline";
} }
} }
@ -69,10 +62,9 @@ extension OnlineTypeExtension on DeviseStatus {
} }
} }
enum AccessStatus { enum AccessStatus {
expired , expired,
effective , effective,
toBeEffective, toBeEffective,
} }
@ -82,28 +74,22 @@ extension AccessStatusExtension on AccessStatus {
case AccessStatus.expired: case AccessStatus.expired:
return "Expired"; return "Expired";
case AccessStatus.effective: case AccessStatus.effective:
return "Effective" ; return "Effective";
case AccessStatus.toBeEffective: case AccessStatus.toBeEffective:
return "To be effective"; return "To be effective";
} }
} }
static AccessStatus fromString(String value) { static AccessStatus fromString(String value) {
switch (value) { switch (value) {
case "EXPIRED" : case "EXPIRED":
return AccessStatus.expired; return AccessStatus.expired;
case "EFFECTIVE" : case "EFFECTIVE":
return AccessStatus.effective; return AccessStatus.effective;
case "TO_BE_EFFECTIVE": case "TO_BE_EFFECTIVE":
return AccessStatus.toBeEffective; return AccessStatus.toBeEffective;
default: default:
throw ArgumentError("Invalid access type: $value"); throw ArgumentError("Invalid access type: $value");
} }
} }
} }

View File

@ -0,0 +1,11 @@
import 'package:intl/intl.dart';
String formatDateTime(DateTime? dateTime) {
if (dateTime == null) {
return '-';
}
final DateFormat dateFormatter = DateFormat('dd/MM/yyyy');
final DateFormat timeFormatter = DateFormat('HH:mm');
return '${dateFormatter.format(dateTime)} ${timeFormatter.format(dateTime)}';
}

View File

@ -4,7 +4,7 @@ String decodeBase64(String str) {
//'-', '+' 62nd char of encoding, '_', '/' 63rd char of encoding //'-', '+' 62nd char of encoding, '_', '/' 63rd char of encoding
String output = str.replaceAll('-', '+').replaceAll('_', '/'); String output = str.replaceAll('-', '+').replaceAll('_', '/');
switch (output.length % 4) { switch (output.length % 4) {
// Pad with trailing '=' // Pad with trailing '='
case 0: // No pad chars in this case case 0: // No pad chars in this case
break; break;
case 2: // Two pad chars case 2: // Two pad chars

View File

@ -1,19 +1,20 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
class ResponsiveLayout extends StatelessWidget { class ResponsiveLayout extends StatelessWidget {
final Widget desktopBody; final Widget desktopBody;
final Widget mobileBody; final Widget mobileBody;
const ResponsiveLayout({super.key,required this.desktopBody,required this.mobileBody}); const ResponsiveLayout(
{super.key, required this.desktopBody, required this.mobileBody});
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return LayoutBuilder(builder: (context, constraints) { return LayoutBuilder(
if(constraints.maxWidth<600){ builder: (context, constraints) {
return mobileBody; if (constraints.maxWidth < 600) {
}else{ return mobileBody;
return desktopBody; } else {
} return desktopBody;
}, }
},
); );
} }
} }

View File

@ -16,7 +16,6 @@ class CustomSnackBar {
BuildContext? currentContext = key?.currentContext; BuildContext? currentContext = key?.currentContext;
if (key != null && currentContext != null) { if (key != null && currentContext != null) {
final snackBar = SnackBar( final snackBar = SnackBar(
padding: const EdgeInsets.all(16), padding: const EdgeInsets.all(16),
backgroundColor: Colors.green, backgroundColor: Colors.green,
content: Row(mainAxisAlignment: MainAxisAlignment.center, children: [ content: Row(mainAxisAlignment: MainAxisAlignment.center, children: [

View File

@ -1,46 +1,43 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'color_manager.dart'; import 'color_manager.dart';
InputDecoration? textBoxDecoration({bool suffixIcon = false}) => InputDecoration( InputDecoration? textBoxDecoration({bool suffixIcon = false}) =>
focusColor: ColorsManager.grayColor, InputDecoration(
suffixIcon:suffixIcon? const Icon(Icons.search):null, focusColor: ColorsManager.grayColor,
hintText: 'Search', suffixIcon: suffixIcon ? const Icon(Icons.search) : null,
filled: true, // Enable background filling hintText: 'Search',
fillColor: const Color(0xffF5F6F7), // Set the background color filled: true, // Enable background filling
border: OutlineInputBorder( fillColor: const Color(0xffF5F6F7), // Set the background color
borderRadius: BorderRadius.circular(8), // Add border radius border: OutlineInputBorder(
borderSide: BorderSide.none, // Remove the underline borderRadius: BorderRadius.circular(8), // Add border radius
), borderSide: BorderSide.none, // Remove the underline
enabledBorder: OutlineInputBorder( ),
borderRadius: BorderRadius.circular(8), // Add border radius enabledBorder: OutlineInputBorder(
borderSide: BorderSide.none, // Remove the underline borderRadius: BorderRadius.circular(8), // Add border radius
), borderSide: BorderSide.none, // Remove the underline
focusedBorder: OutlineInputBorder( ),
borderRadius: BorderRadius.circular(8), // Add border radius focusedBorder: OutlineInputBorder(
borderSide: BorderSide.none, // Remove the underline borderRadius: BorderRadius.circular(8), // Add border radius
), borderSide: BorderSide.none, // Remove the underline
errorBorder: OutlineInputBorder( ),
borderSide: BorderSide(color: Colors.red, width: 2), errorBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(8), borderSide: BorderSide(color: Colors.red, width: 2),
), borderRadius: BorderRadius.circular(8),
focusedErrorBorder: OutlineInputBorder( ),
borderSide: BorderSide(color: Colors.red, width: 2), focusedErrorBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(8), borderSide: BorderSide(color: Colors.red, width: 2),
), borderRadius: BorderRadius.circular(8),
); ),
);
BoxDecoration containerDecoration = BoxDecoration(
BoxDecoration containerDecoration = BoxDecoration(
boxShadow: [ boxShadow: [
BoxShadow( BoxShadow(
color: Colors.grey.withOpacity(0.5), color: Colors.grey.withOpacity(0.5),
spreadRadius: 5, spreadRadius: 5,
blurRadius: 8, blurRadius: 8,
offset: const Offset(0, offset: const Offset(0, 3), // changes position of shadow
3), // changes position of shadow
), ),
], ],
color: ColorsManager.boxColor, color: ColorsManager.boxColor,
borderRadius: const BorderRadius.all(Radius.circular(10))); borderRadius: const BorderRadius.all(Radius.circular(10)));

View File

@ -21,30 +21,35 @@ class MenuSidebar extends StatelessWidget {
color: ColorsManager.whiteColors, color: ColorsManager.whiteColors,
), ),
width: 200, width: 200,
child: Padding( child: Padding(
padding: const EdgeInsets.all(15.0), padding: const EdgeInsets.all(15.0),
child: Column( child: Column(
children: [ children: [
Row( Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [ children: [
const Text('Community',style: TextStyle(fontSize: 20),), const Text(
'Community',
style: TextStyle(fontSize: 20),
),
CircleAvatar( CircleAvatar(
backgroundColor: Colors.grey.shade200, backgroundColor: Colors.grey.shade200,
child: IconButton( child: IconButton(
color: ColorsManager.onSecondaryColor, color: ColorsManager.onSecondaryColor,
onPressed: () {}, onPressed: () {},
icon: const Icon(Icons.add) icon: const Icon(Icons.add)),
), )
)
], ],
), ),
const SizedBox(height: 20,), const SizedBox(
TextFormField( height: 20,
controller: TextEditingController(),
decoration:textBoxDecoration(suffixIcon: true)
), ),
Container(height: 100,) TextFormField(
controller: TextEditingController(),
decoration: textBoxDecoration(suffixIcon: true)),
Container(
height: 100,
)
], ],
), ),
), ),

View File

@ -3,51 +3,54 @@ import 'package:flutter_svg/svg.dart';
import 'package:syncrow_web/utils/constants/assets.dart'; import 'package:syncrow_web/utils/constants/assets.dart';
import 'package:syncrow_web/web_layout/web_app_bar.dart'; import 'package:syncrow_web/web_layout/web_app_bar.dart';
import 'menu_sidebar.dart'; import 'menu_sidebar.dart';
class WebScaffold extends StatelessWidget { class WebScaffold extends StatelessWidget {
final bool enableMenuSideba; final bool enableMenuSideba;
final Widget? appBarTitle; final Widget? appBarTitle;
final List<Widget>? appBarBody; final List<Widget>? appBarBody;
final Widget? scaffoldBody; final Widget? scaffoldBody;
const WebScaffold({super.key,this.appBarTitle,this.appBarBody,this.scaffoldBody,this.enableMenuSideba=true}); const WebScaffold(
{super.key,
this.appBarTitle,
this.appBarBody,
this.scaffoldBody,
this.enableMenuSideba = true});
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Scaffold( return Scaffold(
body: Stack( body: Stack(
children: [
SizedBox(
width: MediaQuery.sizeOf(context).width,
height: MediaQuery.sizeOf(context).height,
child: SvgPicture.asset(
Assets.webBackground,
fit: BoxFit.cover,
),
),
Container(
color: Colors.white.withOpacity(0.7),
),
Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [ children: [
SizedBox( Opacity(
width: MediaQuery.sizeOf(context).width, opacity: 0.7,
height: MediaQuery.sizeOf(context).height, child: WebAppBar(
child: SvgPicture.asset( title: appBarTitle,
Assets.webBackground, body: appBarBody,
fit: BoxFit.cover, )),
Expanded(
child: Row(
children: [
if (enableMenuSideba) const MenuSidebar(),
Expanded(flex: 5, child: scaffoldBody!)
],
), ),
), )
Container(color: Colors.white.withOpacity(0.7),),
Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Opacity(
opacity: 0.7,
child: WebAppBar(
title: appBarTitle,
body: appBarBody,
)
),
Expanded(
child: Row(
children: [
if(enableMenuSideba)
const MenuSidebar(),
Expanded(
flex: 5,
child: scaffoldBody!
)
],
),
)
],
),
], ],
)); ),
],
));
} }
} }