diff --git a/lib/features/devices/bloc/smart_door_bloc/smart_door_bloc.dart b/lib/features/devices/bloc/smart_door_bloc/smart_door_bloc.dart index 29d8a80..01f4035 100644 --- a/lib/features/devices/bloc/smart_door_bloc/smart_door_bloc.dart +++ b/lib/features/devices/bloc/smart_door_bloc/smart_door_bloc.dart @@ -1,68 +1,30 @@ import 'dart:math'; - import 'package:day_picker/model/day_in_week.dart'; import 'package:flutter/material.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_state.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/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'; class SmartDoorBloc extends Bloc { final String deviceId; late SmartDoorModel deviceStatus; - 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? 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; - } - - + static String pageType = ''; SmartDoorBloc({required this.deviceId}) : super(InitialState()) { on(_fetchSmartDoorStatus); + on(getTemporaryPasswords); on(_updateLock); } - - void _fetchSmartDoorStatus( - InitialEvent event, Emitter emit) async { - emit(LoadingInitialState()); + void _fetchSmartDoorStatus(InitialEvent event, Emitter emit) async { try { + emit(LoadingInitialState()); var response = await DevicesAPI.getDeviceStatus(deviceId); List statusModelList = []; for (var status in response['status']) { @@ -76,6 +38,64 @@ class SmartDoorBloc extends Bloc { } } + void getTemporaryPasswords(InitialPasswordsPage event, Emitter 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? selectedDay; + DateTime? startTime; + DateTime? endTime; + List? temporaryPasswords=[]; + List? 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 emit) async { emit(LoadingNewSate(smartDoorModel: deviceStatus)); try { @@ -97,8 +117,8 @@ class SmartDoorBloc extends Bloc { emit(LoadingInitialState()); passwordController.clear(); Random random = Random(); - int min = 1000000; // Minimum 7-digit number - int max = 9999999; // Maximum 7-digit number + int min = 1000000; + int max = 9999999; passwordController.text = (min + random.nextInt(max - min + 1)).toString(); emit(GeneratePasswordState()); } @@ -124,34 +144,18 @@ class SmartDoorBloc extends Bloc { timePicked.minute, ); if (isEffective) { - if (expirationTimeTimeStamp != null && - selectedDateTime.millisecondsSinceEpoch > - expirationTimeTimeStamp!) { - // Show error message - ScaffoldMessenger.of(context).showSnackBar( - const SnackBar( - content: Text( - 'Effective Time cannot be later than Expiration Time.'), - ), - ); + if (expirationTimeTimeStamp != null && selectedDateTime.millisecondsSinceEpoch > expirationTimeTimeStamp!) { + _showSnackBar(context, 'Effective Time cannot be later than Expiration Time.'); } else { effectiveTime = selectedDateTime.toString(); - effectiveTimeTimeStamp = selectedDateTime.millisecondsSinceEpoch; + effectiveTimeTimeStamp = selectedDateTime.millisecondsSinceEpoch; } } else { - if (effectiveTimeTimeStamp != null && - selectedDateTime.millisecondsSinceEpoch < - effectiveTimeTimeStamp!) { - // Show error message - ScaffoldMessenger.of(context).showSnackBar( - const SnackBar( - content: Text( - 'Expiration Time cannot be earlier than Effective Time.'), - ), - ); + if (effectiveTimeTimeStamp != null && selectedDateTime.millisecondsSinceEpoch < effectiveTimeTimeStamp!) { + _showSnackBar(context, 'Expiration Time cannot be earlier than Effective Time.'); } else { expirationTime = selectedDateTime.toString(); - expirationTimeTimeStamp = selectedDateTime.millisecondsSinceEpoch; + expirationTimeTimeStamp = selectedDateTime.millisecondsSinceEpoch; } } emit(TimeSelectedState()); @@ -160,74 +164,98 @@ class SmartDoorBloc extends Bloc { } - Future saveFunction(BuildContext context,) async { - if (passwordController.text.isEmpty) { - ScaffoldMessenger.of(context).showSnackBar( - const SnackBar( - content: Text('Password name required'), - backgroundColor: Colors.red, - ), + Future savePassword(BuildContext context) async { + if (_validateInputs(context)) return; + try { + emit(LoadingInitialState()); + var response = await DevicesAPI.createPassword( + 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; - } - if (passwordNameController.text.isEmpty) { - ScaffoldMessenger.of(context).showSnackBar( - const SnackBar( - content: Text('Password name required'), - 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; + add(InitialPasswordsPage(type: pageType)); + emit(TemporaryPasswordsLoadedState(temporaryPassword: temporaryPasswords!)); + Navigator.pop(context); + Navigator.pop(context); + } catch (e) { + emit(FailedState(errorMessage: 'Failed to save password: $e')); } } + + + 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 days = [ - DayInWeek( - "Sun", - dayKey: 'Sun', - ), - DayInWeek( - "Mon", - dayKey: 'Mon', - ), - DayInWeek( - "Tue", - isSelected: true, - dayKey: 'Tue' - ), - DayInWeek( - "Wed", - dayKey: 'Wed', - ), - DayInWeek( - "Thu", - dayKey: 'Thu', - ), - DayInWeek( - "Fri", - dayKey: 'Fri', - ), - DayInWeek( - "Sat", - dayKey: 'Sat', - ), + DayInWeek("Sun", dayKey: 'Sun'), + DayInWeek("Mon", dayKey: 'Mon'), + DayInWeek("Tue", 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); + } + } diff --git a/lib/features/devices/bloc/smart_door_bloc/smart_door_event.dart b/lib/features/devices/bloc/smart_door_bloc/smart_door_event.dart index 146931e..0a481ab 100644 --- a/lib/features/devices/bloc/smart_door_bloc/smart_door_event.dart +++ b/lib/features/devices/bloc/smart_door_bloc/smart_door_event.dart @@ -8,6 +8,11 @@ abstract class SmartDoorEvent extends Equatable { } class InitialEvent extends SmartDoorEvent {} +class InitialPasswordsPage extends SmartDoorEvent { + final String? type; + const InitialPasswordsPage({ this.type}); +} +class InitialOneTimePassword extends SmartDoorEvent {} class UpdateLockEvent extends SmartDoorEvent { final bool value; diff --git a/lib/features/devices/bloc/smart_door_bloc/smart_door_state.dart b/lib/features/devices/bloc/smart_door_bloc/smart_door_state.dart index 447ab9a..a672a06 100644 --- a/lib/features/devices/bloc/smart_door_bloc/smart_door_state.dart +++ b/lib/features/devices/bloc/smart_door_bloc/smart_door_state.dart @@ -1,5 +1,6 @@ import 'package:equatable/equatable.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 { const SmartDoorState(); @@ -44,3 +45,7 @@ class IsStartEndState extends SmartDoorState{} class changeStartTimeState extends SmartDoorState{} class changeEndTimeState extends SmartDoorState{} class changeTimeState extends SmartDoorState{} +class TemporaryPasswordsLoadedState extends SmartDoorState{ + final List temporaryPassword; + const TemporaryPasswordsLoadedState({required this.temporaryPassword}); +} diff --git a/lib/features/devices/model/create_temporary_password_model.dart b/lib/features/devices/model/create_temporary_password_model.dart new file mode 100644 index 0000000..fe94079 --- /dev/null +++ b/lib/features/devices/model/create_temporary_password_model.dart @@ -0,0 +1,68 @@ + + + +class CreateTemporaryPasswordModel{ + final String name; + late final String password; + late final String effectiveTime; + late final String invalidTime; + List? scheduleList; + + CreateTemporaryPasswordModel({ + required this.name, + required this.password, + required this.effectiveTime, + required this.invalidTime, + this.scheduleList, + }); + + factory CreateTemporaryPasswordModel.fromJson(Map 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 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 workingDay; + + Schedule({ + required this.effectiveTime, + required this.invalidTime, + required this.workingDay, + }); + + factory Schedule.fromJson(Map json) { + return Schedule( + effectiveTime: json['effectiveTime'], + invalidTime: json['invalidTime'], + workingDay: List.from(json['workingDay']), + ); + } + + Map toJson() { + return { + 'effectiveTime': effectiveTime, + 'invalidTime': invalidTime, + 'workingDay': workingDay, + }; + } +} diff --git a/lib/features/devices/model/temporary_password_model.dart b/lib/features/devices/model/temporary_password_model.dart new file mode 100644 index 0000000..40cda2c --- /dev/null +++ b/lib/features/devices/model/temporary_password_model.dart @@ -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 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'], + ); + } +} diff --git a/lib/features/devices/view/widgets/ceiling_sensor/ceiling_sensor_interface.dart b/lib/features/devices/view/widgets/ceiling_sensor/ceiling_sensor_interface.dart index 5b2db52..bfc050f 100644 --- a/lib/features/devices/view/widgets/ceiling_sensor/ceiling_sensor_interface.dart +++ b/lib/features/devices/view/widgets/ceiling_sensor/ceiling_sensor_interface.dart @@ -240,8 +240,7 @@ class CeilingSensorInterface extends StatelessWidget { padding: const EdgeInsets.symmetric(vertical: 12, horizontal: 20), onTap: () async { - if (ceilingSensorButtons()[index]['title'] == - 'Sensitivity') { + if (ceilingSensorButtons()[index]['title'] == 'Sensitivity') { final result = await showDialog( context: context, builder: (context) { diff --git a/lib/features/devices/view/widgets/smart_door/create_temporary_password.dart b/lib/features/devices/view/widgets/smart_door/create_temporary_password.dart index 93856b2..4c20bfd 100644 --- a/lib/features/devices/view/widgets/smart_door/create_temporary_password.dart +++ b/lib/features/devices/view/widgets/smart_door/create_temporary_password.dart @@ -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:time_picker_spinner/time_picker_spinner.dart'; -class CreateTemporaryPassword extends StatefulWidget { - const CreateTemporaryPassword({super.key}); +class CreateTemporaryPassword extends StatelessWidget { + final String? deviceId; - @override - State createState() => - _CreateTemporaryPasswordState(); -} + const CreateTemporaryPassword({super.key,this.deviceId}); -class _CreateTemporaryPasswordState extends State { @override Widget build(BuildContext context) { return BlocProvider( - create: (BuildContext context) => SmartDoorBloc(deviceId: ''), + create: (BuildContext context) => SmartDoorBloc(deviceId: deviceId!), child: BlocConsumer( listener: (context, state) { + print(state); if (state is FailedState) { ScaffoldMessenger.of(context).showSnackBar( SnackBar( @@ -34,18 +31,22 @@ class _CreateTemporaryPasswordState extends State { ), ); } - }, builder: (context, state) { + + + }, builder: (context, state) { return DefaultScaffold( title: 'Create Password', actions: [ TextButton( onPressed: () { - BlocProvider.of(context).saveFunction(context); + BlocProvider.of(context).savePassword(context); }, child: const Text('Save')) ], - child: SingleChildScrollView( - child: Column( + child: state is LoadingInitialState? + const Center(child: CircularProgressIndicator()): SingleChildScrollView( + child: + Column( mainAxisAlignment: MainAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start, children: [ @@ -64,8 +65,7 @@ class _CreateTemporaryPasswordState extends State { fontColor: ColorsManager.grayColor, ), DefaultContainer( - padding: - const EdgeInsets.symmetric(horizontal: 5, vertical: 5), + padding: const EdgeInsets.symmetric(horizontal: 15, vertical: 15), child: Row( children: [ Expanded( @@ -125,8 +125,7 @@ class _CreateTemporaryPasswordState extends State { width: MediaQuery.of(context).size.width / 2, child: TextFormField( controller: - BlocProvider.of(context) - .passwordNameController, + BlocProvider.of(context).passwordNameController, decoration: const InputDecoration( labelText: 'Enter The Name'), )), @@ -146,8 +145,8 @@ class _CreateTemporaryPasswordState extends State { .selectTime(context, isEffective: true); }, child: Text( - BlocProvider.of(context) - .effectiveTime), + BlocProvider.of(context).effectiveTime + ), )), ), const Divider(), @@ -194,7 +193,7 @@ class _CreateTemporaryPasswordState extends State { BlocProvider.of(context).repeat, onChanged: (value) { BlocProvider.of(context) - .repeatFunction(); + .toggleRepeat(); }, applyTheme: true, )), @@ -217,14 +216,14 @@ class _CreateTemporaryPasswordState extends State { children: [ InkWell( onTap: () { - BlocProvider.of(context).isStartEndTimeFun(true); + BlocProvider.of(context).setStartEndTime(true); }, child: BodyMedium(text: 'Start',fontColor:BlocProvider.of(context).isStartEndTime==false? Colors.black:Colors.blue,fontSize: 18,), ), InkWell( onTap: () { - BlocProvider.of(context).isStartEndTimeFun(false); + BlocProvider.of(context).setStartEndTime(false); }, child: BodyMedium(text: 'End',fontColor:BlocProvider.of(context).isStartEndTime? Colors.black:Colors.blue,fontSize: 18,), ) @@ -234,10 +233,10 @@ class _CreateTemporaryPasswordState extends State { const Divider(), Container( height: 80, - child: BlocProvider.of(context).isStartEndTime? + child: Container( height: 90, - child: TimePickerSpinner( + child:BlocProvider.of(context).isStartEndTime? TimePickerSpinner( time: BlocProvider.of(context).startTime, is24HourMode: false, @@ -248,30 +247,24 @@ class _CreateTemporaryPasswordState extends State { highlightedTextStyle: const TextStyle( fontSize: 30, color: Colors.blue), onTimeChange: (time) { - BlocProvider.of(context).startTime=time; BlocProvider.of(context).changeTime(time, BlocProvider.of(context).isStartEndTime); - }, + ): Container( + child: TimePickerSpinner( + time: BlocProvider.of(context).endTime, + is24HourMode: false, + itemHeight: 40, + normalTextStyle: const TextStyle( + fontSize: 24, + ), + highlightedTextStyle: const TextStyle( + fontSize: 30, color: Colors.blue), + onTimeChange: (time) { + BlocProvider.of(context).changeTime(time, BlocProvider.of(context).isStartEndTime); + }, + ), ), - ): - TimePickerSpinner( - time: BlocProvider.of(context).endTime, - is24HourMode: false, - itemHeight: 40, - normalTextStyle: const TextStyle( - fontSize: 24, - ), - highlightedTextStyle: const TextStyle( - fontSize: 24, color: Colors.blue), - onTimeChange: (time) { - setState(() { - BlocProvider.of(context).endTime=time; - BlocProvider.of(context).changeTime(time, BlocProvider.of(context).isStartEndTime); - - // dateTime = time; - }); - }, - ), + ) ), const Divider(), const SizedBox(height: 20), @@ -288,9 +281,7 @@ class _CreateTemporaryPasswordState extends State { borderRadius: BorderRadius.circular(30.0), color: Colors.white), onSelect: (values) { - print(values); - BlocProvider.of(context) - .selectedDay = values; + BlocProvider.of(context).selectedDay = values; }, ), ], diff --git a/lib/features/devices/view/widgets/smart_door/door_dialog.dart b/lib/features/devices/view/widgets/smart_door/door_dialog.dart new file mode 100644 index 0000000..022a20e --- /dev/null +++ b/lib/features/devices/view/widgets/smart_door/door_dialog.dart @@ -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 { + @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), + ), + ), + ), + ], + ) + ], + ), + ), + ); + } +} diff --git a/lib/features/devices/view/widgets/smart_door/door_grid.dart b/lib/features/devices/view/widgets/smart_door/door_grid.dart index d3542b3..fc5c290 100644 --- a/lib/features/devices/view/widgets/smart_door/door_grid.dart +++ b/lib/features/devices/view/widgets/smart_door/door_grid.dart @@ -10,9 +10,9 @@ import 'package:syncrow_app/features/shared_widgets/text_widgets/body_small.dart import 'package:syncrow_app/generated/assets.dart'; class DoorLockGrid extends StatelessWidget { - const DoorLockGrid({ - super.key, - }); + String uuid; + DoorLockGrid({ + super.key,required this.uuid}); @override Widget build(BuildContext context) { @@ -29,10 +29,10 @@ class DoorLockGrid extends StatelessWidget { itemBuilder: (context, index) => DefaultContainer( onTap: () { //TODO: remove checking after adding the pages - doorLockButtons[index]['page'] != null + doorLockButtons()[index]['page'] != null ? Navigator.of(context).push( MaterialPageRoute( - builder: (context) => doorLockButtons[index]['page'] as Widget, + builder: (context) => doorLockButtons(val: uuid)[index]['page'] as Widget, ), ) : null; @@ -44,7 +44,7 @@ class DoorLockGrid extends StatelessWidget { ConstrainedBox( constraints: const BoxConstraints(maxHeight: 46, maxWidth: 50), child: SvgPicture.asset( - doorLockButtons[index]['image'] as String, + doorLockButtons()[index]['image'] as String, ), ), const SizedBox( @@ -53,7 +53,7 @@ class DoorLockGrid extends StatelessWidget { Flexible( child: FittedBox( child: BodySmall( - text: doorLockButtons[index]['title'] as String, + text: doorLockButtons()[index]['title'] as String, // doorLockButtons.keys.elementAt(index), textAlign: TextAlign.center, ), @@ -66,7 +66,7 @@ class DoorLockGrid extends StatelessWidget { } } -List> doorLockButtons = [ +List> doorLockButtons({val}) => [ { 'title': 'Unlocking Records', 'image': Assets.assetsIconsDoorlockAssetsUnlockingRecords, @@ -80,7 +80,7 @@ List> doorLockButtons = [ { 'title': 'Temporary Password', 'image': Assets.assetsIconsDoorlockAssetsTemporaryPassword, - 'page':const TemporaryPasswordPage() , + 'page': TemporaryPasswordPage(deviceId:val) , }, { 'title': 'Smart Linkage', diff --git a/lib/features/devices/view/widgets/smart_door/door_interface.dart b/lib/features/devices/view/widgets/smart_door/door_interface.dart index 2d9620f..b78378f 100644 --- a/lib/features/devices/view/widgets/smart_door/door_interface.dart +++ b/lib/features/devices/view/widgets/smart_door/door_interface.dart @@ -111,7 +111,7 @@ class DoorInterface extends StatelessWidget { doorLock: doorLock, smartDoorModel: smartDoorModel, ), - const DoorLockGrid(), + DoorLockGrid( uuid: doorLock.uuid!, ), ], ) diff --git a/lib/features/devices/view/widgets/smart_door/temporary_password_page.dart b/lib/features/devices/view/widgets/smart_door/temporary_password_page.dart index 4cf1920..7efadb7 100644 --- a/lib/features/devices/view/widgets/smart_door/temporary_password_page.dart +++ b/lib/features/devices/view/widgets/smart_door/temporary_password_page.dart @@ -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/utils/resource_manager/color_manager.dart'; -class TemporaryPasswordPage extends StatefulWidget { - const TemporaryPasswordPage({super.key}); +class TemporaryPasswordPage extends StatelessWidget { + final String? deviceId; + const TemporaryPasswordPage({super.key,this.deviceId}); - @override - State createState() => _TemporaryPasswordPageState(); -} - -class _TemporaryPasswordPageState extends State { @override Widget build(BuildContext context) { return DefaultScaffold( @@ -50,48 +46,15 @@ class _TemporaryPasswordPageState extends State { fontWeight: FontWeight.normal, ), 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( + Icons.arrow_forward_ios, color: ColorsManager.greyColor, 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 { text: 'One-Time Password', fontWeight: FontWeight.normal, ), - onTap: () {}, + onTap: () { + Navigator.of(context).push(MaterialPageRoute(builder: (context) => ViewTemporaryPassword(deviceId:deviceId,type:'One-Time'),)); + + }, trailing: const Icon( Icons.arrow_forward_ios, color: ColorsManager.greyColor, 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( ), ListTile( contentPadding: EdgeInsets.zero, leading: SvgPicture.asset( Assets.timeLimitedPassword), - title: BodyMedium( - text: 'Time-Limited Password', - fontWeight: FontWeight.normal, - ), - onTap: () {}, + title: const BodyMedium( + text: 'Time-Limited Password', + fontWeight: FontWeight.normal, + ), + onTap: () { + Navigator.of(context).push(MaterialPageRoute(builder: (context) => ViewTemporaryPassword(deviceId:deviceId,type:'Time-Limited'),)); + }, trailing: const Icon( Icons.arrow_forward_ios, color: ColorsManager.greyColor, 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, - // ), - // ], - // ), - // ], - // ), ],) ), ], diff --git a/lib/features/devices/view/widgets/smart_door/view_temporary_password.dart b/lib/features/devices/view/widgets/smart_door/view_temporary_password.dart index b65641d..8dede56 100644 --- a/lib/features/devices/view/widgets/smart_door/view_temporary_password.dart +++ b/lib/features/devices/view/widgets/smart_door/view_temporary_password.dart @@ -1,45 +1,112 @@ - import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.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/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/utils/resource_manager/color_manager.dart'; import 'create_temporary_password.dart'; - +import 'door_dialog.dart'; class ViewTemporaryPassword extends StatelessWidget { - const ViewTemporaryPassword({super.key}); + final String? deviceId; + final String? type; + const ViewTemporaryPassword({super.key, this.deviceId, this.type}); @override Widget build(BuildContext context) { - return DefaultScaffold( - title: 'Passwords', - actions: [ - IconButton( - onPressed: () { - Navigator.of(context).push(MaterialPageRoute(builder: (context) => const CreateTemporaryPassword(),)); - - }, - icon: const Icon(Icons.add) - ) - ], - child: Container( - child: Center( - child:Column( - crossAxisAlignment: CrossAxisAlignment.center, - mainAxisAlignment: MainAxisAlignment.center, - children: [ - SvgPicture.asset( - Assets.noValidPasswords - ), - const SizedBox(height: 10,), - const BodyMedium(text: 'No Valid Passwords') - ], - ), - ), - ) - );; + return BlocProvider( + create: (BuildContext context) => + SmartDoorBloc(deviceId: deviceId!)..add(InitialPasswordsPage(type:type )), + child: BlocConsumer( + listener: (context, state) { + if (state is FailedState) { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Text(state.errorMessage), + backgroundColor: Colors.red, + ), + ); + } + }, builder: (context, state) { + return DefaultScaffold( + title: 'Passwords', + actions: [ + IconButton( + onPressed: () { + Navigator.of(context).push(MaterialPageRoute( + builder: (context) => + CreateTemporaryPassword(deviceId: deviceId), + )); + }, + icon: const Icon(Icons.add)) + ], + child: Builder( + builder: (context) { + final smartDoorBloc = BlocProvider.of(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') + ], + ), + ); + }, + )); + })); } } diff --git a/lib/services/api/api_links_endpoints.dart b/lib/services/api/api_links_endpoints.dart index 3f6476d..9c50dc6 100644 --- a/lib/services/api/api_links_endpoints.dart +++ b/lib/services/api/api_links_endpoints.dart @@ -111,4 +111,24 @@ abstract class ApiEndpoints { static const String editDevicePermission = '$baseUrl/device-permission/edit/{userId}'; 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}'; + + } diff --git a/lib/services/api/devices_api.dart b/lib/services/api/devices_api.dart index aebbe94..a9a7b82 100644 --- a/lib/services/api/devices_api.dart +++ b/lib/services/api/devices_api.dart @@ -1,4 +1,5 @@ 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_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/http_service.dart'; +import '../../features/devices/model/create_temporary_password_model.dart'; + class DevicesAPI { static final HTTPService _httpService = HTTPService(); @@ -58,7 +61,8 @@ class DevicesAPI { static Future> getDeviceStatus(String deviceId) async { final response = await _httpService.get( - path: ApiEndpoints.deviceFunctionsStatus.replaceAll('{deviceUuid}', deviceId), + path: ApiEndpoints.deviceFunctionsStatus + .replaceAll('{deviceUuid}', deviceId), showServerMessage: false, expectedResponseModel: (json) { return json; @@ -67,7 +71,8 @@ class DevicesAPI { return response; } - static Future> getDeviceByGroupName(String unitId, String groupName) async { + static Future> getDeviceByGroupName( + String unitId, String groupName) async { final response = await _httpService.get( path: ApiEndpoints.devicesByGroupName .replaceAll('{unitUuid}', unitId) @@ -106,7 +111,8 @@ class DevicesAPI { return response; } - static Future> getDevicesByGatewayId(String gatewayId) async { + static Future> getDevicesByGatewayId( + String gatewayId) async { final response = await _httpService.get( path: ApiEndpoints.gatewayApi.replaceAll('{gatewayUuid}', gatewayId), showServerMessage: false, @@ -123,4 +129,79 @@ class DevicesAPI { ); 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? 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 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> 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; + } }