create visitor password

This commit is contained in:
mohammad
2024-08-18 17:08:36 +03:00
parent e610f7335d
commit 869a10f92c
21 changed files with 932 additions and 368 deletions

View File

@ -0,0 +1,5 @@
<svg width="12" height="12" viewBox="0 0 12 12" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M5.64844 2.51953H6.35149V6.7031H5.64844V2.51953Z" fill="#FFC800"/>
<path d="M5.64844 8.82422H6.35149V9.52734H5.64844V8.82422Z" fill="#FFC800"/>
<path d="M10.2427 1.75734C9.10945 0.624141 7.60266 0 6 0C4.39734 0 2.89055 0.624141 1.75734 1.75734C0.624141 2.89055 0 4.39734 0 6C0 7.60266 0.624141 9.10945 1.75734 10.2427C2.89055 11.3759 4.39734 12 6 12C7.60266 12 9.10945 11.3759 10.2427 10.2427C11.3759 9.10945 12 7.60266 12 6C12 4.39734 11.3759 2.89055 10.2427 1.75734ZM7.05469 10.2305H4.94531V8.12109H7.05469V10.2305ZM7.05469 7.40625H4.94531V1.81641H7.05469V7.40625Z" fill="#FFC800"/>
</svg>

After

Width:  |  Height:  |  Size: 697 B

View File

@ -5,6 +5,7 @@ import 'package:syncrow_web/pages/auth/bloc/auth_bloc.dart';
import 'package:syncrow_web/pages/auth/view/login_page.dart';
import 'package:syncrow_web/pages/home/bloc/home_bloc.dart';
import 'package:syncrow_web/pages/home/view/home_page.dart';
import 'package:syncrow_web/pages/visitor_password/view/add_device_dialog.dart';
import 'package:syncrow_web/pages/visitor_password/view/visitor_password_dialog.dart';
import 'package:syncrow_web/services/locator.dart';
import 'package:syncrow_web/utils/color_manager.dart';
@ -40,6 +41,7 @@ class MyApp extends StatelessWidget {
PointerDeviceKind.unknown,
},
),
theme: ThemeData(
textTheme: const TextTheme(
bodySmall: TextStyle(fontSize: 13, color: ColorsManager.whiteColors, fontWeight: FontWeight.bold),
@ -53,10 +55,11 @@ class MyApp extends StatelessWidget {
fontWeight: FontWeight.bold,
),
),
colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple), // Set up color scheme
useMaterial3: true, // Enable Material 3
),
// home: VisitorPasswordDialog()
// home: AddDeviceDialog()
home:isLoggedIn == 'Success' ? const HomePage() : const LoginPage(),
));
}

View File

@ -171,11 +171,10 @@ class AccessBloc extends Bloc<AccessEvent, AccessState> {
}
DateTime timestampToDateTime(dynamic timestamp) {
return DateTime.fromMillisecondsSinceEpoch(int.parse(timestamp) * 1000);
String timestampToDate(dynamic timestamp) {
DateTime dateTime = DateTime.fromMillisecondsSinceEpoch(int.parse(timestamp) * 1000);
return "${dateTime.year}/${dateTime.month.toString().padLeft(2, '0')}/${dateTime.day.toString().padLeft(2, '0')}";
}
}

View File

