CreateTemporaryPassword

This commit is contained in:
mohammad
2024-06-29 23:38:52 +03:00
parent 8d6d5da5cd
commit e8184c00ff
14 changed files with 680 additions and 342 deletions

View File

@ -1,68 +1,30 @@
import 'dart:math'; import 'dart:math';
import 'package:day_picker/model/day_in_week.dart'; import 'package:day_picker/model/day_in_week.dart';
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:intl/intl.dart';
import 'package:syncrow_app/features/devices/bloc/smart_door_bloc/smart_door_event.dart'; import 'package:syncrow_app/features/devices/bloc/smart_door_bloc/smart_door_event.dart';
import 'package:syncrow_app/features/devices/bloc/smart_door_bloc/smart_door_state.dart'; import 'package:syncrow_app/features/devices/bloc/smart_door_bloc/smart_door_state.dart';
import 'package:syncrow_app/features/devices/model/device_control_model.dart'; import 'package:syncrow_app/features/devices/model/device_control_model.dart';
import 'package:syncrow_app/features/devices/model/smart_door_model.dart'; import 'package:syncrow_app/features/devices/model/smart_door_model.dart';
import 'package:syncrow_app/features/devices/model/status_model.dart'; import 'package:syncrow_app/features/devices/model/status_model.dart';
import 'package:syncrow_app/features/devices/model/create_temporary_password_model.dart';
import 'package:syncrow_app/features/devices/model/temporary_password_model.dart';
import 'package:syncrow_app/services/api/devices_api.dart'; import 'package:syncrow_app/services/api/devices_api.dart';
class SmartDoorBloc extends Bloc<SmartDoorEvent, SmartDoorState> { class SmartDoorBloc extends Bloc<SmartDoorEvent, SmartDoorState> {
final String deviceId; final String deviceId;
late SmartDoorModel deviceStatus; late SmartDoorModel deviceStatus;
TextEditingController passwordController = TextEditingController(); static String pageType = '';
TextEditingController passwordNameController = TextEditingController();
String effectiveTime = 'Select Time';
int? effectiveTimeTimeStamp;
String expirationTime = 'Select Time';
int? expirationTimeTimeStamp;
bool repeat = false;
bool isStartEndTime = true;
List<String>? selectedDay;
DateTime? startTime;
DateTime? endTime ;
changeTime(val,isStartEndTime){
emit(LoadingInitialState());
if(isStartEndTime==true){
startTime=val;
}else{
endTime=val;
}
emit(changeTimeState());
}
bool repeatFunction() {
emit(LoadingInitialState());
repeat = !repeat;
emit(IsRepeatState());
return repeat;
}
bool isStartEndTimeFun(val) {
emit(LoadingInitialState());
isStartEndTime = val;
emit(IsStartEndState());
return isStartEndTime;
}
SmartDoorBloc({required this.deviceId}) : super(InitialState()) { SmartDoorBloc({required this.deviceId}) : super(InitialState()) {
on<InitialEvent>(_fetchSmartDoorStatus); on<InitialEvent>(_fetchSmartDoorStatus);
on<InitialPasswordsPage>(getTemporaryPasswords);
on<UpdateLockEvent>(_updateLock); on<UpdateLockEvent>(_updateLock);
} }
void _fetchSmartDoorStatus(InitialEvent event, Emitter<SmartDoorState> emit) async {
void _fetchSmartDoorStatus(
InitialEvent event, Emitter<SmartDoorState> emit) async {
emit(LoadingInitialState());
try { try {
emit(LoadingInitialState());
var response = await DevicesAPI.getDeviceStatus(deviceId); var response = await DevicesAPI.getDeviceStatus(deviceId);
List<StatusModel> statusModelList = []; List<StatusModel> statusModelList = [];
for (var status in response['status']) { for (var status in response['status']) {
@ -76,6 +38,64 @@ class SmartDoorBloc extends Bloc<SmartDoorEvent, SmartDoorState> {
} }
} }
void getTemporaryPasswords(InitialPasswordsPage event, Emitter<SmartDoorState> emit) async {
try {
emit(LoadingInitialState());
pageType=event.type!;
var response = await DevicesAPI.getTemporaryPasswords(deviceId,pageType);
if (response is List) {
temporaryPasswords = response.map((item) => TemporaryPassword.fromJson(item)).toList();
} else if (response is Map && response.containsKey('data')) {
temporaryPasswords = (response['data'] as List).map((item) => TemporaryPassword.fromJson(item)).toList();
} else {
throw Exception("Unexpected response format");
}
emit(TemporaryPasswordsLoadedState( temporaryPassword: temporaryPasswords!));
} catch (e) {
print(e);
emit(FailedState(errorMessage: e.toString()));
}
}
TextEditingController passwordController = TextEditingController();
TextEditingController passwordNameController = TextEditingController();
String effectiveTime = 'Select Time';
int? effectiveTimeTimeStamp;
String expirationTime ='Select Time';
int? expirationTimeTimeStamp;
bool repeat = false;
bool isStartEndTime = true;
List<String>? selectedDay;
DateTime? startTime;
DateTime? endTime;
List<TemporaryPassword>? temporaryPasswords=[];
List<TemporaryPassword>? oneTimePasswords=[];
changeTime(val, isStartEndTime) {
emit(LoadingInitialState());
if (isStartEndTime == true) {
startTime = val;
} else {
endTime = val;
}
emit(changeTimeState());
}
bool toggleRepeat() {
emit(LoadingInitialState());
repeat = !repeat;
emit(IsRepeatState());
return repeat;
}
bool setStartEndTime(bool val) {
emit(LoadingInitialState());
isStartEndTime = val;
emit(IsStartEndState());
return isStartEndTime;
}
void _updateLock(UpdateLockEvent event, Emitter<SmartDoorState> emit) async { void _updateLock(UpdateLockEvent event, Emitter<SmartDoorState> emit) async {
emit(LoadingNewSate(smartDoorModel: deviceStatus)); emit(LoadingNewSate(smartDoorModel: deviceStatus));
try { try {
@ -97,8 +117,8 @@ class SmartDoorBloc extends Bloc<SmartDoorEvent, SmartDoorState> {
emit(LoadingInitialState()); emit(LoadingInitialState());
passwordController.clear(); passwordController.clear();
Random random = Random(); Random random = Random();
int min = 1000000; // Minimum 7-digit number int min = 1000000;
int max = 9999999; // Maximum 7-digit number int max = 9999999;
passwordController.text = (min + random.nextInt(max - min + 1)).toString(); passwordController.text = (min + random.nextInt(max - min + 1)).toString();
emit(GeneratePasswordState()); emit(GeneratePasswordState());
} }
@ -124,34 +144,18 @@ class SmartDoorBloc extends Bloc<SmartDoorEvent, SmartDoorState> {
timePicked.minute, timePicked.minute,
); );
if (isEffective) { if (isEffective) {
if (expirationTimeTimeStamp != null && if (expirationTimeTimeStamp != null && selectedDateTime.millisecondsSinceEpoch > expirationTimeTimeStamp!) {
selectedDateTime.millisecondsSinceEpoch > _showSnackBar(context, 'Effective Time cannot be later than Expiration Time.');
expirationTimeTimeStamp!) {
// Show error message
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text(
'Effective Time cannot be later than Expiration Time.'),
),
);
} else { } else {
effectiveTime = selectedDateTime.toString(); effectiveTime = selectedDateTime.toString();
effectiveTimeTimeStamp = selectedDateTime.millisecondsSinceEpoch; effectiveTimeTimeStamp = selectedDateTime.millisecondsSinceEpoch;
} }
} else { } else {
if (effectiveTimeTimeStamp != null && if (effectiveTimeTimeStamp != null && selectedDateTime.millisecondsSinceEpoch < effectiveTimeTimeStamp!) {
selectedDateTime.millisecondsSinceEpoch < _showSnackBar(context, 'Expiration Time cannot be earlier than Effective Time.');
effectiveTimeTimeStamp!) {
// Show error message
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text(
'Expiration Time cannot be earlier than Effective Time.'),
),
);
} else { } else {
expirationTime = selectedDateTime.toString(); expirationTime = selectedDateTime.toString();
expirationTimeTimeStamp = selectedDateTime.millisecondsSinceEpoch; expirationTimeTimeStamp = selectedDateTime.millisecondsSinceEpoch;
} }
} }
emit(TimeSelectedState()); emit(TimeSelectedState());
@ -160,74 +164,98 @@ class SmartDoorBloc extends Bloc<SmartDoorEvent, SmartDoorState> {
} }
Future<void> saveFunction(BuildContext context,) async { Future<void> savePassword(BuildContext context) async {
if (passwordController.text.isEmpty) { if (_validateInputs(context)) return;
ScaffoldMessenger.of(context).showSnackBar( try {
const SnackBar( emit(LoadingInitialState());
content: Text('Password name required'), var response = await DevicesAPI.createPassword(
backgroundColor: Colors.red, pageType: pageType,
), deviceId: deviceId,
effectiveTime: effectiveTimeTimeStamp.toString(),
invalidTime: expirationTimeTimeStamp.toString(),
name: passwordNameController.text,
password: passwordController.text,
scheduleList: [
if (repeat)
Schedule(
effectiveTime: getTimeOnly(startTime),
invalidTime: getTimeOnly(endTime).toString(),
workingDay: selectedDay!,
),
],
); );
return; add(InitialPasswordsPage(type: pageType));
} emit(TemporaryPasswordsLoadedState(temporaryPassword: temporaryPasswords!));
if (passwordNameController.text.isEmpty) { Navigator.pop(context);
ScaffoldMessenger.of(context).showSnackBar( Navigator.pop(context);
const SnackBar( } catch (e) {
content: Text('Password name required'), emit(FailedState(errorMessage: 'Failed to save password: $e'));
backgroundColor: Colors.red,
),
);
return;
}
if (effectiveTime == 'Select Time' || effectiveTimeTimeStamp == null) {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text('Select effective time'),
backgroundColor: Colors.red,
),
);
return;
}
if (expirationTime == 'Select Time' || expirationTimeTimeStamp == null) {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text('Select expiration time'),
backgroundColor: Colors.red,
),
);
return;
} }
} }
void deletePassword(BuildContext context, passwordId) async {
try {
emit(LoadingInitialState());
var response = await DevicesAPI.deletePassword(
deviceId: deviceId,
passwordId: passwordId
).then((value) async {
add(InitialPasswordsPage(type: pageType));
});
} catch (e) {
emit(FailedState(errorMessage: e.toString()));
}
}
bool _validateInputs(BuildContext context) {
if (passwordController.text.isEmpty) {
_showSnackBar(context, 'Password required');
return true;
}
if (passwordNameController.text.isEmpty) {
_showSnackBar(context, 'Password name required');
return true;
}
if (effectiveTime == 'Select Time' || effectiveTimeTimeStamp == null) {
_showSnackBar(context, 'Select effective time');
return true;
}
if (expirationTime == 'Select Time' || expirationTimeTimeStamp == null) {
_showSnackBar(context, 'Select expiration time');
return true;
}
if(repeat==true&&(endTime==null||startTime==null||selectedDay==null)){
_showSnackBar(context, 'Start Time and End time and the days required ');
return true;
}
return false;
}
void _showSnackBar(BuildContext context, String message) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(message),
backgroundColor: Colors.red,
),
);
}
List<DayInWeek> days = [ List<DayInWeek> days = [
DayInWeek( DayInWeek("Sun", dayKey: 'Sun'),
"Sun", DayInWeek("Mon", dayKey: 'Mon'),
dayKey: 'Sun', DayInWeek("Tue", dayKey: 'Tue'),
), DayInWeek("Wed", dayKey: 'Wed'),
DayInWeek( DayInWeek("Thu", dayKey: 'Thu'),
"Mon", DayInWeek("Fri", dayKey: 'Fri'),
dayKey: 'Mon', DayInWeek("Sat", dayKey: 'Sat'),
),
DayInWeek(
"Tue",
isSelected: true,
dayKey: 'Tue'
),
DayInWeek(
"Wed",
dayKey: 'Wed',
),
DayInWeek(
"Thu",
dayKey: 'Thu',
),
DayInWeek(
"Fri",
dayKey: 'Fri',
),
DayInWeek(
"Sat",
dayKey: 'Sat',
),
]; ];
String getTimeOnly(DateTime? dateTime) {
if (dateTime == null) return '';
return DateFormat('HH:mm').format(dateTime);
}
} }

