visitor password

This commit is contained in:
mohammad
2024-08-14 12:37:41 +03:00
parent 2d0f85bded
commit 6efcc6081d
9 changed files with 380 additions and 188 deletions

View File

@ -43,8 +43,7 @@ class MyApp extends StatelessWidget {
), ),
theme: ThemeData( theme: ThemeData(
textTheme: const TextTheme( textTheme: const TextTheme(
bodySmall: TextStyle( bodySmall: TextStyle(fontSize: 13, color: ColorsManager.whiteColors, fontWeight: FontWeight.bold),
fontSize: 13, color: ColorsManager.whiteColors, fontWeight: FontWeight.bold),
bodyMedium: TextStyle(color: Colors.black87, fontSize: 14), bodyMedium: TextStyle(color: Colors.black87, fontSize: 14),
bodyLarge: TextStyle(fontSize: 16, color: Colors.white), bodyLarge: TextStyle(fontSize: 16, color: Colors.white),
headlineSmall: TextStyle(color: Colors.black87, fontSize: 18), headlineSmall: TextStyle(color: Colors.black87, fontSize: 18),
@ -58,8 +57,8 @@ class MyApp extends StatelessWidget {
colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple), // Set up color scheme colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple), // Set up color scheme
useMaterial3: true, // Enable Material 3 useMaterial3: true, // Enable Material 3
), ),
home: VisitorPasswordDialog() // home: VisitorPasswordDialog()
// isLoggedIn == 'Success' ? const HomePage() : const LoginPage(), home:isLoggedIn == 'Success' ? const HomePage() : const LoginPage(),
)); ));
} }
} }

View File

@ -1,13 +1,12 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.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_bloc.dart';
import 'package:syncrow_web/pages/access_management/bloc/access_event.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/bloc/access_state.dart';
import 'package:syncrow_web/pages/common/date_time_widget.dart';
import 'package:syncrow_web/pages/common/default_button.dart'; import 'package:syncrow_web/pages/common/default_button.dart';
import 'package:syncrow_web/pages/visitor_password/view/visitor_password_dialog.dart'; import 'package:syncrow_web/pages/visitor_password/view/visitor_password_dialog.dart';
import 'package:syncrow_web/utils/color_manager.dart'; import 'package:syncrow_web/utils/color_manager.dart';
import 'package:syncrow_web/utils/constants/assets.dart';
import 'package:syncrow_web/utils/style.dart'; import 'package:syncrow_web/utils/style.dart';
import 'package:syncrow_web/web_layout/web_scaffold.dart'; import 'package:syncrow_web/web_layout/web_scaffold.dart';
@ -122,42 +121,19 @@ class AccessManagementPage extends StatelessWidget {
const SizedBox( const SizedBox(
width: 15, width: 15,
), ),
Column( DateTimeWebWidget(
crossAxisAlignment: CrossAxisAlignment.start, isRequired: false,
mainAxisAlignment: MainAxisAlignment.start, title: 'Access Time',
children: [ size: size,
const Text('Access Time'), endTime: () {
Container( accessBloc.add(SelectTime(context: context, isStart: false));
width: size.width * 0.25, },
padding: EdgeInsets.all(10), startTime: () {
decoration: containerDecoration, accessBloc.add(SelectTime(context: context, isStart: true));
child: Column( },
children: [ firstString:BlocProvider.of<AccessBloc>(context).startTime ,
Row( secondString:BlocProvider.of<AccessBloc>(context).endTime ,
mainAxisAlignment: ) ,
MainAxisAlignment.spaceBetween,
children: [
InkWell(
onTap: () {
accessBloc.add(SelectTime(context: context, isStart: true));
},
child: Text(BlocProvider.of<AccessBloc>(context).startTime)
),
const Icon(Icons.arrow_right_alt),
InkWell(
onTap: () {
accessBloc.add(SelectTime(context: context, isStart: false));
},
child: Text(BlocProvider.of<AccessBloc>(context).endTime)),
SvgPicture.asset(
Assets.calendarIcon,
),
],
),
],
)),
],
),
const SizedBox( const SizedBox(
width: 15, width: 15,
), ),
@ -334,6 +310,7 @@ class AccessManagementPage extends StatelessWidget {
} }
} }
Widget _buildTableHeaderCell(String title) { Widget _buildTableHeaderCell(String title) {
return Expanded( return Expanded(
child: Container( child: Container(

View File

@ -0,0 +1,67 @@
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,
required this.isRequired,
required this.textFieldName,
required this.controller,
this.description,
});
final bool isRequired;
final String textFieldName;
final String? description;
final TextEditingController? controller;
@override
Widget build(BuildContext context) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
if(isRequired)
Row(
children: [
Text(
'* ',
style: Theme.of(context)
.textTheme
.bodyMedium!
.copyWith(color: Colors.red),
),
Text(textFieldName),
],
),
Text(
description??'', // ' The password will be sent to the visitors email address.',
style: Theme.of(context)
.textTheme
.bodySmall!
.copyWith(
fontSize: 9,
fontWeight: FontWeight.w400,
color: ColorsManager.textGray),
),
],
),
const SizedBox(height: 7,),
Container(
decoration: containerDecoration,
child: TextFormField(
controller: controller,
style: const TextStyle(color: Colors.black),
decoration: textBoxDecoration()!
.copyWith(hintText: 'Please enter'),
),
),
],
);
}
}