@ -1,3 +1,5 @@
import 'package:syncrow_web/utils/constants/const.dart';
class PasswordModel {
final dynamic passwordId;
final dynamic invalidTime;
@ -6,7 +8,7 @@ class PasswordModel {
final dynamic createdTime;
final dynamic passwodName; // New field
final dynamic passwordStatus;
final dynamic passwordType;
final AccessType passwordType;
final dynamic deviceUuid;
PasswordModel({
@ -17,7 +19,7 @@ class PasswordModel {
this.createdTime,
this.passwodName, // New field
this.passwordStatus,
this.passwordType,
required this.passwordType,
this.deviceUuid,
});
@ -30,7 +32,7 @@ class PasswordModel {
createdTime: json['createdTime'],
passwodName: json['passwodName']??'No name', // New field
passwordStatus: json['passwordStatus'],
passwordType: json['passwordType'],
passwordType:AccessTypeExtension.fromString(json['passwordType']) ,
deviceUuid: json['deviceUuid'],
);
}

View File

@ -3,10 +3,12 @@ import 'package:flutter_bloc/flutter_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_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/default_button.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/const.dart';
import 'package:syncrow_web/utils/style.dart';
import 'package:syncrow_web/web_layout/web_scaffold.dart';
@ -51,68 +53,69 @@ class AccessManagementPage extends StatelessWidget {
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
height: size.height * 0.05,
width:size.width * 0.26 ,
decoration: containerDecoration,
child: Center(
child: ListView.builder(
scrollDirection: Axis.horizontal,
itemCount: BlocProvider.of<AccessBloc>(context).tabs.length,
itemBuilder: (context, index) {
final isSelected = index ==
BlocProvider.of<AccessBloc>(context).selectedIndex;
return InkWell(
onTap: () {
BlocProvider.of<AccessBloc>(context).add(TabChangedEvent(index));
},
child: Container(
decoration: BoxDecoration(
color: ColorsManager.boxColor,
border: Border.all(
color: isSelected ? Colors.blue : Colors.transparent,
width: 2.0,),
borderRadius: index == 0
? const BorderRadius.only(
topLeft: Radius.circular(10),
bottomLeft: Radius.circular(10))
: index == 3
? const BorderRadius.only(
topRight: Radius.circular(10),
bottomRight: Radius.circular(10))
: null),
padding: const EdgeInsets.only(left: 10,right: 10),
child: Center(
child: Text(
BlocProvider.of<AccessBloc>(context).tabs[index],
style: TextStyle(
color: isSelected
? Colors.blue
: Colors.black,
height: size.height * 0.05,
child: Flexible(
child: ListView.builder(
scrollDirection: Axis.horizontal,
itemCount: BlocProvider.of<AccessBloc>(context).tabs.length,
shrinkWrap: true,
itemBuilder: (context, index) {
final isSelected = index ==
BlocProvider.of<AccessBloc>(context).selectedIndex;
return InkWell(
onTap: () {
BlocProvider.of<AccessBloc>(context).add(TabChangedEvent(index));
},
child: Container(
decoration: BoxDecoration(
color: ColorsManager.boxColor,
border: Border.all(
color: isSelected ? Colors.blue : Colors.transparent,
width: 2.0,),
borderRadius: index == 0
? const BorderRadius.only(
topLeft: Radius.circular(10),
bottomLeft: Radius.circular(10))
: index == 3
? const BorderRadius.only(
topRight: Radius.circular(10),
bottomRight: Radius.circular(10))
: null),
padding: const EdgeInsets.only(left: 10,right: 10),
child: Center(
child: Text(
BlocProvider.of<AccessBloc>(context).tabs[index],
style: TextStyle(
color: isSelected
? Colors.blue
: Colors.black,
),
),
),
),
),
);
},
);
},
),
),
),
),
const SizedBox(
height: 20,
),
Row(
children: [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.start,
children: [
const Text('Password Name'),
const Text('Name'),
Container(
width: size.width * 0.15,
decoration: containerDecoration,
child: TextFormField(
controller: accessBloc.passwordName,
style: TextStyle(color: Colors.black),
style: const TextStyle(color: Colors.black),
decoration: textBoxDecoration()!
.copyWith(hintText: 'Please enter'),
)),
@ -206,7 +209,7 @@ class AccessManagementPage extends StatelessWidget {
);
},
borderRadius: 8,
child: Text('+ Create Visitor Password ')),
child: const Text('+ Create Visitor Password ')),
),
const SizedBox(
width: 10,
@ -230,120 +233,110 @@ class AccessManagementPage extends StatelessWidget {
height: 20,
),
Expanded(
child: state is TableLoaded
? TableWidget(size, state,accessBloc)
: const Center(child: CircularProgressIndicator()))
child: state is TableLoaded
? DynamicTable(
size: size,
cellDecoration: containerDecoration,
headers: const [
'Name',
'Access Type',
'Access Period',
'Device Id',
'Authorizer',
'Authorization Date & Time',
'Access Status'
],
data: state.data.map((item) {
return [
item.passwodName.toString(),
item.passwordType.value,
('${accessBloc.timestampToDate(item.effectiveTime)} - ${accessBloc.timestampToDate(item.invalidTime)}'),
item.deviceUuid.toString(),
'',
'',
''
];
}).toList(),
)
: const Center(child: CircularProgressIndicator()),
)
],
),
);
})));
}
Container TableWidget(Size size, TableLoaded state,AccessBloc accessBloc) {
return 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('Password name'),
_buildTableHeaderCell(' Password Type'),
_buildTableHeaderCell('Start Time'),
_buildTableHeaderCell('End Time'),
_buildTableHeaderCell('Device Id'),
// _buildTableHeaderCell('Authorization Source'),
// _buildTableHeaderCell('Authorizer'),
_buildTableHeaderCell('Password Created'),
// _buildTableHeaderCell('Access Status'),
_buildTableHeaderCell('Password Status'),
],
),
),
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.passwodName),
_buildTableCell(item.passwordType),
_buildTableCell(accessBloc.timestampToDateTime(item.effectiveTime).toString()),
_buildTableCell(accessBloc.timestampToDateTime(item.invalidTime).toString()),
_buildTableCell(item.deviceUuid.toString()),
// _buildTableCell(item.authorizationSource),
// _buildTableCell(item.authorizer),
_buildTableCell(item.passwordCreated!=null?accessBloc.timestampToDateTime(item.passwordCreated).toString():'no data'),
// _buildTableCell(item.accessStatus),
_buildTableCell(item.passwordStatus.toString()),
],
);
}).toList(),
),
],
),
),
),
],
),
),
],
),
),
);
}
// Container TableWidget(Size size, TableLoaded state,AccessBloc accessBloc) {
// return 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('Password name'),
// _buildTableHeaderCell(' Password Type'),
// _buildTableHeaderCell('Start Time'),
// _buildTableHeaderCell('End Time'),
// _buildTableHeaderCell('Device Id'),
// // _buildTableHeaderCell('Authorization Source'),
// // _buildTableHeaderCell('Authorizer'),
// _buildTableHeaderCell('Password Created'),
// // _buildTableHeaderCell('Access Status'),
// _buildTableHeaderCell('Password Status'),
// ],
// ),
// ),
// 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.passwodName),
// _buildTableCell(item.passwordType),
//
// _buildTableCell(accessBloc.timestampToDateTime(item.effectiveTime).toString()),
// _buildTableCell(accessBloc.timestampToDateTime(item.invalidTime).toString()),
// _buildTableCell(item.deviceUuid.toString()),
// // _buildTableCell(item.authorizationSource),
// // _buildTableCell(item.authorizer),
// _buildTableCell(item.passwordCreated!=null?accessBloc.timestampToDateTime(item.passwordCreated).toString():'no data'),
// // _buildTableCell(item.accessStatus),
// _buildTableCell(item.passwordStatus.toString()),
// ],
// );
// }).toList(),
// ),
// ],
// ),
// ),
// ),
// ],
// ),
// ),
// ],
// ),
// ),
// );
// }
}
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(
height: 80,
padding: const EdgeInsets.all(20.0),
decoration: BoxDecoration(
border: Border(
bottom: BorderSide( // <--- right side
color: ColorsManager.boxDivider,
width: 1.0,
),
)
),
alignment: Alignment.centerLeft,
child: Text(
content,
style: TextStyle(color: Colors.black, fontSize: 12),
),
),
);
}

