region and access_management ui

This commit is contained in:
mohammad
2024-08-12 14:36:46 +03:00
parent 1d226742e6
commit cb0ebcca37
28 changed files with 703 additions and 206 deletions

88
assets/dome.json Normal file
View File

@ -0,0 +1,88 @@
[
{
"accessUser": "Ali Doe",
"accessType": "Admin",
"accessPeriod": "2023-08-01",
"accessibleDevice": "Smart Door",
"authorizationSource": "System",
"authorizer": "Jane Smith",
"authorizationTime": "2023-08-01 10:00 AM",
"accessStatus": "Granted",
"actions": "View"
}, {
"accessUser": "oamr Doe",
"accessType": "Admin",
"accessPeriod": "2023-08-01",
"accessibleDevice": "Smart Door",
"authorizationSource": "System",
"authorizer": "Jane Smith",
"authorizationTime": "2023-08-01 10:00 AM",
"accessStatus": "Granted",
"actions": "View"
}, {
"accessUser": "John Doe",
"accessType": "Admin",
"accessPeriod": "2023-08-01",
"accessibleDevice": "Smart Door",
"authorizationSource": "System",
"authorizer": "Jane Smith",
"authorizationTime": "2023-08-01 10:00 AM",
"accessStatus": "Granted",
"actions": "View"
},
{
"accessUser": "John Doe",
"accessType": "Admin",
"accessPeriod": "2023-08-01",
"accessibleDevice": "Smart Door",
"authorizationSource": "System",
"authorizer": "Jane Smith",
"authorizationTime": "2023-08-01 10:00 AM",
"accessStatus": "Granted",
"actions": "View"
},
{
"accessUser": "John Doe",
"accessType": "Admin",
"accessPeriod": "2023-08-01",
"accessibleDevice": "Smart Door",
"authorizationSource": "System",
"authorizer": "Jane Smith",
"authorizationTime": "2023-08-01 10:00 AM",
"accessStatus": "Granted",
"actions": "View"
},
{
"accessUser": "John Doe",
"accessType": "Admin",
"accessPeriod": "2023-08-01 ",
"accessibleDevice": "Smart Door",
"authorizationSource": "System",
"authorizer": "Jane Smith",
"authorizationTime": "2023-08-01 10:00 AM",
"accessStatus": "Granted",
"actions": "View"
}, {
"accessUser": "John Doe",
"accessType": "Admin",
"accessPeriod": "2023-08-01 ",
"accessibleDevice": "Smart Door",
"authorizationSource": "System",
"authorizer": "Jane Smith",
"authorizationTime": "2023-08-01 10:00 AM",
"accessStatus": "Granted",
"actions": "View"
},
{
"accessUser": "Alice Johnson",
"accessType": "User",
"accessPeriod": "2023-08-01 to 2023-08-31",
"accessibleDevice": "Smart Lock",
"authorizationSource": "Admin",
"authorizer": "John Doe",
"authorizationTime": "2023-08-02 11:00 AM",
"accessStatus": "Pending",
"actions": "Approve"
}
]

View File

@ -0,0 +1,13 @@
<svg width="11" height="11" viewBox="0 0 11 11" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_1338_8425)">
<path d="M8.70833 0.916667H8.25V0.458333C8.25 0.336776 8.20171 0.220197 8.11576 0.134243C8.0298 0.0482886 7.91322 0 7.79167 0C7.67011 0 7.55353 0.0482886 7.46758 0.134243C7.38162 0.220197 7.33333 0.336776 7.33333 0.458333V0.916667H3.66667V0.458333C3.66667 0.336776 3.61838 0.220197 3.53242 0.134243C3.44647 0.0482886 3.32989 0 3.20833 0C3.08678 0 2.9702 0.0482886 2.88424 0.134243C2.79829 0.220197 2.75 0.336776 2.75 0.458333V0.916667H2.29167C1.6841 0.917394 1.10163 1.15907 0.672017 1.58868C0.242404 2.0183 0.000727768 2.60077 0 3.20833L0 8.70833C0.000727768 9.3159 0.242404 9.89837 0.672017 10.328C1.10163 10.7576 1.6841 10.9993 2.29167 11H8.70833C9.3159 10.9993 9.89837 10.7576 10.328 10.328C10.7576 9.89837 10.9993 9.3159 11 8.70833V3.20833C10.9993 2.60077 10.7576 2.0183 10.328 1.58868C9.89837 1.15907 9.3159 0.917394 8.70833 0.916667ZM0.916667 3.20833C0.916667 2.84366 1.06153 2.49392 1.31939 2.23606C1.57726 1.9782 1.92699 1.83333 2.29167 1.83333H8.70833C9.07301 1.83333 9.42274 1.9782 9.68061 2.23606C9.93847 2.49392 10.0833 2.84366 10.0833 3.20833V3.66667H0.916667V3.20833ZM8.70833 10.0833H2.29167C1.92699 10.0833 1.57726 9.93847 1.31939 9.68061C1.06153 9.42274 0.916667 9.07301 0.916667 8.70833V4.58333H10.0833V8.70833C10.0833 9.07301 9.93847 9.42274 9.68061 9.68061C9.42274 9.93847 9.07301 10.0833 8.70833 10.0833Z" fill="#999999"/>
<path d="M5.5 7.5625C5.8797 7.5625 6.1875 7.2547 6.1875 6.875C6.1875 6.4953 5.8797 6.1875 5.5 6.1875C5.1203 6.1875 4.8125 6.4953 4.8125 6.875C4.8125 7.2547 5.1203 7.5625 5.5 7.5625Z" fill="#999999"/>
<path d="M3.20825 7.5625C3.58795 7.5625 3.89575 7.2547 3.89575 6.875C3.89575 6.4953 3.58795 6.1875 3.20825 6.1875C2.82856 6.1875 2.52075 6.4953 2.52075 6.875C2.52075 7.2547 2.82856 7.5625 3.20825 7.5625Z" fill="#999999"/>
<path d="M7.79175 7.5625C8.17144 7.5625 8.47925 7.2547 8.47925 6.875C8.47925 6.4953 8.17144 6.1875 7.79175 6.1875C7.41205 6.1875 7.10425 6.4953 7.10425 6.875C7.10425 7.2547 7.41205 7.5625 7.79175 7.5625Z" fill="#999999"/>
</g>
<defs>
<clipPath id="clip0_1338_8425">
<rect width="11" height="11" fill="white"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 2.2 KiB