View File

@ -8,6 +8,11 @@ abstract class SmartDoorEvent extends Equatable {
} }
class InitialEvent extends SmartDoorEvent {} class InitialEvent extends SmartDoorEvent {}
class InitialPasswordsPage extends SmartDoorEvent {
final String? type;
const InitialPasswordsPage({ this.type});
}
class InitialOneTimePassword extends SmartDoorEvent {}
class UpdateLockEvent extends SmartDoorEvent { class UpdateLockEvent extends SmartDoorEvent {
final bool value; final bool value;

View File

@ -1,5 +1,6 @@
import 'package:equatable/equatable.dart'; import 'package:equatable/equatable.dart';
import 'package:syncrow_app/features/devices/model/smart_door_model.dart'; import 'package:syncrow_app/features/devices/model/smart_door_model.dart';
import 'package:syncrow_app/features/devices/model/temporary_password_model.dart';
class SmartDoorState extends Equatable { class SmartDoorState extends Equatable {
const SmartDoorState(); const SmartDoorState();
@ -44,3 +45,7 @@ class IsStartEndState extends SmartDoorState{}
class changeStartTimeState extends SmartDoorState{} class changeStartTimeState extends SmartDoorState{}
class changeEndTimeState extends SmartDoorState{} class changeEndTimeState extends SmartDoorState{}
class changeTimeState extends SmartDoorState{} class changeTimeState extends SmartDoorState{}
class TemporaryPasswordsLoadedState extends SmartDoorState{
final List<TemporaryPassword> temporaryPassword;
const TemporaryPasswordsLoadedState({required this.temporaryPassword});
}

View File

@ -0,0 +1,68 @@
class CreateTemporaryPasswordModel{
final String name;
late final String password;
late final String effectiveTime;
late final String invalidTime;
List<Schedule>? scheduleList;
CreateTemporaryPasswordModel({
required this.name,
required this.password,
required this.effectiveTime,
required this.invalidTime,
this.scheduleList,
});
factory CreateTemporaryPasswordModel.fromJson(Map<String, dynamic> json) {
return CreateTemporaryPasswordModel(
name: json['name'],
password: json['password'],
effectiveTime: json['effectiveTime'],
invalidTime: json['invalidTime'],
scheduleList: (json['scheduleList'] as List)
.map((i) => Schedule.fromJson(i))
.toList(),
);
}
Map<String, dynamic> toJson() {
return {
'name': name,
'password': password,
'effectiveTime': effectiveTime,
'invalidTime': invalidTime,
'scheduleList': scheduleList!.map((i) => i.toJson()).toList(),
};
}
}
class Schedule {
final String effectiveTime;
final String invalidTime;
final List<String> workingDay;
Schedule({
required this.effectiveTime,
required this.invalidTime,
required this.workingDay,
});
factory Schedule.fromJson(Map<String, dynamic> json) {
return Schedule(
effectiveTime: json['effectiveTime'],
invalidTime: json['invalidTime'],
workingDay: List<String>.from(json['workingDay']),
);
}
Map<String, dynamic> toJson() {
return {
'effectiveTime': effectiveTime,
'invalidTime': invalidTime,
'workingDay': workingDay,
};
}
}

View File

@ -0,0 +1,37 @@
class TemporaryPassword {
final int effectiveTime;
final int id;
final int invalidTime;
final String name;
final int phase;
final String phone;
final int sn;
final String timeZone;
final int type;
TemporaryPassword({
required this.effectiveTime,
required this.id,
required this.invalidTime,
required this.name,
required this.phase,
required this.phone,
required this.sn,
required this.timeZone,
required this.type,
});
factory TemporaryPassword.fromJson(Map<String, dynamic> json) {
return TemporaryPassword(
effectiveTime: json['effectiveTime'],
id: json['id'],
invalidTime: json['invalidTime'],
name: json['name'],
phase: json['phase'],
phone: json['phone'] ?? '',
sn: json['sn'],
timeZone: json['timeZone'] ?? '',
type: json['type'],
);
}
}

View File

@ -240,8 +240,7 @@ class CeilingSensorInterface extends StatelessWidget {
padding: padding:
const EdgeInsets.symmetric(vertical: 12, horizontal: 20), const EdgeInsets.symmetric(vertical: 12, horizontal: 20),
onTap: () async { onTap: () async {
if (ceilingSensorButtons()[index]['title'] == if (ceilingSensorButtons()[index]['title'] == 'Sensitivity') {
'Sensitivity') {
final result = await showDialog( final result = await showDialog(
context: context, context: context,
builder: (context) { builder: (context) {

View File

@ -11,21 +11,18 @@ import 'package:syncrow_app/features/shared_widgets/text_widgets/body_medium.dar
import 'package:syncrow_app/utils/resource_manager/color_manager.dart'; import 'package:syncrow_app/utils/resource_manager/color_manager.dart';
import 'package:time_picker_spinner/time_picker_spinner.dart'; import 'package:time_picker_spinner/time_picker_spinner.dart';
class CreateTemporaryPassword extends StatefulWidget { class CreateTemporaryPassword extends StatelessWidget {
const CreateTemporaryPassword({super.key}); final String? deviceId;
@override const CreateTemporaryPassword({super.key,this.deviceId});
State<CreateTemporaryPassword> createState() =>
_CreateTemporaryPasswordState();
}
class _CreateTemporaryPasswordState extends State<CreateTemporaryPassword> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return BlocProvider( return BlocProvider(
create: (BuildContext context) => SmartDoorBloc(deviceId: ''), create: (BuildContext context) => SmartDoorBloc(deviceId: deviceId!),
child: BlocConsumer<SmartDoorBloc, SmartDoorState>( child: BlocConsumer<SmartDoorBloc, SmartDoorState>(
listener: (context, state) { listener: (context, state) {
print(state);
if (state is FailedState) { if (state is FailedState) {
ScaffoldMessenger.of(context).showSnackBar( ScaffoldMessenger.of(context).showSnackBar(
SnackBar( SnackBar(
@ -34,18 +31,22 @@ class _CreateTemporaryPasswordState extends State<CreateTemporaryPassword> {
), ),
); );
} }
}, builder: (context, state) {
}, builder: (context, state) {
return DefaultScaffold( return DefaultScaffold(
title: 'Create Password', title: 'Create Password',
actions: [ actions: [
TextButton( TextButton(
onPressed: () { onPressed: () {
BlocProvider.of<SmartDoorBloc>(context).saveFunction(context); BlocProvider.of<SmartDoorBloc>(context).savePassword(context);
}, },
child: const Text('Save')) child: const Text('Save'))
], ],
child: SingleChildScrollView( child: state is LoadingInitialState?
child: Column( const Center(child: CircularProgressIndicator()): SingleChildScrollView(
child:
Column(
mainAxisAlignment: MainAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
@ -64,8 +65,7 @@ class _CreateTemporaryPasswordState extends State<CreateTemporaryPassword> {
fontColor: ColorsManager.grayColor, fontColor: ColorsManager.grayColor,
), ),
DefaultContainer( DefaultContainer(
padding: padding: const EdgeInsets.symmetric(horizontal: 15, vertical: 15),
const EdgeInsets.symmetric(horizontal: 5, vertical: 5),
child: Row( child: Row(
children: [ children: [
Expanded( Expanded(
@ -125,8 +125,7 @@ class _CreateTemporaryPasswordState extends State<CreateTemporaryPassword> {
width: MediaQuery.of(context).size.width / 2, width: MediaQuery.of(context).size.width / 2,
child: TextFormField( child: TextFormField(
controller: controller:
BlocProvider.of<SmartDoorBloc>(context) BlocProvider.of<SmartDoorBloc>(context).passwordNameController,
.passwordNameController,
decoration: const InputDecoration( decoration: const InputDecoration(
labelText: 'Enter The Name'), labelText: 'Enter The Name'),
)), )),
@ -146,8 +145,8 @@ class _CreateTemporaryPasswordState extends State<CreateTemporaryPassword> {
.selectTime(context, isEffective: true); .selectTime(context, isEffective: true);
}, },
child: Text( child: Text(
BlocProvider.of<SmartDoorBloc>(context) BlocProvider.of<SmartDoorBloc>(context).effectiveTime
.effectiveTime), ),
)), )),
), ),
const Divider(), const Divider(),
@ -194,7 +193,7 @@ class _CreateTemporaryPasswordState extends State<CreateTemporaryPassword> {
BlocProvider.of<SmartDoorBloc>(context).repeat, BlocProvider.of<SmartDoorBloc>(context).repeat,
onChanged: (value) { onChanged: (value) {
BlocProvider.of<SmartDoorBloc>(context) BlocProvider.of<SmartDoorBloc>(context)
.repeatFunction(); .toggleRepeat();
}, },
applyTheme: true, applyTheme: true,
)), )),
@ -217,14 +216,14 @@ class _CreateTemporaryPasswordState extends State<CreateTemporaryPassword> {
children: [ children: [
InkWell( InkWell(
onTap: () { onTap: () {
BlocProvider.of<SmartDoorBloc>(context).isStartEndTimeFun(true); BlocProvider.of<SmartDoorBloc>(context).setStartEndTime(true);
}, },
child: BodyMedium(text: 'Start',fontColor:BlocProvider.of<SmartDoorBloc>(context).isStartEndTime==false? Colors.black:Colors.blue,fontSize: 18,), child: BodyMedium(text: 'Start',fontColor:BlocProvider.of<SmartDoorBloc>(context).isStartEndTime==false? Colors.black:Colors.blue,fontSize: 18,),
), ),
InkWell( InkWell(
onTap: () { onTap: () {
BlocProvider.of<SmartDoorBloc>(context).isStartEndTimeFun(false); BlocProvider.of<SmartDoorBloc>(context).setStartEndTime(false);
}, },
child: BodyMedium(text: 'End',fontColor:BlocProvider.of<SmartDoorBloc>(context).isStartEndTime? Colors.black:Colors.blue,fontSize: 18,), child: BodyMedium(text: 'End',fontColor:BlocProvider.of<SmartDoorBloc>(context).isStartEndTime? Colors.black:Colors.blue,fontSize: 18,),
) )
@ -234,10 +233,10 @@ class _CreateTemporaryPasswordState extends State<CreateTemporaryPassword> {
const Divider(), const Divider(),
Container( Container(
height: 80, height: 80,
child: BlocProvider.of<SmartDoorBloc>(context).isStartEndTime? child:
Container( Container(
height: 90, height: 90,
child: TimePickerSpinner( child:BlocProvider.of<SmartDoorBloc>(context).isStartEndTime? TimePickerSpinner(
time: time:
BlocProvider.of<SmartDoorBloc>(context).startTime, BlocProvider.of<SmartDoorBloc>(context).startTime,
is24HourMode: false, is24HourMode: false,
@ -248,30 +247,24 @@ class _CreateTemporaryPasswordState extends State<CreateTemporaryPassword> {
highlightedTextStyle: const TextStyle( highlightedTextStyle: const TextStyle(
fontSize: 30, color: Colors.blue), fontSize: 30, color: Colors.blue),
onTimeChange: (time) { onTimeChange: (time) {
BlocProvider.of<SmartDoorBloc>(context).startTime=time;
BlocProvider.of<SmartDoorBloc>(context).changeTime(time, BlocProvider.of<SmartDoorBloc>(context).isStartEndTime); BlocProvider.of<SmartDoorBloc>(context).changeTime(time, BlocProvider.of<SmartDoorBloc>(context).isStartEndTime);
}, },
): Container(
child: TimePickerSpinner(
time: BlocProvider.of<SmartDoorBloc>(context).endTime,
is24HourMode: false,
itemHeight: 40,
normalTextStyle: const TextStyle(
fontSize: 24,
),
highlightedTextStyle: const TextStyle(
fontSize: 30, color: Colors.blue),
onTimeChange: (time) {
BlocProvider.of<SmartDoorBloc>(context).changeTime(time, BlocProvider.of<SmartDoorBloc>(context).isStartEndTime);
},
),
), ),
): )
TimePickerSpinner(
time: BlocProvider.of<SmartDoorBloc>(context).endTime,
is24HourMode: false,
itemHeight: 40,
normalTextStyle: const TextStyle(
fontSize: 24,
),
highlightedTextStyle: const TextStyle(
fontSize: 24, color: Colors.blue),
onTimeChange: (time) {
setState(() {
BlocProvider.of<SmartDoorBloc>(context).endTime=time;
BlocProvider.of<SmartDoorBloc>(context).changeTime(time, BlocProvider.of<SmartDoorBloc>(context).isStartEndTime);
// dateTime = time;
});
},
),
), ),
const Divider(), const Divider(),
const SizedBox(height: 20), const SizedBox(height: 20),
@ -288,9 +281,7 @@ class _CreateTemporaryPasswordState extends State<CreateTemporaryPassword> {
borderRadius: BorderRadius.circular(30.0), borderRadius: BorderRadius.circular(30.0),
color: Colors.white), color: Colors.white),
onSelect: (values) { onSelect: (values) {
print(values); BlocProvider.of<SmartDoorBloc>(context).selectedDay = values;
BlocProvider.of<SmartDoorBloc>(context)
.selectedDay = values;
}, },
), ),
], ],

View File

@ -0,0 +1,135 @@
import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
import 'package:syncrow_app/features/devices/model/device_model.dart';
import 'package:syncrow_app/features/devices/model/temporary_password_model.dart';
import 'package:syncrow_app/features/shared_widgets/text_widgets/body_medium.dart';
import 'package:syncrow_app/features/shared_widgets/text_widgets/title_medium.dart';
import 'package:syncrow_app/utils/context_extension.dart';
import 'package:syncrow_app/utils/helpers/snack_bar.dart';
import 'package:syncrow_app/utils/resource_manager/color_manager.dart';
import 'package:syncrow_app/utils/resource_manager/font_manager.dart';
class DoorDialog extends StatefulWidget {
final String title;
final TemporaryPassword value;
const DoorDialog({
super.key,
required this.title,
required this.value,
});
@override
DoorDialogState createState() => DoorDialogState();
}
class DoorDialogState extends State<DoorDialog> {
@override
void initState() {
super.initState();
}
@override
Widget build(BuildContext context) {
final DateTime effectiveDateTime =
DateTime.fromMillisecondsSinceEpoch(widget.value.effectiveTime);
final DateTime expiredDateTime =
DateTime.fromMillisecondsSinceEpoch(widget.value.invalidTime);
final DateFormat formatter = DateFormat('HH:mm');
return Dialog(
child: Container(
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(20),
),
padding: const EdgeInsets.only(top: 20),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
BodyMedium(
text: widget.title,
style: context.bodyMedium.copyWith(
color: ColorsManager.primaryColorWithOpacity,
fontWeight: FontsManager.extraBold,
),
),
Padding(
padding: const EdgeInsets.symmetric(
vertical: 15,
horizontal: 50,
),
child: Container(
height: 1,
width: double.infinity,
color: ColorsManager.greyColor,
),
),
Padding(
padding: const EdgeInsets.all(15.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
const Text('Effective Time :'),
Text(formatter.format(effectiveDateTime)),
],
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
const Text('Expired Time :'),
Text(formatter.format(expiredDateTime)),
],
),
],
),
),
Container(
height: 1,
width: double.infinity,
color: ColorsManager.greyColor,
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
InkWell(
onTap: () {
Navigator.pop(context);
},
child: Center(
child: BodyMedium(
text: 'Cancel',
style: context.bodyMedium
.copyWith(color: ColorsManager.greyColor),
),
),
),
Container(
height: 50,
width: 1,
color: ColorsManager.greyColor,
),
InkWell(
onTap: () {
Navigator.pop(context, 'delete');
},
child: Center(
child: BodyMedium(
text: 'Delete Password',
style: context.bodyMedium.copyWith(
color: ColorsManager.primaryColorWithOpacity),
),
),
),
],
)
],
),
),
);
}
}

View File

@ -10,9 +10,9 @@ import 'package:syncrow_app/features/shared_widgets/text_widgets/body_small.dart
import 'package:syncrow_app/generated/assets.dart'; import 'package:syncrow_app/generated/assets.dart';
class DoorLockGrid extends StatelessWidget { class DoorLockGrid extends StatelessWidget {
const DoorLockGrid({ String uuid;
super.key, DoorLockGrid({
}); super.key,required this.uuid});
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
@ -29,10 +29,10 @@ class DoorLockGrid extends StatelessWidget {
itemBuilder: (context, index) => DefaultContainer( itemBuilder: (context, index) => DefaultContainer(
onTap: () { onTap: () {
//TODO: remove checking after adding the pages //TODO: remove checking after adding the pages
doorLockButtons[index]['page'] != null doorLockButtons()[index]['page'] != null
? Navigator.of(context).push( ? Navigator.of(context).push(
MaterialPageRoute( MaterialPageRoute(
builder: (context) => doorLockButtons[index]['page'] as Widget, builder: (context) => doorLockButtons(val: uuid)[index]['page'] as Widget,
), ),
) )
: null; : null;
@ -44,7 +44,7 @@ class DoorLockGrid extends StatelessWidget {
ConstrainedBox( ConstrainedBox(
constraints: const BoxConstraints(maxHeight: 46, maxWidth: 50), constraints: const BoxConstraints(maxHeight: 46, maxWidth: 50),
child: SvgPicture.asset( child: SvgPicture.asset(
doorLockButtons[index]['image'] as String, doorLockButtons()[index]['image'] as String,
), ),
), ),
const SizedBox( const SizedBox(
@ -53,7 +53,7 @@ class DoorLockGrid extends StatelessWidget {
Flexible( Flexible(
child: FittedBox( child: FittedBox(
child: BodySmall( child: BodySmall(
text: doorLockButtons[index]['title'] as String, text: doorLockButtons()[index]['title'] as String,
// doorLockButtons.keys.elementAt(index), // doorLockButtons.keys.elementAt(index),
textAlign: TextAlign.center, textAlign: TextAlign.center,
), ),
@ -66,7 +66,7 @@ class DoorLockGrid extends StatelessWidget {
} }
} }
List<Map<String, dynamic>> doorLockButtons = [ List<Map<String, dynamic>> doorLockButtons({val}) => [
{ {
'title': 'Unlocking Records', 'title': 'Unlocking Records',
'image': Assets.assetsIconsDoorlockAssetsUnlockingRecords, 'image': Assets.assetsIconsDoorlockAssetsUnlockingRecords,
@ -80,7 +80,7 @@ List<Map<String, dynamic>> doorLockButtons = [
{ {
'title': 'Temporary Password', 'title': 'Temporary Password',
'image': Assets.assetsIconsDoorlockAssetsTemporaryPassword, 'image': Assets.assetsIconsDoorlockAssetsTemporaryPassword,
'page':const TemporaryPasswordPage() , 'page': TemporaryPasswordPage(deviceId:val) ,
}, },
{ {
'title': 'Smart Linkage', 'title': 'Smart Linkage',

View File

@ -111,7 +111,7 @@ class DoorInterface extends StatelessWidget {
doorLock: doorLock, doorLock: doorLock,
smartDoorModel: smartDoorModel, smartDoorModel: smartDoorModel,
), ),
const DoorLockGrid(), DoorLockGrid( uuid: doorLock.uuid!, ),
], ],
) )

View File

@ -9,14 +9,10 @@ import 'package:syncrow_app/features/shared_widgets/text_widgets/body_medium.dar
import 'package:syncrow_app/generated/assets.dart'; import 'package:syncrow_app/generated/assets.dart';
import 'package:syncrow_app/utils/resource_manager/color_manager.dart'; import 'package:syncrow_app/utils/resource_manager/color_manager.dart';
class TemporaryPasswordPage extends StatefulWidget { class TemporaryPasswordPage extends StatelessWidget {
const TemporaryPasswordPage({super.key}); final String? deviceId;
const TemporaryPasswordPage({super.key,this.deviceId});
@override
State<TemporaryPasswordPage> createState() => _TemporaryPasswordPageState();
}
class _TemporaryPasswordPageState extends State<TemporaryPasswordPage> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return DefaultScaffold( return DefaultScaffold(
@ -50,48 +46,15 @@ class _TemporaryPasswordPageState extends State<TemporaryPasswordPage> {
fontWeight: FontWeight.normal, fontWeight: FontWeight.normal,
), ),
onTap: () { onTap: () {
Navigator.of(context).push(MaterialPageRoute(builder: (context) => const ViewTemporaryPassword(),)); Navigator.of(context).push(MaterialPageRoute(builder: (context) => ViewTemporaryPassword(deviceId:deviceId,type:'Online Password'),));
}, },
trailing: const Icon( trailing: const Icon(
Icons.arrow_forward_ios, Icons.arrow_forward_ios,
color: ColorsManager.greyColor, color: ColorsManager.greyColor,
size: 15, size: 15,
), ),
), ),
// Row(
// children: [
// Expanded(
// child: Row(
// children: [
// SizedBox(
// width: 45,
// height: 40,
// child: SvgPicture.asset(
// Assets.timeLimitedPasswordIcon),
// ),
// const SizedBox(width: 15),
// const Expanded(
// child:
// BodyMedium(
// text: 'Time-Limited Password',
// fontWeight: FontWeight.normal,
// ),
// ),
// ],
// ),
// ),
// const Row(
// mainAxisAlignment: MainAxisAlignment.end,
// children: [
// Icon(
// Icons.arrow_forward_ios,
// color: ColorsManager.greyColor,
// size: 15,
// ),
// ],
// ),
// ],
// ),
), ),
], ],
), ),
@ -123,96 +86,35 @@ class _TemporaryPasswordPageState extends State<TemporaryPasswordPage> {
text: 'One-Time Password', text: 'One-Time Password',
fontWeight: FontWeight.normal, fontWeight: FontWeight.normal,
), ),
onTap: () {}, onTap: () {
Navigator.of(context).push(MaterialPageRoute(builder: (context) => ViewTemporaryPassword(deviceId:deviceId,type:'One-Time'),));
},
trailing: const Icon( trailing: const Icon(
Icons.arrow_forward_ios, Icons.arrow_forward_ios,
color: ColorsManager.greyColor, color: ColorsManager.greyColor,
size: 15, size: 15,
), ),
), ),
// Row(
// children: [
// Expanded(
// child: Row(
// children: [
// SizedBox(
// width: 45,
// height: 40,
// child: SvgPicture.asset(
// Assets.oneTimePassword),
// ),
// const SizedBox(width: 15),
// const Expanded(
// child: BodyMedium(
// text: 'One-Time Password',
// fontWeight: FontWeight.normal,
// ),
// ),
// ],
// ),
// ),
// const Row(
// mainAxisAlignment: MainAxisAlignment.end,
// children: [
// Icon(
// Icons.arrow_forward_ios,
// color: ColorsManager.greyColor,
// size: 15,
// ),
// ],
// ),
// ],
// ),
const Divider( ), const Divider( ),
ListTile( ListTile(
contentPadding: EdgeInsets.zero, contentPadding: EdgeInsets.zero,
leading: SvgPicture.asset( leading: SvgPicture.asset(
Assets.timeLimitedPassword), Assets.timeLimitedPassword),
title: BodyMedium( title: const BodyMedium(
text: 'Time-Limited Password', text: 'Time-Limited Password',
fontWeight: FontWeight.normal, fontWeight: FontWeight.normal,
), ),
onTap: () {}, onTap: () {
Navigator.of(context).push(MaterialPageRoute(builder: (context) => ViewTemporaryPassword(deviceId:deviceId,type:'Time-Limited'),));
},
trailing: const Icon( trailing: const Icon(
Icons.arrow_forward_ios, Icons.arrow_forward_ios,
color: ColorsManager.greyColor, color: ColorsManager.greyColor,
size: 15, size: 15,
), ),
), ),
// Row(
// children: [
// Expanded(
// child: Row(
// children: [
// SizedBox(
// width: 45,
// height: 40,
// child: SvgPicture.asset(
// Assets.timeLimitedPassword),
//
// ),
// const SizedBox(width: 15),
// const Expanded(
// child: BodyMedium(
// text: 'Time-Limited Password',
// fontWeight: FontWeight.normal,
// ),
// ),
// ],
// ),
// ),
// const Row(
// mainAxisAlignment: MainAxisAlignment.end,
// children: [
// Icon(
// Icons.arrow_forward_ios,
// color: ColorsManager.greyColor,
// size: 15,
// ),
// ],
// ),
// ],
// ),
],) ],)
), ),
], ],

View File

@ -1,45 +1,112 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_svg/svg.dart'; import 'package:flutter_svg/svg.dart';
import 'package:syncrow_app/features/devices/bloc/smart_door_bloc/smart_door_bloc.dart';
import 'package:syncrow_app/features/devices/bloc/smart_door_bloc/smart_door_event.dart';
import 'package:syncrow_app/features/devices/bloc/smart_door_bloc/smart_door_state.dart';
import 'package:syncrow_app/features/shared_widgets/default_container.dart';
import 'package:syncrow_app/features/shared_widgets/default_scaffold.dart'; import 'package:syncrow_app/features/shared_widgets/default_scaffold.dart';
import 'package:syncrow_app/features/shared_widgets/text_widgets/body_medium.dart'; import 'package:syncrow_app/features/shared_widgets/text_widgets/body_medium.dart';
import 'package:syncrow_app/features/shared_widgets/text_widgets/body_small.dart';
import 'package:syncrow_app/generated/assets.dart'; import 'package:syncrow_app/generated/assets.dart';
import 'package:syncrow_app/utils/resource_manager/color_manager.dart';
import 'create_temporary_password.dart'; import 'create_temporary_password.dart';
import 'door_dialog.dart';
class ViewTemporaryPassword extends StatelessWidget { class ViewTemporaryPassword extends StatelessWidget {
const ViewTemporaryPassword({super.key}); final String? deviceId;
final String? type;
const ViewTemporaryPassword({super.key, this.deviceId, this.type});
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return DefaultScaffold( return BlocProvider(
title: 'Passwords', create: (BuildContext context) =>
actions: [ SmartDoorBloc(deviceId: deviceId!)..add(InitialPasswordsPage(type:type )),
IconButton( child: BlocConsumer<SmartDoorBloc, SmartDoorState>(
onPressed: () { listener: (context, state) {
Navigator.of(context).push(MaterialPageRoute(builder: (context) => const CreateTemporaryPassword(),)); if (state is FailedState) {
ScaffoldMessenger.of(context).showSnackBar(
}, SnackBar(
icon: const Icon(Icons.add) content: Text(state.errorMessage),
) backgroundColor: Colors.red,
], ),
child: Container( );
child: Center( }
child:Column( }, builder: (context, state) {
crossAxisAlignment: CrossAxisAlignment.center, return DefaultScaffold(
mainAxisAlignment: MainAxisAlignment.center, title: 'Passwords',
children: [ actions: [
SvgPicture.asset( IconButton(
Assets.noValidPasswords onPressed: () {
), Navigator.of(context).push(MaterialPageRoute(
const SizedBox(height: 10,), builder: (context) =>
const BodyMedium(text: 'No Valid Passwords') CreateTemporaryPassword(deviceId: deviceId),
], ));
), },
), icon: const Icon(Icons.add))
) ],
);; child: Builder(
builder: (context) {
final smartDoorBloc = BlocProvider.of<SmartDoorBloc>(context);
return state is LoadingInitialState
? const Center(child: CircularProgressIndicator())
: Center(
child: smartDoorBloc.temporaryPasswords!.isNotEmpty
? ListView.builder(
itemCount: smartDoorBloc.temporaryPasswords!.length,
itemBuilder: (context, index) {
return Padding(
padding: const EdgeInsets.all(5.0),
child: DefaultContainer(
padding: const EdgeInsets.symmetric(
horizontal: 15, vertical: 10),
child: ListTile(
contentPadding: EdgeInsets.zero,
leading: SvgPicture.asset(
Assets.timeLimitedPasswordIcon),
title: BodyMedium(
text:
'Password Name: ${smartDoorBloc.temporaryPasswords![index].name}',
fontWeight: FontWeight.normal,
),
onTap: () async {
final result = await showDialog(
context: context,
builder: (context) {
return DoorDialog(
title: 'Password Information',
value: smartDoorBloc.temporaryPasswords![index],
);
},
);
if(result=='delete'){
smartDoorBloc.deletePassword(context, smartDoorBloc.temporaryPasswords![index].id.toString());
}
},
trailing: const Icon(
Icons.arrow_forward_ios,
color: ColorsManager.greyColor,
size: 15,
),
),
),
);
},
)
: Column(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
children: [
SvgPicture.asset(Assets.noValidPasswords),
const SizedBox(
height: 10,
),
const BodyMedium(text: 'No Valid Passwords')
],
),
);
},
));
}));
} }
} }

View File

@ -111,4 +111,24 @@ abstract class ApiEndpoints {
static const String editDevicePermission = '$baseUrl/device-permission/edit/{userId}'; static const String editDevicePermission = '$baseUrl/device-permission/edit/{userId}';
static const String assignDeviceToRoom = '$baseUrl/device/room'; static const String assignDeviceToRoom = '$baseUrl/device/room';
//////////////////////Door Lock //////////////////////
//online
static const String addTemporaryPassword = '$baseUrl/door-lock/temporary-password/online/{doorLockUuid}';
static const String getTemporaryPassword = '$baseUrl/door-lock/temporary-password/online/{doorLockUuid}';
//one-time offline
static const String addOneTimeTemporaryPassword = '$baseUrl/door-lock/temporary-password/offline/one-time/{doorLockUuid}';
static const String getOneTimeTemporaryPassword = '$baseUrl/door-lock/temporary-password/offline/one-time/{doorLockUuid}';
//multiple-time offline
static const String addMultipleTimeTemporaryPassword = '$baseUrl/door-lock/temporary-password/offline/multiple-time/{doorLockUuid}';
static const String getMultipleTimeTemporaryPassword = '$baseUrl/door-lock/temporary-password/offline/multiple-time/{doorLockUuid}';
//multiple-time offline
static const String deleteTemporaryPassword = '$baseUrl/door-lock/temporary-password/{doorLockUuid}/{passwordId}';
} }

View File

@ -1,4 +1,5 @@
import 'dart:async'; import 'dart:async';
import 'dart:convert';
import 'package:syncrow_app/features/devices/model/device_category_model.dart'; import 'package:syncrow_app/features/devices/model/device_category_model.dart';
import 'package:syncrow_app/features/devices/model/device_control_model.dart'; import 'package:syncrow_app/features/devices/model/device_control_model.dart';
@ -6,6 +7,8 @@ import 'package:syncrow_app/features/devices/model/device_model.dart';
import 'package:syncrow_app/services/api/api_links_endpoints.dart'; import 'package:syncrow_app/services/api/api_links_endpoints.dart';
import 'package:syncrow_app/services/api/http_service.dart'; import 'package:syncrow_app/services/api/http_service.dart';
import '../../features/devices/model/create_temporary_password_model.dart';
class DevicesAPI { class DevicesAPI {
static final HTTPService _httpService = HTTPService(); static final HTTPService _httpService = HTTPService();
@ -58,7 +61,8 @@ class DevicesAPI {
static Future<Map<String, dynamic>> getDeviceStatus(String deviceId) async { static Future<Map<String, dynamic>> getDeviceStatus(String deviceId) async {
final response = await _httpService.get( final response = await _httpService.get(
path: ApiEndpoints.deviceFunctionsStatus.replaceAll('{deviceUuid}', deviceId), path: ApiEndpoints.deviceFunctionsStatus
.replaceAll('{deviceUuid}', deviceId),
showServerMessage: false, showServerMessage: false,
expectedResponseModel: (json) { expectedResponseModel: (json) {
return json; return json;
@ -67,7 +71,8 @@ class DevicesAPI {
return response; return response;
} }
static Future<List<DeviceModel>> getDeviceByGroupName(String unitId, String groupName) async { static Future<List<DeviceModel>> getDeviceByGroupName(
String unitId, String groupName) async {
final response = await _httpService.get( final response = await _httpService.get(
path: ApiEndpoints.devicesByGroupName path: ApiEndpoints.devicesByGroupName
.replaceAll('{unitUuid}', unitId) .replaceAll('{unitUuid}', unitId)
@ -106,7 +111,8 @@ class DevicesAPI {
return response; return response;
} }
static Future<List<DeviceModel>> getDevicesByGatewayId(String gatewayId) async { static Future<List<DeviceModel>> getDevicesByGatewayId(
String gatewayId) async {
final response = await _httpService.get( final response = await _httpService.get(
path: ApiEndpoints.gatewayApi.replaceAll('{gatewayUuid}', gatewayId), path: ApiEndpoints.gatewayApi.replaceAll('{gatewayUuid}', gatewayId),
showServerMessage: false, showServerMessage: false,
@ -123,4 +129,79 @@ class DevicesAPI {
); );
return response; return response;
} }
static Future getTemporaryPasswords(String deviceId, pageType) async {
final response = await _httpService.get(
path: pageType == 'One-Time'
? ApiEndpoints.getOneTimeTemporaryPassword
.replaceAll('{doorLockUuid}', deviceId)
: pageType == 'Online Password'
? ApiEndpoints.getTemporaryPassword
.replaceAll('{doorLockUuid}', deviceId)
: ApiEndpoints.getMultipleTimeTemporaryPassword
.replaceAll('{doorLockUuid}', deviceId),
showServerMessage: false,
expectedResponseModel: (json) {
return json;
},
);
return response;
}
//create password
static Future createPassword({
required String name,
required String password,
required String effectiveTime,
required String invalidTime,
required String deviceId,
required String pageType,
List<Schedule>? scheduleList,
}) async {
try {
String endpointPath;
if (pageType == 'Online Password') {
endpointPath = ApiEndpoints.addTemporaryPassword
.replaceAll('{doorLockUuid}', deviceId);
} else if (pageType == 'One-Time') {
endpointPath = ApiEndpoints.addOneTimeTemporaryPassword
.replaceAll('{doorLockUuid}', deviceId);
} else {
endpointPath = ApiEndpoints.addMultipleTimeTemporaryPassword
.replaceAll('{doorLockUuid}', deviceId);
}
Map<String, dynamic> body = {
"name": name,
"password": password,
"effectiveTime": effectiveTime,
"invalidTime": invalidTime,
};
if (pageType == 'Online Password' && scheduleList != null) {
body["scheduleList"] =
scheduleList.map((schedule) => schedule.toJson()).toList();
}
final response = await _httpService.post(
path: endpointPath,
body: body,
showServerMessage: false,
expectedResponseModel: (json) => json,
);
return response;
} catch (e) {}
}
static Future<Map<String, dynamic>> deletePassword(
{required String passwordId, required String deviceId}) async {
final response = await _httpService.delete(
path: ApiEndpoints.deleteTemporaryPassword
.replaceAll('{doorLockUuid}', deviceId)
.replaceAll('{passwordId}', passwordId),
showServerMessage: false,
expectedResponseModel: (json) {
return json;
},
);
return response;
}
} }