View File

@ -0,0 +1,103 @@
import 'package:flutter/material.dart';
import 'package:syncrow_web/utils/color_manager.dart';
class DynamicTable extends StatelessWidget {
final List<String> headers;
final List<List<dynamic>> data;
final BoxDecoration? headerDecoration;
final BoxDecoration? cellDecoration;
final Size size;
const DynamicTable({
Key? key,
required this.headers,
required this.data,
required this.size,
this.headerDecoration,
this.cellDecoration,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return Container(
decoration: cellDecoration,
child: Padding(
padding: const EdgeInsets.all(10.0),
child: ListView(
scrollDirection: Axis.horizontal,
children: [
Container(
width:size.width,
child: Column(
children: [
Container(
decoration: headerDecoration ??
BoxDecoration(color: Colors.grey[200]),
child: Row(
children: headers.map((header) =>
_buildTableHeaderCell(header)).toList(),
),
),
Expanded(
child: Container(
color: Colors.white,
child: ListView(
shrinkWrap: true,
children: data.map((row) {
return Row(
children: row.map((cell) =>
_buildTableCell(cell.toString())).toList(),
);
}).toList(),
),
),
),
],
),
),
],
),
),
);
}
}
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(
height: 80,
padding: const EdgeInsets.all(20.0),
decoration: const BoxDecoration(
border: Border(
bottom: BorderSide( // <--- right side
color: ColorsManager.boxDivider,
width: 1.0,
),
)
),
alignment: Alignment.centerLeft,
child: Text(
content,
style: TextStyle(color: Colors.black, fontSize: 12),
),
),
);
}

View File