View File

@ -29,9 +29,7 @@ class MyApp extends StatelessWidget {
providers: [
BlocProvider(create: (context) => HomeBloc()),
],
child:
MaterialApp(
child: MaterialApp(
debugShowCheckedModeBanner: false, // Hide debug banner
scrollBehavior: const MaterialScrollBehavior().copyWith(
dragDevices: {
@ -58,7 +56,7 @@ class MyApp extends StatelessWidget {
colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple), // Set up color scheme
useMaterial3: true, // Enable Material 3
),
home: isLoggedIn == 'Success' ? const HomePage() : const LoginPage(),
home: isLoggedIn == 'Success' ? const HomePage() : const LoginPage(),
));
}
}

View File

@ -1,142 +0,0 @@
import 'package:flutter/material.dart';
import 'package:flutter_svg/svg.dart';
import 'package:syncrow_web/utils/color_manager.dart';
import 'package:syncrow_web/utils/constants/assets.dart';
import 'package:syncrow_web/web_layout/web_scaffold.dart';
class AccessManagementPage extends StatelessWidget {
const AccessManagementPage({super.key});
@override
Widget build(BuildContext context) {
Size size = MediaQuery.of(context).size;
return WebScaffold(
enableMenuSideba: false,
appBarTitle: Row(
children: [
Text(
'Access Management',
style: Theme.of(context).textTheme.headlineLarge,
)
],
),
appBarBody: [
Text(
'Physical Access',
style: Theme.of(context)
.textTheme
.headlineMedium!
.copyWith(color: Colors.white),
),
Text(
'App Access',
style: Theme.of(context)
.textTheme
.headlineMedium!
.copyWith(color: Colors.white),
)
],
scaffoldBody: Container(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
width:size.width*0.3,
height: size.height*0.05,
decoration: BoxDecoration(
boxShadow: [
BoxShadow(
color: Colors.grey.withOpacity(0.5),
spreadRadius: 5,
blurRadius: 7,
offset: Offset(0, 3), // changes position of shadow
),
],
color: ColorsManager.boxColor,
borderRadius: BorderRadius.all(Radius.circular(10))),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
Text('All'),
Text('To Be Effective (0)'),
Text('Effective (0)'),
Text('Expired'),
],
),
),
SizedBox(height: 10,),
Row(
children: [
Container(
width:size.width*0.08,
height: size.height*0.05,
decoration: BoxDecoration(
boxShadow: [
BoxShadow(
color: Colors.grey.withOpacity(0.5),
spreadRadius: 5,
blurRadius: 7,
offset: Offset(0, 3), // changes position of shadow
),
],
color: ColorsManager.boxColor,
borderRadius: BorderRadius.all(Radius.circular(10))),
child: TextFormField()
),
Container(
width:size.width*0.08,
height: size.height*0.05,
decoration: BoxDecoration(
boxShadow: [
BoxShadow(
color: Colors.grey.withOpacity(0.5),
spreadRadius: 5,
blurRadius: 7,
offset: Offset(0, 3), // changes position of shadow
),
],
color: ColorsManager.boxColor,
borderRadius: BorderRadius.all(Radius.circular(10))),
child: TextFormField()
),
Container(
width:size.width*0.08,
height: size.height*0.05,
decoration: BoxDecoration(
boxShadow: [
BoxShadow(
color: Colors.grey.withOpacity(0.5),
spreadRadius: 5,
blurRadius: 7,
offset: Offset(0, 3), // changes position of shadow
),
],
color: ColorsManager.boxColor,
borderRadius: BorderRadius.all(Radius.circular(10))),
child: TextFormField()
),
Container(
width:size.width*0.08,
height: size.height*0.05,
decoration: BoxDecoration(
boxShadow: [
BoxShadow(
color: Colors.grey.withOpacity(0.5),
spreadRadius: 5,
blurRadius: 7,
offset: Offset(0, 3), // changes position of shadow
),
],
color: ColorsManager.boxColor,
borderRadius: BorderRadius.all(Radius.circular(10))),
child: TextFormField()
)
],
),
],
),
));
}
}