View File

@ -0,0 +1,74 @@
import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:syncrow_web/utils/constants/assets.dart';
import 'package:syncrow_web/utils/style.dart';
class DateTimeWebWidget extends StatelessWidget {
const DateTimeWebWidget({
super.key,
required this.size,
required this.isRequired,
required this.title,
required this.startTime,
required this.endTime,
required this.firstString,
required this.secondString,
});
final Size size;
final String title;
final bool isRequired;
final String firstString;
final String secondString;
final Function()? startTime;
final Function()? endTime;
@override
Widget build(BuildContext context) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.start,
children: [
Row(
children: [
if(isRequired)
Text(
'* ',
style: Theme.of(context)
.textTheme
.bodyMedium!
.copyWith(color: Colors.red),
),
Text(title??''),
],
),
SizedBox(height: 8,),
Container(
width: size.width * 0.25,
padding: EdgeInsets.all(10),
decoration: containerDecoration,
child: Column(
children: [
Row(
mainAxisAlignment:
MainAxisAlignment.spaceBetween,
children: [
InkWell(
onTap: startTime,
child: Text(firstString)
),
const Icon(Icons.arrow_right_alt),
InkWell(
onTap:endTime,
child: Text(secondString)),
SvgPicture.asset(
Assets.calendarIcon,
),
],
),
],
)),
],
);
}
}

View File

@ -1,13 +1,108 @@
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:syncrow_web/pages/visitor_password/bloc/visitor_password_event.dart'; import 'package:syncrow_web/pages/visitor_password/bloc/visitor_password_event.dart';
import 'package:syncrow_web/pages/visitor_password/bloc/visitor_password_state.dart'; import 'package:syncrow_web/pages/visitor_password/bloc/visitor_password_state.dart';
import 'package:syncrow_web/utils/color_manager.dart';
import 'package:syncrow_web/utils/snack_bar.dart';
// Define the BLoC // Define the BLoC
class VisitorPasswordBloc extends Bloc<VisitorPasswordEvent, VisitorPasswordState> { class VisitorPasswordBloc extends Bloc<VisitorPasswordEvent, VisitorPasswordState> {
VisitorPasswordBloc() : super(VisitorPasswordInitial()) { VisitorPasswordBloc() : super(VisitorPasswordInitial()) {
on<SelectPasswordType>((event, emit) { on<SelectUsageFrequency>(selectUsageFrequency);
// Handle the event and emit the new state on<SelectPasswordType>(selectAccessType);
emit(PasswordTypeSelected(event.type)); on<SelectTimeVisitorPassword>(selectTimeVisitorPassword);
});
} }
final TextEditingController userNameController = TextEditingController();
final TextEditingController emailController = TextEditingController();
String accessTypeSelected='';
String usageFrequencySelected='';
int? effectiveTimeTimeStamp;
int? expirationTimeTimeStamp;
String startTime = 'Start Time';
String endTime = 'End Time';
selectAccessType(SelectPasswordType event, Emitter<VisitorPasswordState> emit) {
accessTypeSelected=event.type;
print(accessTypeSelected);
emit(PasswordTypeSelected(event.type));
}
selectUsageFrequency(SelectUsageFrequency event, Emitter<VisitorPasswordState> emit) {
usageFrequencySelected=event.usageType;
print(usageFrequencySelected);
emit(UsageFrequencySelected(event.usageType));
}
Future<void> selectTimeVisitorPassword(SelectTimeVisitorPassword event, Emitter<VisitorPasswordState> emit) async {
final DateTime? picked = await showDatePicker(
context: event.context,
initialDate: DateTime.now(),
firstDate: DateTime(2015, 8),
lastDate: DateTime(2101),
);
if (picked != null) {
final TimeOfDay? timePicked = await showTimePicker(
context: event.context,
initialTime: TimeOfDay.now(),
builder: (context, child) {
return Theme(
data: ThemeData.light().copyWith(
colorScheme: const ColorScheme.light(
primary: ColorsManager.primaryColor,
onSurface: Colors.black,
),
buttonTheme: const ButtonThemeData(
colorScheme: ColorScheme.light(
primary: Colors.green,
),
),
),
child: child!,
);
},
);
if (timePicked != null) {
final selectedDateTime = DateTime(
picked.year,
picked.month,
picked.day,
timePicked.hour,
timePicked.minute,
);
final selectedTimestamp = DateTime(
selectedDateTime.year,
selectedDateTime.month,
selectedDateTime.day,
selectedDateTime.hour,
selectedDateTime.minute,
).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.');
} else {
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.');
} else {
endTime = selectedDateTime.toString().split('.').first; // Remove seconds and milliseconds
expirationTimeTimeStamp = selectedTimestamp;
}
}
}
}
// emit(AccessInitial());
// emit(TableLoaded(data));
}
} }