@ -29,8 +29,7 @@ class CustomWebTextField extends StatelessWidget {
if(isRequired)
Row(
children: [
Text(
'* ',
Text('* ',
style: Theme.of(context)
.textTheme
.bodyMedium!
@ -39,6 +38,7 @@ class CustomWebTextField extends StatelessWidget {
Text(textFieldName),
],
),
const SizedBox(width: 10,),
Text(
description??'', // ' The password will be sent to the visitors email address.',
style: Theme.of(context)
@ -53,7 +53,17 @@ class CustomWebTextField extends StatelessWidget {
),
const SizedBox(height: 7,),
Container(
decoration: containerDecoration,
height: MediaQuery.of(context).size.height*0.05,
decoration: containerDecoration.copyWith(
color: const Color(0xFFF5F6F7),
boxShadow: [
BoxShadow(
color: Colors.grey.withOpacity(0.3),
spreadRadius:2,
blurRadius: 3,
offset: Offset(1, 1), // changes position of shadow
), ]
),
child: TextFormField(
controller: controller,
style: const TextStyle(color: Colors.black),

View File

@ -2,6 +2,8 @@ import 'package:flutter/material.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_state.dart';
import 'package:syncrow_web/pages/visitor_password/model/device_model.dart';
import 'package:syncrow_web/services/access_mang_api.dart';
import 'package:syncrow_web/utils/color_manager.dart';
import 'package:syncrow_web/utils/snack_bar.dart';
@ -9,15 +11,29 @@ import 'package:syncrow_web/utils/snack_bar.dart';
class VisitorPasswordBloc extends Bloc<VisitorPasswordEvent, VisitorPasswordState> {
VisitorPasswordBloc() : super(VisitorPasswordInitial()) {
on<SelectUsageFrequency>(selectUsageFrequency);
on<FetchDevice>(_onFetchDevice);
on<SelectPasswordType>(selectAccessType);
on<SelectTimeVisitorPassword>(selectTimeVisitorPassword);
on<ToggleRepeatEvent>(toggleRepeat);
on<ToggleDaySelectionEvent>(toggleDaySelection);
}
final TextEditingController userNameController = TextEditingController();
final TextEditingController emailController = TextEditingController();
final TextEditingController deviceNameController = TextEditingController();
final TextEditingController deviceIdController = TextEditingController();
final TextEditingController unitNameController = TextEditingController();
final TextEditingController virtualAddressController = TextEditingController();
List<DeviceModel> data=[];
String accessTypeSelected='Offline Password';
String usageFrequencySelected='One-Time';
@ -136,6 +152,17 @@ class VisitorPasswordBloc extends Bloc<VisitorPasswordEvent, VisitorPasswordStat
}
emit(ChangeTimeState());
}
//Add Accessible Device
Future<void> _onFetchDevice(
FetchDevice event, Emitter<VisitorPasswordState> emit) async {
try {
emit(DeviceLoaded());
data = await AccessMangApi().fetchDevices();
emit(TableLoaded(data));
} catch (e) {
emit(FailedState(e.toString()));
}
}
}

View File

@ -47,3 +47,4 @@ class ToggleDaySelectionEvent extends VisitorPasswordEvent {
class ToggleRepeatEvent extends VisitorPasswordEvent {}
class FetchDevice extends VisitorPasswordEvent {}

View File

@ -1,6 +1,7 @@
import 'package:equatable/equatable.dart';
import 'package:syncrow_web/pages/visitor_password/model/device_model.dart';
abstract class VisitorPasswordState extends Equatable {
const VisitorPasswordState();
@ -42,3 +43,20 @@ class IsRepeatState extends VisitorPasswordState {
class LoadingInitialState extends VisitorPasswordState {}
class ChangeTimeState extends VisitorPasswordState {}
class DeviceLoaded extends VisitorPasswordState {}
class FailedState extends VisitorPasswordState {
final String message;
FailedState(this.message);
@override
List<Object> get props => [message];
}
class TableLoaded extends VisitorPasswordState {
final List<DeviceModel> data;
const TableLoaded(this.data);
@override
List<Object> get props => [data];
}

View File

@ -0,0 +1,99 @@
class DeviceModel {
dynamic productUuid;
dynamic productType;
dynamic activeTime;
dynamic category;
dynamic categoryName;
dynamic createTime;
dynamic gatewayId;
dynamic icon;
dynamic ip;
dynamic lat;
dynamic localKey;
dynamic lon;
dynamic model;
dynamic name;
dynamic online;
dynamic ownerId;
dynamic sub;
dynamic timeZone;
dynamic updateTime;
dynamic uuid;
DeviceModel({
required this.productUuid,
required this.productType,
required this.activeTime,
required this.category,
required this.categoryName,
required this.createTime,
required this.gatewayId,
required this.icon,
required this.ip,
required this.lat,
required this.localKey,
required this.lon,
required this.model,
required this.name,
required this.online,
required this.ownerId,
required this.sub,
required this.timeZone,
required this.updateTime,
required this.uuid,
});
// Deserialize from JSON
factory DeviceModel.fromJson(Map<String, dynamic> json) {
return DeviceModel(
productUuid: json['productUuid'] ,
productType: json['productType'],
activeTime: json['activeTime'],
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'] ,
name: json['name'],
online: json['online'],
ownerId: json['ownerId'] ,
sub: json['sub'],
timeZone: json['timeZone'],
updateTime: json['updateTime'] ,
uuid: json['uuid'],
);
}
// Serialize to JSON
Map<String, dynamic> toJson() {
return {
'productUuid': productUuid,
'productType': productType,
'activeTime': activeTime,
'category': category,
'categoryName': categoryName,
'createTime': createTime,
'gatewayId': gatewayId,
'icon': icon,
'ip': ip,
'lat': lat,
'localKey': localKey,
'lon': lon,
'model': model,
'name': name,
'online': online,
'ownerId': ownerId,
'sub': sub,
'timeZone': timeZone,
'updateTime': updateTime,
'uuid': uuid,
};
}
}

View File

@ -0,0 +1,210 @@
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:syncrow_web/pages/common/custom_web_textfield.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_event.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/constants/assets.dart';
import 'package:syncrow_web/utils/style.dart';
import '../../common/custom_table.dart';
class AddDeviceDialog extends StatelessWidget {
const AddDeviceDialog({super.key});
@override
Widget build(BuildContext context) {
Size size = MediaQuery.of(context).size;
return BlocProvider(
create: (context) => VisitorPasswordBloc()..add(FetchDevice()),
child: BlocBuilder<VisitorPasswordBloc, VisitorPasswordState>(
builder: (BuildContext context, VisitorPasswordState state) {
final visitorBloc = BlocProvider.of<VisitorPasswordBloc>(context);
return AlertDialog(
backgroundColor: Colors.white,
title: const Text('Add Accessible Device'),
content: Container(
height: MediaQuery.of(context).size.height/1.7,
width: MediaQuery.of(context).size.width/2,
child: Padding(
padding: const EdgeInsets.all(10.0),
child: Column(
children: <Widget>[
Container(
width: size.width,
padding: EdgeInsets.all(15),
decoration:containerDecoration.copyWith(
color: ColorsManager.worningColor,
border: Border.all(color: Color(0xffFFD22F)),
boxShadow: []
),
child: Row(
children: [
SizedBox(
child: SvgPicture.asset(
Assets.deviceNoteIcon,
height: 15,
width: 15,
),
),
SizedBox(width: 10,),
Text('Only online accessible devices can be added'),
],
)
),
SizedBox(height: 20,),
Row(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Expanded(
flex: 2,
child: CustomWebTextField(
controller: visitorBloc.deviceNameController,
isRequired: true,
textFieldName: 'Device Name',
description: '',
),
),
const SizedBox(width: 10),
Expanded(
flex: 2,
child: CustomWebTextField(
controller: visitorBloc.deviceNameController,
isRequired: true,
textFieldName: 'Device ID',
description: '',
),
),
const SizedBox(width: 10),
Expanded(
flex: 2,
child: CustomWebTextField(
controller: visitorBloc.unitNameController,
isRequired: true,
textFieldName: 'Unit Name',
description: '',
),
),
const SizedBox(width: 10),
Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
const SizedBox(height: 25),
Center(
child: Container(
height: 43,
width: 100,
decoration: containerDecoration,
child: Center(
child: DefaultButton(
onPressed: () {
// Your search function here
},
borderRadius: 9,
child: const Text('Search'),
),
),
),
),
],
),
const SizedBox(width: 10),
Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
const SizedBox(height: 25),
Center(
child: Container(
height: 43,
width: 100,
decoration: containerDecoration,
child: Center(
child: DefaultButton(
backgroundColor: ColorsManager.whiteColors,
borderRadius: 9,
child: Text(
'Reset',
style: Theme.of(context).textTheme.bodySmall!.copyWith(color: Colors.black),
),
),
),
),
),
],
),
],
),
SizedBox(height: 20),
Container(
child: Expanded(
child: state is TableLoaded
? Container(
decoration: containerDecoration,
child: DynamicTable(
size: size*0.5,
headers: ['Device Name', 'Device ID', 'Access Type', 'Unit Name', 'Status'],
data: state.data.map((item) {
return [
item.name.toString(),
item.uuid.toString(),
item.productType.toString(),
'',
item.online.toString(),
// item.categoryName.toString(),
// accessBloc.timestampToDateTime(item.effectiveTime).toString(),
// accessBloc.timestampToDateTime(item.invalidTime).toString(),
// item.deviceUuid.toString(),
// item.passwordCreated != null ? accessBloc.timestampToDateTime(item.passwordCreated).toString() : 'no data',
// item.passwordStatus.toString(),
];
}).toList(),
),
)
// TableWidget(size: size, headers: ['Device Name', 'Device ID', 'Access Type', 'Unit Name', 'Status', 'Virtual Address',], data: [], bloc: bloc)
: const Center(child: CircularProgressIndicator())),
)
],
),
),
),
actionsAlignment: MainAxisAlignment.center,
actions: <Widget>[
Container(
decoration: containerDecoration,
width: size.width * 0.2,
child: DefaultButton(
borderRadius: 8,
onPressed: () {
Navigator.of(context).pop(); // Close the dialog
},
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'),
),
),
],
);
},
),
); }
}

View File

@ -7,8 +7,8 @@ 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_event.dart';
import 'package:syncrow_web/pages/visitor_password/bloc/visitor_password_state.dart';
import 'package:syncrow_web/pages/visitor_password/view/add_device_dialog.dart';
import 'package:syncrow_web/pages/visitor_password/view/repeat_widget.dart';
import 'package:syncrow_web/utils/snack_bar.dart';
import 'package:syncrow_web/utils/style.dart';
class VisitorPasswordDialog extends StatelessWidget {
@ -24,216 +24,242 @@ class VisitorPasswordDialog extends StatelessWidget {
final visitorBloc = BlocProvider.of<VisitorPasswordBloc>(context);
bool isRepeat = state is IsRepeatState ? state.repeat : visitorBloc.repeat;
return AlertDialog(
backgroundColor: Colors.white,
title: const Text('Create visitor password'),
content: SingleChildScrollView(
child: ListBody(
children: <Widget>[
Row(
children: [
Expanded(
flex: 2,
child: CustomWebTextField(
controller: visitorBloc.userNameController,
isRequired: true,
textFieldName: 'User Name',
description: '',
),
),
const Spacer(),
Expanded(
flex: 2,
child: CustomWebTextField(
controller: visitorBloc.emailController,
isRequired: true,
textFieldName: 'Email Address',
description: 'The password will be sent to the visitors email address.',
),
),
],
),
SizedBox(height: size.height * 0.02), // Add spacing
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
child: Padding(
padding: const EdgeInsets.all(10.0),
child: ListBody(
children: <Widget>[
Container(
child: Row(
children: [
Text(
'* ',
style: Theme.of(context)
.textTheme
.bodyMedium!
.copyWith(color: Colors.red),
Expanded(
flex: 2,
child: CustomWebTextField(
controller: visitorBloc.userNameController,
isRequired: true,
textFieldName: 'Name',
description: '',
),
),
const Text('Access Type'),
const Spacer(),
Expanded(
flex: 2,
child: CustomWebTextField(
controller: visitorBloc.emailController,
isRequired: true,
textFieldName: 'Email Address',
description: 'The password will be sent to the visitors email address.',
),
),
const Spacer(),
],
),
Row(
children: <Widget>[
SizedBox(
width: size.width * 0.15,
child: RadioListTile<String>(
title: const Text('Offline Password'),
value: 'Offline Password',
groupValue: (state is PasswordTypeSelected)
? state.selectedType
: visitorBloc.accessTypeSelected,
onChanged: (String? value) {
if (value != null) {
context.read<VisitorPasswordBloc>().add(SelectPasswordType(value));
}
},
),
SizedBox(height: 20,),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Text(
'* ',
style: Theme.of(context)
.textTheme
.bodyMedium!
.copyWith(color: Colors.red),
),
),
SizedBox(
width: size.width * 0.15,
child: RadioListTile<String>(
title: const Text('Online Password'),
value: 'Online Password',
groupValue: (state is PasswordTypeSelected)
? state.selectedType
: visitorBloc.accessTypeSelected,
onChanged: (String? value) {
if (value != null) {
context.read<VisitorPasswordBloc>().add(SelectPasswordType(value));
}
},
const Text('Access Type'),
],
),
Row(
children: <Widget>[
SizedBox(
width: size.width * 0.15,
child: RadioListTile<String>(
title: const Text('Offline Password'),
value: 'Offline Password',
groupValue: (state is PasswordTypeSelected)
? state.selectedType
: visitorBloc.accessTypeSelected,
onChanged: (String? value) {
if (value != null) {
context.read<VisitorPasswordBloc>().add(SelectPasswordType(value));
}
},
),
),
),
SizedBox(
width: size.width * 0.15,
child: RadioListTile<String>(
title: const Text('Dynamic Password'),
value: 'Dynamic Password',
groupValue: (state is PasswordTypeSelected)
? state.selectedType
: visitorBloc.accessTypeSelected,
onChanged: (String? value) {
if (value != null) {
context.read<VisitorPasswordBloc>().add(SelectPasswordType(value));
}
},
SizedBox(
width: size.width * 0.15,
child: RadioListTile<String>(
title: const Text('Online Password'),
value: 'Online Password',
groupValue: (state is PasswordTypeSelected)
? state.selectedType
: visitorBloc.accessTypeSelected,
onChanged: (String? value) {
if (value != null) {
context.read<VisitorPasswordBloc>().add(SelectPasswordType(value));
}
},
),
),
),
],
),
],
),
visitorBloc.accessTypeSelected=='Dynamic Password' ?
SizedBox():
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Text(
'* ',
style: Theme.of(context)
.textTheme
.bodyMedium!
.copyWith(color: Colors.red),
),
const Text('Usage Frequency'),
],
),
Row(
children: <Widget>[
SizedBox(
width: 200,
child: RadioListTile<String>(
title: const Text('One-Time'),
value: 'One-Time',
groupValue: (state is UsageFrequencySelected)
? state.selectedFrequency
: visitorBloc.usageFrequencySelected,
onChanged: (String? value) {
if (value != null) {
context.read<VisitorPasswordBloc>().add(SelectUsageFrequency(value));
}
},
SizedBox(
width: size.width * 0.15,
child: RadioListTile<String>(
title: const Text('Dynamic Password'),
value: 'Dynamic Password',
groupValue: (state is PasswordTypeSelected)
? state.selectedType
: visitorBloc.accessTypeSelected,
onChanged: (String? value) {
if (value != null) {
context.read<VisitorPasswordBloc>().add(SelectPasswordType(value));
}
},
),
),
),
SizedBox(
width: 200,
child: RadioListTile<String>(
title: const Text('Periodic'),
value: 'Periodic',
groupValue: (state is UsageFrequencySelected)
? state.selectedFrequency
: visitorBloc.usageFrequencySelected,
onChanged: (String? value) {
if (value != null) {
context.read<VisitorPasswordBloc>().add(SelectUsageFrequency(value));
}
},
],
),
const Text('Only currently online devices can be selected. It is recommended to use when the device network is stable, and the system randomly generates a digital password'),
SizedBox(height: 20,)
],
),
visitorBloc.accessTypeSelected=='Dynamic Password' ?
SizedBox():
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Text(
'* ',
style: Theme.of(context)
.textTheme
.bodyMedium!
.copyWith(color: Colors.red),
),
),
],
),
],
),
visitorBloc.accessTypeSelected=='Dynamic Password' ?
SizedBox():
const Text('Usage Frequency'),
],
),
Row(
children: <Widget>[
SizedBox(
width: 200,
child: RadioListTile<String>(
title: const Text('One-Time'),
value: 'One-Time',
groupValue: (state is UsageFrequencySelected)
? state.selectedFrequency
: visitorBloc.usageFrequencySelected,
onChanged: (String? value) {
if (value != null) {
context.read<VisitorPasswordBloc>().add(SelectUsageFrequency(value));
}
},
),
),
SizedBox(
width: 200,
child: RadioListTile<String>(
title: const Text('Periodic'),
value: 'Periodic',
groupValue: (state is UsageFrequencySelected)
? state.selectedFrequency
: visitorBloc.usageFrequencySelected,
onChanged: (String? value) {
if (value != null) {
context.read<VisitorPasswordBloc>().add(SelectUsageFrequency(value));
}
},
),
),
],
),
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: visitorBloc.startTime,
secondString: visitorBloc.endTime,
),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Text(
'* ',
style: Theme.of(context)
.textTheme
.bodyMedium!
.copyWith(color: Colors.red),
),
const Text('Access Devices'),
],
),
const Text('Within the validity period, each device can be unlocked only once.'),
visitorBloc.usageFrequencySelected=='Periodic'&&visitorBloc.accessTypeSelected=='Offline Password'?
SizedBox(
width: 100,
child: ListTile(
contentPadding: EdgeInsets.zero,
leading: const Text('Repeat'),
trailing: Transform.scale(
scale: .8,
child: CupertinoSwitch(
value: visitorBloc.repeat,
onChanged: (value) {
visitorBloc.add(ToggleRepeatEvent());
},
applyTheme: true,
Text('Within the validity period, each device can be unlocked only once.')
],
),
const SizedBox(height: 20,),
visitorBloc.accessTypeSelected=='Dynamic Password' ?
SizedBox():
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: visitorBloc.startTime,
secondString: visitorBloc.endTime,
),
const SizedBox(height: 20,),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Text(
'* ',
style: Theme.of(context)
.textTheme
.bodyMedium!
.copyWith(color: Colors.red),
),
const Text('Access Devices'),
],
),
const Text('Within the validity period, each device can be unlocked only once.'),
const SizedBox(height: 20,),
visitorBloc.usageFrequencySelected=='Periodic'&&visitorBloc.accessTypeSelected=='Offline Password'?
SizedBox(
width: 100,
child: ListTile(
contentPadding: EdgeInsets.zero,
leading: const Text('Repeat'),
trailing: Transform.scale(
scale: .8,
child: CupertinoSwitch(
value: visitorBloc.repeat,
onChanged: (value) {
visitorBloc.add(ToggleRepeatEvent());
},
applyTheme: true,
),
),
),
),
):const SizedBox(),
isRepeat ? const RepeatWidget() : const SizedBox(),
Container(
decoration: containerDecoration,
width: size.width * 0.2,
child: const DefaultButton(
borderRadius: 8,
child: Text('+ Add Device'),
),
),
],
),
):const SizedBox(),
isRepeat ? const RepeatWidget() : const SizedBox(),
Container(
decoration: containerDecoration,
width: size.width * 0.1,
child: DefaultButton(
onPressed: () {
showDialog(
context: context,
barrierDismissible: false,
builder: (BuildContext context) {
return const AddDeviceDialog();
],
},
);
},
borderRadius: 8,
child: Text('+ Add Device'),
),
),
],
),
],
),
),
),
actionsAlignment: MainAxisAlignment.center,