View File

@ -0,0 +1,26 @@
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:syncrow_web/pages/access_management/bloc/access_event.dart';
import 'package:syncrow_web/pages/access_management/bloc/access_state.dart';
import 'package:syncrow_web/pages/access_management/model/access_manag_model.dart';
import 'package:syncrow_web/services/access_mang_api.dart';
class AccessBloc extends Bloc<AccessEvent, AccessState> {
AccessBloc() : super((AccessInitial())) {
on<FetchTableData>(_onFetchTableData);
}
String startTime = 'Start Time';
String endTime = 'End Time';
Future<void> _onFetchTableData(
FetchTableData event, Emitter<AccessState> emit) async {
try {
emit(AccessLoaded());
List<AccessManagModel> data = await AccessMangApi().fetchInfo();
print('objectwww888888${data[0].accessPeriod}');
emit(TableLoaded(data));
} catch (e) {
emit(FailedState(e.toString()));
}
}
}

View File

@ -0,0 +1,11 @@
import 'package:equatable/equatable.dart';
abstract class AccessEvent extends Equatable {
const AccessEvent();
@override
List<Object> get props => [];
}
class FetchTableData extends AccessEvent {}

View File

@ -0,0 +1,31 @@
import 'package:equatable/equatable.dart';
import 'package:syncrow_web/pages/access_management/model/access_manag_model.dart';
abstract class AccessState extends Equatable {
const AccessState();
@override
List<Object> get props => [];
}
class AccessInitial extends AccessState {}
class AccessLoaded extends AccessState {}
class FailedState extends AccessState {
final String message;
FailedState(this.message);
@override
List<Object> get props => [message];
}
class TableLoaded extends AccessState {
final List<AccessManagModel> data;
const TableLoaded(this.data);
@override
List<Object> get props => [data];
}

View File

@ -0,0 +1,37 @@
class AccessManagModel {
final String accessUser;
final String accessType;
final String accessPeriod;
final String accessibleDevice;
final String authorizationSource;
final String authorizer;
final String authorizationTime;
final String accessStatus;
final String actions;
AccessManagModel({
required this.accessUser,
required this.accessType,
required this.accessPeriod,
required this.accessibleDevice,
required this.authorizationSource,
required this.authorizer,
required this.authorizationTime,
required this.accessStatus,
required this.actions,
});
factory AccessManagModel.fromJson(Map<String, dynamic> json) {
return AccessManagModel(
accessUser: json['accessUser'],
accessType: json['accessType'],
accessPeriod: json['accessPeriod'],
accessibleDevice: json['accessibleDevice'],
authorizationSource: json['authorizationSource'],
authorizer: json['authorizer'],
authorizationTime: json['authorizationTime'],
accessStatus: json['accessStatus'],
actions: json['actions'],
);
}
}

View File

