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

View File

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

View File

@ -11,6 +11,7 @@ abstract class AccessState extends Equatable {
class AccessInitial extends AccessState {}
class AccessLoaded extends AccessState {}
class FailedState extends AccessState {
final String message;

View File

@ -30,9 +30,9 @@ class PasswordModel {
effectiveTime: json['effectiveTime'],
passwordCreated: json['passwordCreated'],
createdTime: json['createdTime'],
passwordName: json['passwordName']??'No name', // New field
passwordStatus:AccessStatusExtension.fromString(json['passwordStatus']),
passwordType:AccessTypeExtension.fromString(json['passwordType']),
passwordName: json['passwordName'] ?? 'No name', // New field
passwordStatus: AccessStatusExtension.fromString(json['passwordStatus']),
passwordType: AccessTypeExtension.fromString(json['passwordType']),
deviceUuid: json['deviceUuid'],
);
}
@ -50,5 +50,4 @@ class PasswordModel {
'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/date_time_widget.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/utils/color_manager.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") {
forgetValidate = 'Wrong one time password.';
emit(AuthInitialState());
}else if (response == "OTP expired") {
} else if (response == "OTP expired") {
forgetValidate = 'One time password has been expired.';
emit(AuthInitialState());
}
@ -94,6 +94,7 @@ class AuthBloc extends Bloc<AuthEvent, AuthState> {
// emit(FailureForgetState(error: failure.toString()));
}
}
//925207
String? validateCode(String? value) {
if (value == null || value.isEmpty) {

View File

@ -13,47 +13,60 @@ class LoginButtonPressed extends AuthEvent {
final String password;
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
List<Object> get props => [username, password,regionUuid];
List<Object> get props => [username, password, regionUuid];
}
class CheckBoxEvent extends AuthEvent {
final bool? newValue;
const CheckBoxEvent({required this.newValue,});
const CheckBoxEvent({
required this.newValue,
});
@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 {
final int remainingTime;
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;
const PasswordVisibleEvent({required this.newValue,});
const PasswordVisibleEvent({
required this.newValue,
});
}
class RegionInitialEvent extends AuthEvent {}
class CheckEnableEvent extends AuthEvent {}
class ChangeValidateEvent extends AuthEvent {}
class SelectRegionEvent extends AuthEvent {
@ -62,4 +75,3 @@ class SelectRegionEvent extends AuthEvent {
@override
List<Object> get props => [val];
}

View File

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

View File

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

View File

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

View File

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

View File

@ -10,7 +10,10 @@ class VerifyPassCode {
final String deviceId;
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(
phone: json[verificationPhone],

View File

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

View File

@ -1,10 +1,8 @@
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_web_page.dart';
import 'package:syncrow_web/utils/responsive_layout.dart';
class ForgetPasswordPage extends StatelessWidget {
const ForgetPasswordPage({super.key});
@ -12,8 +10,6 @@ class ForgetPasswordPage extends StatelessWidget {
Widget build(BuildContext context) {
return const ResponsiveLayout(
desktopBody: ForgetPasswordWebPage(),
mobileBody:ForgetPasswordWebPage()
);
mobileBody: ForgetPasswordWebPage());
}
}

View File

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

View File

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

View File

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

View File

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

View File

@ -40,14 +40,12 @@ class DefaultButton extends StatelessWidget {
: customButtonStyle ??
ButtonStyle(
textStyle: MaterialStateProperty.all(
customTextStyle
?? Theme.of(context).textTheme.bodySmall!.copyWith(
customTextStyle ??
Theme.of(context).textTheme.bodySmall!.copyWith(
fontSize: 13,
color: foregroundColor,
fontWeight: FontWeight.normal
fontWeight: FontWeight.normal),
),
),
foregroundColor: MaterialStateProperty.all(
isSecondary
? Colors.black

View File

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

View File

@ -224,8 +224,14 @@ class _DynamicTableState extends State<DynamicTable> {
alignment: Alignment.centerLeft,
child: Text(
content,
style: const TextStyle(
color: Colors.black, fontSize: 10, fontWeight: FontWeight.w400),
style: TextStyle(
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_svg/flutter_svg.dart';
import 'package:syncrow_web/utils/color_manager.dart';
import 'package:syncrow_web/utils/constants/assets.dart';
import 'package:syncrow_web/utils/style.dart';
class DateTimeWebWidget extends StatelessWidget {
@ -34,7 +33,7 @@ class DateTimeWebWidget extends StatelessWidget {
children: [
Row(
children: [
if(isRequired)
if (isRequired)
Text(
'* ',
style: Theme.of(context)
@ -42,15 +41,22 @@ class DateTimeWebWidget extends StatelessWidget {
.bodyMedium!
.copyWith(color: Colors.red),
),
Text(title??'' ,
style: Theme.of(context).textTheme.bodySmall!.copyWith(
color: Colors.black,fontSize: 13),),
Text(
title,
style: Theme.of(context)
.textTheme
.bodySmall!
.copyWith(color: Colors.black, fontSize: 13),
),
],
),
const SizedBox(height: 8,),
const SizedBox(
height: 8,
),
Container(
height:size.height * 0.055 ,
padding: EdgeInsets.only(top: 10,bottom: 10,right: 30,left: 10),
height: size.height * 0.055,
padding:
const EdgeInsets.only(top: 10, bottom: 10, right: 30, left: 10),
decoration: containerDecoration,
child: FittedBox(
child: Column(
@ -61,25 +67,41 @@ class DateTimeWebWidget extends StatelessWidget {
InkWell(
onTap: startTime,
child: FittedBox(
child: Text(firstString,
style: Theme.of(context).textTheme.bodySmall!.copyWith(
color: ColorsManager.grayColor,fontSize: 12,fontWeight: FontWeight.w400),),
)
),
SizedBox(width: 30,),
const Icon(Icons.arrow_right_alt),
SizedBox(width: 30,),
InkWell(
onTap:endTime,
child: FittedBox(
child: Text(secondString,
style: Theme.of(context).textTheme.bodySmall!.copyWith(
color: ColorsManager.grayColor,fontSize: 12,fontWeight: FontWeight.w400),
child: Text(
firstString,
style: Theme.of(context)
.textTheme
.bodySmall!
.copyWith(
color: ColorsManager.grayColor,
fontSize: 12,
fontWeight: FontWeight.w400),
),
)),
SizedBox(width: 30,),
const SizedBox(
width: 30,
),
const Icon(Icons.arrow_right_alt),
const SizedBox(
width: 30,
),
InkWell(
onTap: endTime,
child: FittedBox(
child: Text(
secondString,
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 {
final Widget? second;
const FirstLayer({super.key,this.second});
const FirstLayer({super.key, this.second});
@override
Widget build(BuildContext context) {

View File

@ -1,5 +1,3 @@
import 'package:flutter/material.dart';
class HourPickerDialog extends StatefulWidget {
@ -17,7 +15,9 @@ class _HourPickerDialogState extends State<HourPickerDialog> {
@override
void 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;
}

View File

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

View File

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

View File

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

View File

@ -1,13 +1,130 @@
import 'package:bloc/bloc.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_state.dart';
class DeviceManagmentBloc extends Bloc<DeviceManagmentEvent, DeviceManagmentState> {
DeviceManagmentBloc() : super(DeviceManagmentInitial()) {
on<DeviceManagmentEvent>((event, emit) {
// TODO: implement event handler
});
class DeviceManagementBloc
extends Bloc<DeviceManagementEvent, DeviceManagementState> {
int _selectedIndex = 0;
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';
sealed class DeviceManagmentEvent extends Equatable {
const DeviceManagmentEvent();
abstract class DeviceManagementEvent extends Equatable {
const DeviceManagementEvent();
@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';
sealed class DeviceManagmentState extends Equatable {
const DeviceManagmentState();
abstract class DeviceManagementState extends Equatable {
const DeviceManagementState();
@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_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/web_layout/web_scaffold.dart';
@ -7,17 +9,31 @@ class DeviceManagementPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return WebScaffold(
enableMenuSideba: true,
appBarTitle: Row(
children: [
Text(
return BlocProvider(
create: (context) => DeviceManagementBloc()..add(FetchDevices()),
child: WebScaffold(
appBarTitle: Text(
'Device Management',
style: Theme.of(context).textTheme.headlineLarge,
)
],
),
scaffoldBody: const DeviceManagementBody(),
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'));
}
},
),
),
);
}
}

View File

@ -1,17 +1,53 @@
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/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/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/style.dart';
class DeviceManagementBody extends StatelessWidget with HelperResponsiveLayout {
const DeviceManagementBody({super.key});
const DeviceManagementBody({super.key, required this.devices});
final List<AllDevicesModel> devices;
@override
Widget build(BuildContext context) {
return BlocBuilder<DeviceManagementBloc, DeviceManagementState>(
builder: (context, state) {
List<AllDevicesModel> devicesToShow = [];
int selectedIndex = 0;
int onlineCount = 0;
int offlineCount = 0;
int lowBatteryCount = 0;
if (state is DeviceManagementLoaded) {
devicesToShow = state.devices;
selectedIndex = state.selectedIndex;
onlineCount = state.onlineCount;
offlineCount = state.offlineCount;
lowBatteryCount = state.lowBatteryCount;
} else if (state is DeviceManagementFiltered) {
devicesToShow = state.filteredDevices;
selectedIndex = state.selectedIndex;
onlineCount = state.onlineCount;
offlineCount = state.offlineCount;
lowBatteryCount = state.lowBatteryCount;
}
// 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,
@ -20,68 +56,34 @@ class DeviceManagementBody extends StatelessWidget with HelperResponsiveLayout {
crossAxisAlignment: CrossAxisAlignment.start,
children: [
FilterWidget(
size: context.screenSize,
tabs: ['All', 'Online', 'Offline', 'Low Battery'],
selectedIndex: 0,
onTabChanged: (index) {},
size: MediaQuery.of(context).size,
tabs: tabs,
selectedIndex: selectedIndex,
onTabChanged: (index) {
context
.read<DeviceManagementBloc>()
.add(SelectedFilterChanged(index));
},
),
const SizedBox(
height: 20,
),
if (isLargeScreenSize(context)) ...[
Row(
children: [
const StatefulTextField(
title: "Community",
width: 200,
elevation: 2,
const DeviceSearchFilters(),
const SizedBox(
height: 12,
),
const SizedBox(width: 20),
const StatefulTextField(
title: "Unit Name",
width: 200,
elevation: 2,
Container(
height: 43,
width: 100,
decoration: containerDecoration,
child: Center(
child: DefaultButton(
onPressed: () {},
borderRadius: 9,
child: const Text('Control'),
),
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,
),
@ -111,19 +113,28 @@ class DeviceManagementBody extends StatelessWidget with HelperResponsiveLayout {
'Status',
'Last Offline Date and Time',
],
data: []
// state.data.map((item) {
// return [
// item.name.toString(),
// item.uuid.toString(),
// item.productType.toString(),
// '',
// item.online.value.toString(),
// ];
// }).toList(),
)),
data: devicesToShow.map((device) {
return [
device.categoryName ?? '',
device.name ?? '',
device.uuid ?? '',
device.unit?.name ?? '',
device.room?.name ?? '',
device.batteryLevel?.toString() ?? '',
formatDateTime(DateTime.fromMillisecondsSinceEpoch(
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
List<Object> get props => [graph, builder];
}
class HomeUserInfoLoaded extends HomeState {
final UserModel user;

View File

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

View File

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

View File

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

View File

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

View File

@ -39,7 +39,8 @@ class HomeWebPage extends StatelessWidget {
Text(
'ACCESS YOUR APPS',
style: Theme.of(context)
.textTheme.headlineLarge!
.textTheme
.headlineLarge!
.copyWith(color: Colors.black, fontSize: 40),
),
const SizedBox(height: 30),
@ -63,7 +64,8 @@ class HomeWebPage extends StatelessWidget {
active: homeBloc.homeItems[index].active!,
name: homeBloc.homeItems[index].title!,
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(
width: 100,
child: TextFormField(
decoration:
const InputDecoration(labelText: "Subtree separation"),
decoration: const InputDecoration(
labelText: "Subtree separation"),
onChanged: (text) {
firstNodeName = text;
},
@ -73,9 +73,7 @@ class TreeWidget extends StatelessWidget {
child: GraphView(
graph: state.graph,
algorithm: BuchheimWalkerAlgorithm(
state.builder,
TreeEdgeRenderer(state.builder)
),
state.builder, TreeEdgeRenderer(state.builder)),
paint: Paint()
..color = Colors.green
..strokeWidth = 1
@ -138,7 +136,7 @@ Widget rectangleWidget(String text, Node node, BuildContext blocContext) {
);
},
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),
padding: EdgeInsets.all(20.0),
decoration: BoxDecoration(

View File

@ -220,7 +220,6 @@ class VisitorPasswordBloc
}
}
Future<void> postOnlineMultipleTimePassword(
OnlineMultipleTimePasswordEvent event,
Emitter<VisitorPasswordState> emit) async {
@ -246,11 +245,10 @@ class VisitorPasswordBloc
passwordName: event.passwordName);
if (res == true) {
emit(SuccessState());
}else {
} else {
throw Exception('Failed to create password');
}
emit(TableLoaded(data));
} catch (e) {
emit(FailedState(e.toString()));
Navigator.pop(event.context!);
@ -273,11 +271,10 @@ class VisitorPasswordBloc
passwordName: event.passwordName);
if (res == true) {
emit(SuccessState());
}else {
} else {
throw Exception('Failed to create password');
}
emit(TableLoaded(data));
} catch (e) {
emit(FailedState(e.toString()));
Navigator.pop(event.context!);
@ -303,18 +300,18 @@ class VisitorPasswordBloc
);
if (res == true) {
emit(SuccessState());
}else {
} else {
throw Exception('Failed to create password');
}
emit(TableLoaded(data));
} catch (e) {
emit(FailedState(e.toString()));
Navigator.pop(event.context!);
stateDialog(
context: event.context!,
message: e.toString(),
title: 'Something Wrong'); }
title: 'Something Wrong');
}
}
void selectDevice(

View File

@ -26,19 +26,19 @@ class SelectUsageFrequency extends VisitorPasswordEvent {
@override
List<Object> get props => [usageType];
}
class SelectTimeVisitorPassword extends VisitorPasswordEvent {
final BuildContext context;
final bool isStart;
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
List<Object> get props => [context,isStart,isRepeat];
List<Object> get props => [context, isStart, isRepeat];
}
class ToggleDaySelectionEvent extends VisitorPasswordEvent {
final String key;
@ -47,12 +47,11 @@ class ToggleDaySelectionEvent extends VisitorPasswordEvent {
List<Object> get props => [key];
}
class ToggleRepeatEvent extends VisitorPasswordEvent {}
class GeneratePasswordEvent extends VisitorPasswordEvent {}
class FetchDevice extends VisitorPasswordEvent {
}
class FetchDevice extends VisitorPasswordEvent {}
//online password
class OnlineOneTimePasswordEvent extends VisitorPasswordEvent {
@ -60,20 +59,31 @@ class OnlineOneTimePasswordEvent extends VisitorPasswordEvent {
final String? passwordName;
final BuildContext? context;
const OnlineOneTimePasswordEvent({this.email,this.passwordName,this.context});
const OnlineOneTimePasswordEvent(
{this.email, this.passwordName, this.context});
@override
List<Object> get props => [email!,passwordName!,];
List<Object> get props => [
email!,
passwordName!,
];
}
class OnlineMultipleTimePasswordEvent extends VisitorPasswordEvent {
final String? email;
final String? passwordName;
final String? invalidTime;
final String? effectiveTime;
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
List<Object> get props => [email!,passwordName!,invalidTime!,effectiveTime!,context!];
List<Object> get props =>
[email!, passwordName!, invalidTime!, effectiveTime!, context!];
}
//offline password
@ -81,9 +91,14 @@ class OfflineOneTimePasswordEvent extends VisitorPasswordEvent {
final BuildContext? context;
final String? email;
final String? passwordName;
const OfflineOneTimePasswordEvent({this.email,this.passwordName,this.context});
const OfflineOneTimePasswordEvent(
{this.email, this.passwordName, this.context});
@override
List<Object> get props => [email!,passwordName!,context!,];
List<Object> get props => [
email!,
passwordName!,
context!,
];
}
class OfflineMultipleTimePasswordEvent extends VisitorPasswordEvent {
@ -93,13 +108,18 @@ class OfflineMultipleTimePasswordEvent extends VisitorPasswordEvent {
final String? effectiveTime;
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
List<Object> get props => [email!,passwordName!,invalidTime!,effectiveTime!,context!];
List<Object> get props =>
[email!, passwordName!, invalidTime!, effectiveTime!, context!];
}
class SelectDeviceEvent extends VisitorPasswordEvent {
final String deviceId;
const SelectDeviceEvent(this.deviceId);
@ -116,22 +136,26 @@ class FilterDataEvent extends VisitorPasswordEvent {
this.endTime,
});
}
class UpdateFilteredDevicesEvent extends VisitorPasswordEvent {
final List<DeviceModel> filteredData;
UpdateFilteredDevicesEvent(this.filteredData);
}class SelectTimeEvent extends VisitorPasswordEvent {
}
class SelectTimeEvent extends VisitorPasswordEvent {
final BuildContext context;
final bool isEffective;
const SelectTimeEvent({required this.context,required this.isEffective});
const SelectTimeEvent({required this.context, required this.isEffective});
@override
List<Object> get props => [context,isEffective];
List<Object> get props => [context, isEffective];
}
class ChangeTimeEvent extends VisitorPasswordEvent {
final dynamic val;
final bool isStartEndTime;
const ChangeTimeEvent({required this.val,required this.isStartEndTime});
const ChangeTimeEvent({required this.val, required this.isStartEndTime});
@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:syncrow_web/pages/visitor_password/model/device_model.dart';
@ -12,8 +10,6 @@ abstract class VisitorPasswordState extends Equatable {
class VisitorPasswordInitial extends VisitorPasswordState {}
class PasswordTypeSelected extends VisitorPasswordState {
final String selectedType;
const PasswordTypeSelected(this.selectedType);
@ -36,13 +32,16 @@ class IsRepeatState extends VisitorPasswordState {
@override
List<Object> get props => [repeat];
}
class LoadingInitialState extends VisitorPasswordState {}
class ChangeTimeState extends VisitorPasswordState {}
class TimeSelectedState extends VisitorPasswordState {}
class DeviceLoaded extends VisitorPasswordState {}
class SuccessState extends VisitorPasswordState {}
class FailedState extends VisitorPasswordState {

View File

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

View File

@ -44,7 +44,8 @@ class RepeatWidget extends StatelessWidget {
value: visitorBloc.selectedDays.contains(day['key']),
onChanged: (bool? value) {
if (value != null) {
visitorBloc.add(ToggleDaySelectionEvent(key: day['key']!));
visitorBloc
.add(ToggleDaySelectionEvent(key: day['key']!));
}
},
),
@ -60,18 +61,20 @@ class RepeatWidget extends StatelessWidget {
title: '',
size: size,
endTime: () {
visitorBloc.add(SelectTimeEvent(
context: context,
isEffective: false));
visitorBloc
.add(SelectTimeEvent(context: context, isEffective: false));
Future.delayed(const Duration(milliseconds: 500), () {
visitorBloc.add(ChangeTimeEvent(val: visitorBloc.endTime, isStartEndTime: true));
visitorBloc.add(ChangeTimeEvent(
val: visitorBloc.endTime, isStartEndTime: true));
});
},
startTime: () {
Future.delayed(const Duration(milliseconds: 500), () {
visitorBloc.add(ChangeTimeEvent(val: visitorBloc.endTime, isStartEndTime: true));
visitorBloc.add(ChangeTimeEvent(
val: visitorBloc.endTime, isStartEndTime: true));
});
visitorBloc.add(SelectTimeEvent(context: context, isEffective: true));
visitorBloc
.add(SelectTimeEvent(context: context, isEffective: true));
},
firstString: visitorBloc.effectiveTime,
secondString: visitorBloc.expirationTime,
@ -80,7 +83,6 @@ class RepeatWidget extends StatelessWidget {
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/utils/constants/api_const.dart';
class AccessMangApi{
class AccessMangApi {
Future<List<PasswordModel>> fetchVisitorPassword() async {
try {
final response = await HTTPService().get(
@ -49,8 +48,8 @@ class AccessMangApi{
}
}
Future<bool> postOnlineOneTime({
String? email,
Future<bool> postOnlineOneTime(
{String? email,
String? passwordName,
String? password,
String? effectiveTime,
@ -64,14 +63,14 @@ class AccessMangApi{
"passwordName": passwordName,
"password": password,
"devicesUuid": devicesUuid,
"effectiveTime":effectiveTime ,
"effectiveTime": effectiveTime,
"invalidTime": invalidTime
}),
showServerMessage: true,
expectedResponseModel: (json) {
if(json['statusCode'].toString()=='201'){
if (json['statusCode'].toString() == '201') {
return true;
}else{
} else {
return false;
}
},
@ -84,8 +83,8 @@ class AccessMangApi{
}
}
Future postOnlineMultipleTime({
String? effectiveTime,
Future postOnlineMultipleTime(
{String? effectiveTime,
String? invalidTime,
String? email,
String? password,
@ -102,22 +101,24 @@ class AccessMangApi{
"invalidTime": invalidTime,
};
if (scheduleList != null) {
body["scheduleList"] = scheduleList.map((schedule) => schedule.toJson()).toList();
body["scheduleList"] =
scheduleList.map((schedule) => schedule.toJson()).toList();
}
final response = await HTTPService().post(
path: ApiEndpoints.sendOnlineMultipleTime,
body: jsonEncode(body),
showServerMessage: true,
expectedResponseModel: (json) {
if(json['data']['successOperations'][0]['success'].toString()=='true'){
if (json['data']['successOperations'][0]['success'].toString() ==
'true') {
return true;
}else{
} else {
return false;
}
},
);
return response;
} on DioException catch (e){
} on DioException catch (e) {
debugPrint('Error fetching ${e.type.name}');
debugPrint('Error fetching ${e.response!.statusMessage}');
return false;
@ -126,7 +127,8 @@ class AccessMangApi{
// 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 {
final response = await HTTPService().post(
path: ApiEndpoints.sendOffLineOneTime,
@ -143,8 +145,7 @@ class AccessMangApi{
} else {
return false;
}
}
);
});
return response;
} catch (e) {
debugPrint('Error fetching $e');
@ -152,20 +153,18 @@ class AccessMangApi{
}
}
Future postOffLineMultipleTime({
String? email,
Future postOffLineMultipleTime(
{String? email,
String? passwordName,
String? effectiveTime,
String? invalidTime,
List<String>? devicesUuid
}) async {
List<String>? devicesUuid}) async {
try {
final response = await HTTPService().post(
path: ApiEndpoints.sendOffLineOneTime,
body: jsonEncode({
"email": email,
"devicesUuid":devicesUuid,
"devicesUuid": devicesUuid,
"passwordName": passwordName,
"effectiveTime": effectiveTime,
"invalidTime": invalidTime
@ -178,8 +177,7 @@ class AccessMangApi{
} else {
return false;
}
}
);
});
return response;
} catch (e) {
debugPrint('Error fetching $e');

View File

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

View File

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

View File

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

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/utils/constants/api_const.dart';
class HomeApi{
class HomeApi {
Future fetchUserInfo(userId) async {
final response = await HTTPService().get(
path: ApiEndpoints.getUser.replaceAll('{userUuid}', userId!),
showServerMessage: true,
expectedResponseModel: (json) {
return UserModel.fromJson(json);
}
);
});
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';
final GetIt serviceLocator = GetIt.instance;
//setupLocator() // to search for dependency injection in flutter
//setupLocator() // to search for dependency injection in flutter
initialSetup() {
serviceLocator.registerSingleton<HTTPInterceptor>(HTTPInterceptor());
//Base classes

View File

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

View File

@ -5,21 +5,28 @@ abstract class ApiEndpoints {
////////////////////////////////////// Authentication ///////////////////////////////
static const String signUp = '$baseUrl/authentication/user/signup';
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 verifyOtp = '$baseUrl/authentication/user/verify-otp';
static const String getRegion = '$baseUrl/region';
static const String visitorPassword = '$baseUrl/visitor-password';
static const String getDevices = '$baseUrl/visitor-password/devices';
static const String sendOnlineOneTime = '$baseUrl/visitor-password/temporary-password/online/one-time';
static const String sendOnlineMultipleTime = '$baseUrl/visitor-password/temporary-password/online/multiple-time';
static const String sendOnlineOneTime =
'$baseUrl/visitor-password/temporary-password/online/one-time';
static const String sendOnlineMultipleTime =
'$baseUrl/visitor-password/temporary-password/online/multiple-time';
//offline Password
static const String sendOffLineOneTime = '$baseUrl/visitor-password/temporary-password/offline/one-time';
static const String sendOffLineMultipleTime = '$baseUrl/visitor-password/temporary-password/offline/multiple-time';
static const String sendOffLineOneTime =
'$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}';
////// 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 google = "assets/images/google.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 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 moveinIcon = "assets/images/movein_icon.svg";
static const String constructionIcon = "assets/images/construction_icon.svg";

View File

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

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

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

View File

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

View File

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

View File

@ -28,23 +28,28 @@ class MenuSidebar extends StatelessWidget {
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
const Text('Community',style: TextStyle(fontSize: 20),),
const Text(
'Community',
style: TextStyle(fontSize: 20),
),
CircleAvatar(
backgroundColor: Colors.grey.shade200,
child: IconButton(
color: ColorsManager.onSecondaryColor,
onPressed: () {},
icon: const Icon(Icons.add)
),
icon: const Icon(Icons.add)),
)
],
),
const SizedBox(height: 20,),
const SizedBox(
height: 20,
),
TextFormField(
controller: TextEditingController(),
decoration:textBoxDecoration(suffixIcon: true)
),
Container(height: 100,)
decoration: textBoxDecoration(suffixIcon: true)),
Container(
height: 100,
)
],
),
),

View File

@ -3,12 +3,18 @@ import 'package:flutter_svg/svg.dart';
import 'package:syncrow_web/utils/constants/assets.dart';
import 'package:syncrow_web/web_layout/web_app_bar.dart';
import 'menu_sidebar.dart';
class WebScaffold extends StatelessWidget {
final bool enableMenuSideba;
final Widget? appBarTitle;
final List<Widget>? appBarBody;
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
Widget build(BuildContext context) {
return Scaffold(
@ -22,7 +28,9 @@ class WebScaffold extends StatelessWidget {
fit: BoxFit.cover,
),
),
Container(color: Colors.white.withOpacity(0.7),),
Container(
color: Colors.white.withOpacity(0.7),
),
Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
@ -31,17 +39,12 @@ class WebScaffold extends StatelessWidget {
child: WebAppBar(
title: appBarTitle,
body: appBarBody,
)
),
)),
Expanded(
child: Row(
children: [
if(enableMenuSideba)
const MenuSidebar(),
Expanded(
flex: 5,
child: scaffoldBody!
)
if (enableMenuSideba) const MenuSidebar(),
Expanded(flex: 5, child: scaffoldBody!)
],
),
)