View File

@ -6,6 +6,8 @@ import 'package:syncrow_web/pages/access_management/model/password_model.dart';
import 'package:syncrow_web/services/api/http_service.dart';
import 'package:syncrow_web/utils/constants/api_const.dart';
import '../pages/visitor_password/model/device_model.dart';
class AccessMangApi{
@ -31,6 +33,27 @@ class AccessMangApi{
}
}
Future fetchDevices() async {
try {
final response = await HTTPService().get(
path: ApiEndpoints.getDevices,
showServerMessage: true,
expectedResponseModel: (json) {
List<dynamic> jsonData = json;
print('fetchDevices List: $json');
List<DeviceModel> passwordList = jsonData.map((jsonItem) {
return DeviceModel.fromJson(jsonItem);
}).toList();
return passwordList;
},
);
return response;
} catch (e) {
debugPrint('Error fetching $e');
return [];
}
}

View File

@ -46,8 +46,10 @@ class AuthenticationAPI {
},
showServerMessage: true,
expectedResponseModel: (json) {
print('object==$json');
return 30;
}
);
return 30;
} on DioException catch (e) {

View File

@ -18,6 +18,7 @@ abstract class ColorsManager {
static const Color dozeColor = Color(0xFFFEC258);
static const Color relaxColor = Color(0xFFFBD288);
static const Color readingColor = Color(0xFFF7D69C);
static const Color worningColor = Color(0xFFFFF3C8);
static const Color energizingColor = Color(0xFFEDEDED);
static const Color dividerColor = Color(0xFFEBEBEB);
static const Color slidingBlueColor = Color(0x99023DFE);

View File

@ -10,5 +10,6 @@ abstract class ApiEndpoints {
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 getUser = '$baseUrl/user/{userUuid}';
}

View File

@ -24,4 +24,5 @@ class Assets {
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";
static const String deviceNoteIcon = "assets/images/device_note.svg";
}

View File

@ -0,0 +1,44 @@
enum AccessType {
onlineOnetime,
onlineMultiple,
offlineOnetime,
offlineMultiple,
}
extension AccessTypeExtension on AccessType {
String get value {
switch (this) {
case AccessType.onlineOnetime:
return "Online Password";
case AccessType.onlineMultiple:
return "online Multiple Password";
case AccessType.offlineOnetime:
return "Offline Onetime Password";
case AccessType.offlineMultiple:
return "Offline Multiple Password";
}
}
static AccessType fromString(String value) {
switch (value) {
case "ONLINE_ONETIME":
return AccessType.onlineOnetime;
case "ONLINE_MULTIPLE":
return AccessType.onlineMultiple;
case "OFFLINE_ONETIME":
return AccessType.offlineOnetime;
case "OFFLINE_MULTIPLE":
return AccessType.offlineMultiple;
default:
throw ArgumentError("Invalid access type: $value");
}
}
}

View File

@ -1,5 +0,0 @@

View File

@ -6,7 +6,7 @@ InputDecoration? textBoxDecoration({bool suffixIcon = false}) => InputDecoration
suffixIcon:suffixIcon? const Icon(Icons.search):null,
hintText: 'Search',
filled: true, // Enable background filling
fillColor: Colors.grey.shade200, // Set the background color
fillColor: const Color(0xffF5F6F7), // Set the background color
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(8), // Add border radius
borderSide: BorderSide.none, // Remove the underline
@ -30,16 +30,17 @@ InputDecoration? textBoxDecoration({bool suffixIcon = false}) => InputDecoration
);
Decoration containerDecoration = BoxDecoration(
BoxDecoration containerDecoration = BoxDecoration(
boxShadow: [
BoxShadow(
color: Colors.grey.withOpacity(0.5),
spreadRadius: 5,
blurRadius: 8,
offset: Offset(0,
offset: const Offset(0,
3), // changes position of shadow
),
],
color: ColorsManager.boxColor,
borderRadius: BorderRadius.all(Radius.circular(10)));
borderRadius: const BorderRadius.all(Radius.circular(10)));