@ -0,0 +1,301 @@
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:syncrow_web/pages/access_management/bloc/access_bloc.dart';
import 'package:syncrow_web/pages/access_management/bloc/access_event.dart';
import 'package:syncrow_web/pages/access_management/bloc/access_state.dart';
import 'package:syncrow_web/pages/common/default_button.dart';
import 'package:syncrow_web/utils/color_manager.dart';
import 'package:syncrow_web/utils/constants/assets.dart';
import 'package:syncrow_web/utils/style.dart';
import 'package:syncrow_web/web_layout/web_scaffold.dart';
class AccessManagementPage extends StatelessWidget {
const AccessManagementPage({super.key});
@override
Widget build(BuildContext context) {
Size size = MediaQuery.of(context).size;
return WebScaffold(
enableMenuSideba: false,
appBarTitle: Row(
children: [
Text(
'Access Management',
style: Theme.of(context).textTheme.headlineLarge,
)
],
),
appBarBody: [
Text(
'Physical Access',
style: Theme.of(context)
.textTheme
.headlineMedium!
.copyWith(color: Colors.white),
),
],
scaffoldBody: BlocProvider(
create: (BuildContext context) => AccessBloc()..add(FetchTableData() ),
child: BlocConsumer<AccessBloc, AccessState>(
listener: (context, state) {
if (state is FailedState) {
// CustomSnackBar.displaySnackBar(
// state.errorMessage
// );
}
}, builder: (context, state) {
return Container(
padding: EdgeInsets.all(30),
height: size.height,
width: size.width,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
width: size.width * 0.3,
height: size.height * 0.05,
decoration: containerDecoration,
child: const Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
Text('All'),
Text('To Be Effective (0)'),
Text('Effective (0)'),
Text('Expired'),
],
),
),
const SizedBox(
height: 20,
),
Wrap(
children: [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.start,
children: [
Text('User Name'),
Container(
width: size.width*0.15,
decoration: containerDecoration,
child: TextFormField(
decoration: textBoxDecoration()!
.copyWith(hintText: 'Please enter'),
)),
],
),
const SizedBox(width: 15,),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.start,
children: [
const Text('Email Address'),
Container(
width: size.width*0.15,
decoration: containerDecoration,
child: TextFormField(
decoration: textBoxDecoration()!
.copyWith(hintText: 'Please enter'),
)),
],
),
const SizedBox(width: 15,),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.start,
children: [
const Text('Access Time'),
Container(
width: size.width*0.18,
padding: EdgeInsets.all(10),
decoration: containerDecoration,
child: Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
InkWell(child: Text(BlocProvider.of<AccessBloc>(context).startTime)),
const Icon(Icons.arrow_right_alt),
InkWell(child: Text(BlocProvider.of<AccessBloc>(context).endTime)),
SvgPicture.asset(
Assets.calendarIcon,
),
],
),
],
)),
],
),
const SizedBox(width: 15,),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.start,
children: [
const Text('Authorization Source'),
Container(
width: size.width*0.18,
decoration: containerDecoration,
child: TextFormField(
decoration: textBoxDecoration(),
)),
],
),
const SizedBox(width: 15,),
SizedBox(
width: size.width*0.06,
child: Column(
children: [
Text(''),
Container(
decoration: containerDecoration,
child: DefaultButton(child: Text('Search'),borderRadius: 9)),
],
),
),
const SizedBox(width: 10,),
SizedBox(
width: size.width*0.06,
child: Column(
children: [
Text(''),
Container(
decoration: containerDecoration,
child: DefaultButton(
backgroundColor: ColorsManager.whiteColors,borderRadius: 9,
child: Text('Reset'
,style: Theme.of(context).textTheme.bodySmall!.copyWith(color: Colors.black),)
,),
),
],
),
),
],
),
const SizedBox(
height: 20,
),
Wrap(children: [
Container(
width: size.width*0.15,
decoration: containerDecoration,
child: const DefaultButton(
borderRadius: 8,
child: Text('+ Create Visitor Password ')),
),
const SizedBox(width: 10,),
Container(
width: size.width*0.12,
decoration: containerDecoration,
child: DefaultButton(
borderRadius: 8,
backgroundColor: ColorsManager.whiteColors,
child: Text('Admin Password'
,style: Theme.of(context).textTheme.bodySmall!.copyWith(color: Colors.black),)
))
],),
const SizedBox(
height: 20,
),
Expanded(
child:state is TableLoaded?
Container(
decoration: containerDecoration,
width: size.width,
child: Padding(
padding: const EdgeInsets.all(10.0),
child: ListView(
scrollDirection: Axis.horizontal,
children: [
Container(
width: size.width,
height:size.height ,
child: Column(
children: [
Container(
color: ColorsManager.boxColor,
child: Row(
children: [
_buildTableHeaderCell('Access User'),
_buildTableHeaderCell('Access Type'),
_buildTableHeaderCell('Access Period'),
_buildTableHeaderCell('Accessible Device'),
_buildTableHeaderCell('Authorization Source'),
_buildTableHeaderCell('Authorizer'),
_buildTableHeaderCell('Authorization Time'),
_buildTableHeaderCell('Access Status'),
_buildTableHeaderCell('Actions'),
],
),
),
Expanded(
child: Container(
width: size.width,
color: ColorsManager.whiteColors,
child: ListView(
shrinkWrap: true,
children: [
Column(
children: state.data.map((item) {
return Row(
children: [
_buildTableCell(item.accessUser),
_buildTableCell(item.accessType),
_buildTableCell(item.accessPeriod),
_buildTableCell(item.accessibleDevice),
_buildTableCell(item.authorizationSource),
_buildTableCell(item.authorizer),
_buildTableCell(item.authorizationTime),
_buildTableCell(item.accessStatus),
_buildTableCell(item.actions),
],
);
}).toList(),
),
],
),
),
),
],
),
),
],
),
),
):const Center(child: CircularProgressIndicator())
)
],
),
);
})));
}
}
Widget _buildTableHeaderCell(String title) {
return Expanded(
child: Container(
decoration: const BoxDecoration(
border: Border.symmetric(vertical: BorderSide(color: ColorsManager.boxDivider))
),
alignment: Alignment.centerLeft,
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Text(title, style: TextStyle(fontWeight: FontWeight.bold)),
),
),
);
}
Widget _buildTableCell(String content) {
return Expanded(
child: Container(
padding: const EdgeInsets.all(20.0),
decoration: const BoxDecoration(
border: Border.symmetric(horizontal: BorderSide(color: ColorsManager.boxDivider))
),
alignment: Alignment.centerLeft,
child: Text(content,style: TextStyle(color: Colors.black,fontSize: 12),),
),
);
}