View File

@ -1,13 +1,8 @@
import 'package:equatable/equatable.dart'; import 'package:equatable/equatable.dart';
import 'package:flutter/cupertino.dart';
abstract class VisitorPasswordEvent extends Equatable { abstract class VisitorPasswordEvent extends Equatable {
const VisitorPasswordEvent( const VisitorPasswordEvent();
);
@override @override
List<Object> get props => []; List<Object> get props => [];
@ -21,3 +16,21 @@ class SelectPasswordType extends VisitorPasswordEvent {
@override @override
List<Object> get props => [type]; List<Object> get props => [type];
} }
class SelectUsageFrequency extends VisitorPasswordEvent {
final String usageType;
const SelectUsageFrequency(this.usageType);
@override
List<Object> get props => [usageType];
}
class SelectTimeVisitorPassword extends VisitorPasswordEvent {
final BuildContext context;
final bool isStart;
const SelectTimeVisitorPassword({ required this.context,required this.isStart});
@override
List<Object> get props => [context,isStart];
}

View File

@ -16,8 +16,17 @@ class VisitorPasswordInitial extends VisitorPasswordState {}
class PasswordTypeSelected extends VisitorPasswordState { class PasswordTypeSelected extends VisitorPasswordState {
final String selectedType; final String selectedType;
PasswordTypeSelected(this.selectedType); const PasswordTypeSelected(this.selectedType);
@override @override
List<Object> get props => [selectedType]; List<Object> get props => [selectedType];
}
class UsageFrequencySelected extends VisitorPasswordState {
final String selectedFrequency;
const UsageFrequencySelected(this.selectedFrequency);
@override
List<Object> get props => [selectedFrequency];
} }

View File

@ -1,86 +1,54 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:syncrow_web/pages/common/custom_web_textfield.dart';
import 'package:syncrow_web/pages/common/date_time_widget.dart';
import 'package:syncrow_web/pages/common/default_button.dart';
import 'package:syncrow_web/pages/visitor_password/bloc/visitor_password_bloc.dart'; import 'package:syncrow_web/pages/visitor_password/bloc/visitor_password_bloc.dart';
import 'package:syncrow_web/pages/visitor_password/bloc/visitor_password_event.dart'; import 'package:syncrow_web/pages/visitor_password/bloc/visitor_password_event.dart';
import 'package:syncrow_web/pages/visitor_password/bloc/visitor_password_state.dart'; import 'package:syncrow_web/pages/visitor_password/bloc/visitor_password_state.dart';
import 'package:syncrow_web/utils/color_manager.dart';
import 'package:syncrow_web/utils/style.dart'; import 'package:syncrow_web/utils/style.dart';
class VisitorPasswordDialog extends StatelessWidget { class VisitorPasswordDialog extends StatelessWidget {
const VisitorPasswordDialog({super.key}); const VisitorPasswordDialog({super.key});
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
Size size = MediaQuery.of(context).size; Size size = MediaQuery.of(context).size;
return BlocProvider( return BlocProvider(
create: (context) => VisitorPasswordBloc(), create: (context) => VisitorPasswordBloc(),
child: BlocBuilder<VisitorPasswordBloc, VisitorPasswordState>( child: BlocBuilder<VisitorPasswordBloc, VisitorPasswordState>(
builder: (context, state) { builder: (context, state) {
final visitorBloc = BlocProvider.of<VisitorPasswordBloc>(context);
return AlertDialog( return AlertDialog(
title: const Text('Create visitor password'), title: const Text('Create visitor password'),
content: SingleChildScrollView( content: SingleChildScrollView(
child: ListBody( child: ListBody(
children: <Widget>[ children: <Widget>[
Row( Row(
children: [ children: [
Column( Expanded(
crossAxisAlignment: CrossAxisAlignment.start, flex: 2,
mainAxisAlignment: MainAxisAlignment.start, child: CustomWebTextField(
children: [ controller:visitorBloc.userNameController ,
Row( isRequired: true,
children: [ textFieldName: 'User Name',
Text( description: '',
'* ', ),
style: Theme.of(context)
.textTheme
.bodyMedium!
.copyWith(color: Colors.red),
),
const Text('User Name'),
],
),
Container(
width: size.width * 0.15,
decoration: containerDecoration,
child: TextFormField(
style: TextStyle(color: Colors.black),
decoration: textBoxDecoration()!
.copyWith(hintText: 'Please enter'),
),
),
],
), ),
SizedBox(width: size.width * 0.05), // Add spacing between columns const Spacer(),
Column( Expanded(
crossAxisAlignment: CrossAxisAlignment.start, flex: 2,
mainAxisAlignment: MainAxisAlignment.start, child: CustomWebTextField(
children: [ controller:visitorBloc.emailController ,
Row( isRequired: true,
children: [ textFieldName: 'Email Address',
Text( description: 'The password will be sent to the visitors email address.',
'* ', ),
style: Theme.of(context)
.textTheme
.bodyMedium!
.copyWith(color: Colors.red),
),
const Text('Email Address'),
],
),
Container(
width: size.width * 0.15,
decoration: containerDecoration,
child: TextFormField(
style: TextStyle(color: Colors.black),
decoration: textBoxDecoration()!
.copyWith(hintText: 'Please enter'),
),
),
],
), ),
], ],
), ),
SizedBox(height: size.height * 0.02), // Add spacing SizedBox(height: size.height * 0.02), // Add spacing
Column( Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
@ -101,25 +69,22 @@ class VisitorPasswordDialog extends StatelessWidget {
Row( Row(
children: <Widget>[ children: <Widget>[
SizedBox( SizedBox(
width: 200, width: size.width*0.15,
child: RadioListTile<String>( child: RadioListTile<String>(
title: Text('Offline Password'), title: const Text('Offline Password'),
value: 'Offline Password', value: 'Offline Password',
groupValue: (state is PasswordTypeSelected) groupValue: (state is PasswordTypeSelected)
? state.selectedType ? state.selectedType
: 'Offline Password', : 'Offline Password',
onChanged: (String? value) { onChanged: (String? value) {
if (value != null) { if (value != null) {
context context.read<VisitorPasswordBloc>().add(SelectPasswordType(value));
.read<VisitorPasswordBloc>()
.add(SelectPasswordType(value));
} }
}, },
), ),
), ),
SizedBox( SizedBox(
width: 200, width: size.width*0.15,
child: RadioListTile<String>( child: RadioListTile<String>(
title: Text('Online Password'), title: Text('Online Password'),
value: 'Online Password', value: 'Online Password',
@ -136,10 +101,9 @@ class VisitorPasswordDialog extends StatelessWidget {
), ),
), ),
SizedBox( SizedBox(
width: 200, width: size.width*0.15,
child: RadioListTile<String>( child: RadioListTile<String>(
title: Text('Dynamic Password'), title: const Text('Dynamic Password'),
value: 'Dynamic Password', value: 'Dynamic Password',
groupValue: (state is PasswordTypeSelected) groupValue: (state is PasswordTypeSelected)
? state.selectedType ? state.selectedType
@ -180,32 +144,29 @@ class VisitorPasswordDialog extends StatelessWidget {
child: RadioListTile<String>( child: RadioListTile<String>(
title: const Text('One-Time'), title: const Text('One-Time'),
value: 'One-Time', value: 'One-Time',
groupValue: (state is PasswordTypeSelected) groupValue: (state is UsageFrequencySelected)
? state.selectedType ? state.selectedFrequency
: 'One-Time', : 'One-Time',
onChanged: (String? value) { onChanged: (String? value) {
if (value != null) { if (value != null) {
context context.read<VisitorPasswordBloc>()
.read<VisitorPasswordBloc>() .add(SelectUsageFrequency(value));
.add(SelectPasswordType(value));
} }
}, },
), ),
), ),
SizedBox( SizedBox(
width: 200, width: 200,
child: RadioListTile<String>( child: RadioListTile<String>(
title: Text('Periodic'), title: const Text('Periodic'),
value: 'Periodic', value: 'Periodic',
groupValue: (state is PasswordTypeSelected) groupValue: (state is UsageFrequencySelected)
? state.selectedType ? state.selectedFrequency
: 'Periodic', : 'Periodic1',
onChanged: (String? value) { onChanged: (String? value) {
if (value != null) { if (value != null) {
context context.read<VisitorPasswordBloc>()
.read<VisitorPasswordBloc>() .add(SelectUsageFrequency(value));
.add(SelectPasswordType(value));
} }
}, },
), ),
@ -214,73 +175,69 @@ class VisitorPasswordDialog extends StatelessWidget {
) )
], ],
), ),
DateTimeWebWidget(
isRequired: true,
title: 'Access Period',
size: size,
endTime: () {
visitorBloc.add(SelectTimeVisitorPassword(context: context, isStart: false));
},
startTime: () {
visitorBloc.add(SelectTimeVisitorPassword(context: context, isStart: true));
},
firstString:BlocProvider.of<VisitorPasswordBloc>(context).startTime ,
secondString:BlocProvider.of<VisitorPasswordBloc>(context).endTime ,
) ,
Column( Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Row( Row(
children: [ children: [
Text( Text(
'* ', '* ',
style: Theme.of(context) style: Theme.of(context)
.textTheme .textTheme
.bodyMedium! .bodyMedium!
.copyWith(color: Colors.red), .copyWith(color: Colors.red),
), ),
const Text('Access Period'), const Text('Access Devices'),
], ],
), ),
Row( const Text('Within the validity period, each device can be unlocked only once.'),
children: <Widget>[ Container(
SizedBox( decoration: containerDecoration,
width: 200, width: size.width*0.2,
child: RadioListTile<String>( child: const DefaultButton(
title: const Text('One-Time'), borderRadius: 8,
value: 'One-Time', child:Text('+ Add Device'),)),
groupValue: (state is PasswordTypeSelected)
? state.selectedType
: 'One-Time',
onChanged: (String? value) {
if (value != null) {
context
.read<VisitorPasswordBloc>()
.add(SelectPasswordType(value));
}
},
),
),
SizedBox(
width: 200,
child: RadioListTile<String>(
title: Text('Periodic'),
value: 'Periodic',
groupValue: (state is PasswordTypeSelected)
? state.selectedType
: 'Periodic',
onChanged: (String? value) {
if (value != null) {
context
.read<VisitorPasswordBloc>()
.add(SelectPasswordType(value));
}
},
),
),
],
)
], ],
), )
], ],
), ),
), ),
actionsAlignment: MainAxisAlignment.center,
actions: <Widget>[ actions: <Widget>[
TextButton( Container(
child: const Text('Approve'), decoration: containerDecoration,
onPressed: () { width: size.width*0.2,
Navigator.of(context).pop(); child: DefaultButton(
}, borderRadius: 8,
), onPressed:() {},
backgroundColor: Colors.white,
child:Text('Cancel',style: Theme.of(context)
.textTheme
.bodyMedium!,),)),
Container(
decoration: containerDecoration,
width: size.width*0.2,
child: const DefaultButton(
borderRadius: 8,
child:Text('Ok'),)),
], ],
); );
}, },
@ -288,3 +245,4 @@ class VisitorPasswordDialog extends StatelessWidget {
); );
} }
} }

View File

@ -28,7 +28,7 @@ Decoration containerDecoration = BoxDecoration(
BoxShadow( BoxShadow(
color: Colors.grey.withOpacity(0.5), color: Colors.grey.withOpacity(0.5),
spreadRadius: 5, spreadRadius: 5,
blurRadius: 7, blurRadius: 8,
offset: Offset(0, offset: Offset(0,
3), // changes position of shadow 3), // changes position of shadow
), ),