View File

@ -23,6 +23,7 @@ class AuthBloc extends Bloc<AuthEvent, AuthState> {
on<UpdateTimerEvent>(_onUpdateTimer);
on<PasswordVisibleEvent>(_passwordVisible);
on<RegionInitialEvent>(_fetchRegion);
on<SelectRegionEvent>(selectRegion);
}
////////////////////////////// forget password //////////////////////////////////
@ -32,7 +33,7 @@ class AuthBloc extends Bloc<AuthEvent, AuthState> {
final forgetFormKey = GlobalKey<FormState>();
Timer? _timer;
int _remainingTime = 0;
List<RegionModel>? regionList;
List<RegionModel>? regionList=[RegionModel(name: 'name', id: 'id')];
Future<void> _onStartTimer(StartTimerEvent event, Emitter<AuthState> emit) async {
if (_validateInputs(emit)) return;
@ -42,7 +43,8 @@ class AuthBloc extends Bloc<AuthEvent, AuthState> {
_remainingTime = 60;
add(UpdateTimerEvent(
remainingTime: _remainingTime, isButtonEnabled: false));
await AuthenticationAPI.sendOtp(email: forgetEmailController.text);
debugPrint('_remainingTime=$_remainingTime');
_remainingTime = (await AuthenticationAPI.sendOtp(email: forgetEmailController.text,regionUuid: regionUuid))!;
_timer = Timer.periodic(const Duration(seconds: 1), (timer) {
_remainingTime--;
if (_remainingTime <= 0) {
@ -99,6 +101,7 @@ class AuthBloc extends Bloc<AuthEvent, AuthState> {
String maskedEmail = '';
String otpCode = '';
String validate = '';
String regionUuid = '';
static Token token = Token.emptyConstructor();
static UserModel? user;
bool showValidationMessage = false;
@ -116,6 +119,7 @@ class AuthBloc extends Bloc<AuthEvent, AuthState> {
model: LoginWithEmailModel(
email: event.username,
password: event.password,
regionUuid: event.regionUuid
),
);
} catch (failure) {
@ -274,15 +278,7 @@ class AuthBloc extends Bloc<AuthEvent, AuthState> {
return '$maskedLocalPart@$domainPart';
}
final List<String> regions = [
'North America',
'South America',
'Europe',
'Asia',
'Africa',
'Australia',
'Antarctica',
];
static Future<String> getTokenAndValidate() async {
@ -317,8 +313,8 @@ class AuthBloc extends Bloc<AuthEvent, AuthState> {
void _fetchRegion(RegionInitialEvent event, Emitter<AuthState> emit) async {
try {
emit(AuthLoading());
regionList = await AuthenticationAPI.fetchRegion();
emit(LoginSuccess());
regionList = await AuthenticationAPI.fetchRegion();
emit(AuthInitialState());
} catch (e) {
emit( LoginFailure(error: e.toString()));
@ -326,6 +322,34 @@ class AuthBloc extends Bloc<AuthEvent, AuthState> {
}
Future selectRegion(SelectRegionEvent event, Emitter<AuthState> emit) async {
try {
emit(AuthLoading());
regionUuid= event.val;
emit(AuthInitialState());
} catch (e) {
emit(FailureForgetState(error: e.toString()));
return;
}
}
String formattedTime(int time) {
final int days = (time / 86400).floor(); // 86400 seconds in a day
final int hours = ((time % 86400) / 3600).floor();
final int minutes = (((time % 86400) % 3600) / 60).floor();
final int seconds = (((time % 86400) % 3600) % 60).floor();
final String formattedTime = [
if (days > 0) '${days}d', // Append 'd' for days
if (days > 0 || hours > 0) hours.toString().padLeft(2, '0'), // Show hours if there are days or hours
minutes.toString().padLeft(2, '0'),
seconds.toString().padLeft(2, '0'),
].join(':');
return formattedTime;
}
}

View File

@ -1,4 +1,5 @@
import 'package:equatable/equatable.dart';
import 'package:flutter/material.dart';
abstract class AuthEvent extends Equatable {
const AuthEvent();
@ -10,11 +11,12 @@ abstract class AuthEvent extends Equatable {
class LoginButtonPressed extends AuthEvent {
final String username;
final String password;
final String regionUuid;
const LoginButtonPressed({required this.username, required this.password});
const LoginButtonPressed({required this.username, required this.password, required this.regionUuid});
@override
List<Object> get props => [username, password];
List<Object> get props => [username, password,regionUuid];
}
class CheckBoxEvent extends AuthEvent {
@ -52,4 +54,10 @@ class PasswordVisibleEvent extends AuthEvent{
class RegionInitialEvent extends AuthEvent {}
class SelectRegionEvent extends AuthEvent {}
class SelectRegionEvent extends AuthEvent {
final String val;
const SelectRegionEvent({required this.val});
@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 {}
@ -73,3 +74,12 @@ class AuthTokenError extends AuthError {
class AuthSuccess extends AuthState {}
class AuthTokenSuccess extends AuthSuccess {}
class TimerUpdated extends AuthState {
final String formattedTime;
final bool isButtonEnabled;
TimerUpdated({
required this.formattedTime,
required this.isButtonEnabled,
});
}

View File

@ -1,16 +1,19 @@
class LoginWithEmailModel {
final String email;
final String password;
final String regionUuid;
LoginWithEmailModel({
required this.email,
required this.password,
required this.regionUuid,
});
factory LoginWithEmailModel.fromJson(Map<String, dynamic> json) {
return LoginWithEmailModel(
email: json['email'],
password: json['password'],
regionUuid: json['regionUuid'],
);
}
@ -18,6 +21,7 @@ class LoginWithEmailModel {
return {
'email': email,
'password': password,
'regionUuid': regionUuid,
};
}
}

View File

@ -4,6 +4,7 @@ import 'package:flutter_svg/flutter_svg.dart';
import 'package:syncrow_web/pages/auth/bloc/auth_bloc.dart';
import 'package:syncrow_web/pages/auth/bloc/auth_event.dart';
import 'package:syncrow_web/pages/auth/bloc/auth_state.dart';
import 'package:syncrow_web/pages/auth/model/region_model.dart';
import 'package:syncrow_web/pages/common/default_button.dart';
import 'package:syncrow_web/pages/common/first_layer.dart';
import 'package:syncrow_web/utils/color_manager.dart';
@ -13,9 +14,10 @@ class ForgetPasswordWebPage extends StatelessWidget {
const ForgetPasswordWebPage({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
body: BlocProvider(
create: (context) => AuthBloc(),
create: (context) => AuthBloc()..add(RegionInitialEvent()),
child: BlocConsumer<AuthBloc, AuthState>(
listener: (context, state) {
if (state is SuccessForgetState){
@ -30,7 +32,7 @@ class ForgetPasswordWebPage extends StatelessWidget {
}
},
builder: (context, state) {
if (state is LoadingForgetState) {
if (state is AuthLoading) {
return const Center(child: CircularProgressIndicator());
} else {
return _buildForm(context, state);
@ -147,14 +149,14 @@ class ForgetPasswordWebPage extends StatelessWidget {
),
isDense: true,
style: const TextStyle(color: Colors.black),
items: forgetBloc.regions.map((String region) {
items:forgetBloc.regionList!.map((RegionModel region) {
return DropdownMenuItem<String>(
value: region,
child: Text(region),
value: region.id,
child: Text(region.name),
);
}).toList(),
onChanged: (String? value) {
print(value);
forgetBloc.add(SelectRegionEvent(val: value!,));
},
),
)
@ -201,8 +203,9 @@ class ForgetPasswordWebPage extends StatelessWidget {
onTap: () {
BlocProvider.of<AuthBloc>(context).add(StartTimerEvent());
},
child: Text(
'Get Code ${state is TimerState && !state.isButtonEnabled ? "(${state.remainingTime.toString()})" : ""}',
child:
Text(
'Get Code ${state is TimerState && !state.isButtonEnabled ? "(${ BlocProvider.of<AuthBloc>(context).formattedTime(state.remainingTime)})" : ""}',
style: TextStyle(
color: state is TimerState && !state.isButtonEnabled
? Colors.grey

View File

@ -6,6 +6,7 @@ import 'package:flutter_svg/svg.dart';
import 'package:syncrow_web/pages/auth/bloc/auth_bloc.dart';
import 'package:syncrow_web/pages/auth/bloc/auth_event.dart';
import 'package:syncrow_web/pages/auth/bloc/auth_state.dart';
import 'package:syncrow_web/pages/auth/model/region_model.dart';
import 'package:syncrow_web/pages/auth/view/forget_password_page.dart';
import 'package:syncrow_web/pages/common/default_button.dart';
import 'package:syncrow_web/utils/color_manager.dart';
@ -145,13 +146,13 @@ class LoginMobilePage extends StatelessWidget {
textAlign: TextAlign.center,
),
),
isDense: true,
style: const TextStyle(color: Colors.black),
items:
loginBloc.regions.map((String region) {
items:loginBloc.regionList!.map((RegionModel region) {
return DropdownMenuItem<String>(
value: region,
child: Text(region),
value: region.name,
child: Text(region.name),
);
}).toList(),
onChanged: (String? value) {
@ -300,10 +301,9 @@ class LoginMobilePage extends StatelessWidget {
.validate()) {
loginBloc.add(
LoginButtonPressed(
username:
loginBloc.loginEmailController.text,
password:
loginBloc.loginPasswordController.text,
regionUuid:'' ,
username: loginBloc.loginEmailController.text,
password: loginBloc.loginPasswordController.text,
),
);
}

View File

@ -7,6 +7,7 @@ import 'package:flutter_svg/svg.dart';
import 'package:syncrow_web/pages/auth/bloc/auth_bloc.dart';
import 'package:syncrow_web/pages/auth/bloc/auth_event.dart';
import 'package:syncrow_web/pages/auth/bloc/auth_state.dart';
import 'package:syncrow_web/pages/auth/model/region_model.dart';
import 'package:syncrow_web/pages/auth/view/forget_password_page.dart';
import 'package:syncrow_web/pages/common/default_button.dart';
import 'package:syncrow_web/pages/common/first_layer.dart';
@ -28,7 +29,7 @@ class _LoginWebPageState extends State<LoginWebPage> {
Widget build(BuildContext context) {
return Scaffold(
body: BlocProvider(
create: (BuildContext context) => AuthBloc(),
create: (BuildContext context) => AuthBloc()..add(RegionInitialEvent()),
child: BlocConsumer<AuthBloc, AuthState>(
listener: (context, state) {
if (state is LoginSuccess) {
@ -153,13 +154,16 @@ class _LoginWebPageState extends State<LoginWebPage> {
),
isDense: true,
style: const TextStyle(color: Colors.black),
items:loginBloc.regions.map((String region) {
items:loginBloc.regionList!.map((RegionModel region) {
return DropdownMenuItem<String>(
value: region,
child: Text(region),
value: region.id,
child: Text(region.name),
);
}).toList(),
onChanged: (String? value) {},
onChanged: (String? value) {
loginBloc.add(SelectRegionEvent(val: value!,));
},
),
)
],
@ -310,6 +314,7 @@ class _LoginWebPageState extends State<LoginWebPage> {
onPressed: () {
if (loginBloc.loginFormKey.currentState!.validate()) {
loginBloc.add(LoginButtonPressed(
regionUuid:loginBloc.regionUuid ,
username: loginBloc.loginEmailController.text,
password: loginBloc.loginPasswordController.text,
),

View File

@ -18,7 +18,6 @@ class DefaultButton extends StatelessWidget {
this.height,
this.padding,
});
final void Function()? onPressed;
final Widget child;
final double? height;
@ -28,15 +27,10 @@ class DefaultButton extends StatelessWidget {
final double? padding;
final bool isDone;
final bool isLoading;
final TextStyle? customTextStyle;
final ButtonStyle? customButtonStyle;
final Color? backgroundColor;
final Color? foregroundColor;
@override
Widget build(BuildContext context) {
return ElevatedButton(

View File

@ -3,7 +3,7 @@ import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
import 'package:graphview/GraphView.dart';
import 'package:syncrow_web/pages/access_management/access_management.dart';
import 'package:syncrow_web/pages/access_management/view/access_management.dart';
import 'package:syncrow_web/pages/auth/model/user_model.dart';
import 'package:syncrow_web/pages/home/bloc/home_event.dart';
import 'package:syncrow_web/pages/home/bloc/home_state.dart';
@ -38,7 +38,6 @@ class HomeBloc extends Bloc<HomeEvent, HomeState> {
..levelSeparation = (150)
..subtreeSeparation = (150)
..orientation = (BuchheimWalkerConfiguration.ORIENTATION_TOP_BOTTOM);
emit(HomeUpdateTree(graph: graph, builder: builder));
}

View File

@ -0,0 +1,36 @@
import 'dart:convert';
import 'package:flutter/services.dart';
import 'package:syncrow_web/pages/access_management/model/access_manag_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 AccessMangApi{
// Future<List<AccessManagModel>> fetchInfo() async {
// final response = await HTTPService().get(
// path: '/Users/mohammad/StudioProjects/web_auth/assets/demo.json',
// showServerMessage: true,
// expectedResponseModel: (json) {
// print('fetchInfo=$json');
// return (json as List).map((item) => AccessManagModel.fromJson(item)).toList();
// },
// );
// return response;
// }
Future<List<AccessManagModel>> fetchInfo() async {
// Load the JSON file
final jsonString = await rootBundle.loadString('assets/dome.json');
// Parse the JSON string
final List<dynamic> jsonList = json.decode(jsonString);
print('jsonList=${jsonList.runtimeType}');
print('jsonList=${jsonList}');
// Convert the list of JSON objects to a list of AccessManagModel instances
final List<AccessManagModel> accessList = jsonList.map((item) => AccessManagModel.fromJson(item)).toList();
return accessList;
}
}

View File

@ -13,7 +13,8 @@ class HTTPInterceptor extends InterceptorsWrapper {
List<String> headerExclusionListOfAddedParameters = [
ApiEndpoints.login,
ApiEndpoints.getRegion
ApiEndpoints.getRegion,
ApiEndpoints.sendOtp
];
@override

View File

@ -1,5 +1,7 @@
import 'dart:convert';
import 'package:dio/dio.dart';
import 'package:flutter/foundation.dart';
import 'package:syncrow_web/pages/auth/model/region_model.dart';
import 'package:syncrow_web/pages/auth/model/token.dart';
import 'package:syncrow_web/services/api/http_service.dart';
@ -8,6 +10,7 @@ import 'package:syncrow_web/utils/constants/api_const.dart';
class AuthenticationAPI {
static Future<Token> loginWithEmail({required var model}) async {
print('model=$model');
final response = await HTTPService().post(
path: ApiEndpoints.login,
body: model.toJson(),
@ -19,22 +22,57 @@ class AuthenticationAPI {
}
static Future forgetPassword(
{required var email, required var password}) async {
{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 sendOtp({required var email}) async {
final response = await HTTPService().post(
path: ApiEndpoints.sendOtp,
body: {"email": email, "type": "VERIFICATION"},
showServerMessage: true,
expectedResponseModel: (json) {});
return response;
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
},
showServerMessage: true,
options: Options(),
expectedResponseModel: (json) {
Map<String, dynamic> parsedJson = jsonDecode(json);
int cooldown = parsedJson['data']['cooldown'];
debugPrint('Cooldown: $cooldown seconds');
return cooldown;
}
);
return response;
} on DioError catch (e) {
if (e.response != null) {
if (e.response!.statusCode == 400) {
// Handle 400 Bad Request
final errorData = e.response!.data;
String errorMessage = errorData['message'] ?? 'Unknown error';
int cooldown = errorData['data']['cooldown'] ?? 0;
return cooldown;
} else {
debugPrint('Error: ${e.response!.statusCode} - ${e.response!.statusMessage}');
}
} else {
debugPrint('Error: ${e.message}');
}
return null;
} catch (e) {
debugPrint('Unexpected Error: $e');
return null;
}
}
static Future<bool> verifyOtp(

View File

@ -3,13 +3,11 @@ import 'package:syncrow_web/services/api/http_service.dart';
import 'package:syncrow_web/utils/constants/api_const.dart';
class HomeApi{
Future fetchUserInfo(userId) async {
final response = await HTTPService().get(
path: ApiEndpoints.getUser.replaceAll('{userUuid}', userId!),
showServerMessage: true,
expectedResponseModel: (json) {
print('user=$json');
return UserModel.fromJson(json);
}
);

View File

@ -30,4 +30,5 @@ abstract class ColorsManager {
static const Color btnColor = Color(0xFF00008B);
static const Color blueColor = Color(0xFF0036E6);
static const Color boxColor = Color(0xFFF5F6F7);
static const Color boxDivider = Color(0xFFE0E0E0);
}

View File

@ -23,4 +23,5 @@ class Assets {
static const String energyIcon = "assets/images/energy_icon.svg";
static const String integrationsIcon = "assets/images/Integrations_icon.svg";
static const String assetIcon = "assets/images/asset_icon.svg";
static const String calendarIcon = "assets/images/calendar_icon.svg";
}

View File

@ -23,4 +23,15 @@ InputDecoration? textBoxDecoration({bool suffixIcon = false}) => InputDecoration
Decoration containerDecoration = const BoxDecoration(color: Colors.white,borderRadius: BorderRadius.all(Radius.circular(20)));
Decoration containerDecoration = BoxDecoration(
boxShadow: [
BoxShadow(
color: Colors.grey.withOpacity(0.5),
spreadRadius: 5,
blurRadius: 7,
offset: Offset(0,
3), // changes position of shadow
),
],
color: ColorsManager.boxColor,
borderRadius: BorderRadius.all(Radius.circular(10)));

View File

@ -13,7 +13,7 @@ class WebAppBar extends StatelessWidget {
Widget build(BuildContext context) {
return BlocBuilder<HomeBloc, HomeState>(builder: (context, state) {
return Container(
height: 120,
height: 100,
decoration: const BoxDecoration(color: ColorsManager.secondaryColor),
padding: const EdgeInsets.all(10),
child: Expanded(

View File

@ -29,7 +29,7 @@ class WebScaffold extends StatelessWidget {
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Opacity(
opacity: 0.6,
opacity: 0.7,
child: WebAppBar(
title: appBarTitle,
body: appBarBody,

View File

@ -70,6 +70,7 @@ flutter:
# To add assets to your application, add an assets section, like this:
assets:
- assets/images/
- assets/
# An image asset can refer to one or more resolution-specific "variants", see