diff --git a/assets/icons/sos.svg b/assets/icons/sos.svg new file mode 100644 index 00000000..887e9f19 --- /dev/null +++ b/assets/icons/sos.svg @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/assets/icons/sos_normal.svg b/assets/icons/sos_normal.svg new file mode 100644 index 00000000..ea795024 --- /dev/null +++ b/assets/icons/sos_normal.svg @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/lib/pages/device_managment/all_devices/helper/route_controls_based_code.dart b/lib/pages/device_managment/all_devices/helper/route_controls_based_code.dart index 8edb0a1e..e38ac582 100644 --- a/lib/pages/device_managment/all_devices/helper/route_controls_based_code.dart +++ b/lib/pages/device_managment/all_devices/helper/route_controls_based_code.dart @@ -20,6 +20,8 @@ import 'package:syncrow_web/pages/device_managment/one_gang_switch/view/wall_lig import 'package:syncrow_web/pages/device_managment/one_gang_switch/view/wall_light_device_control.dart'; import 'package:syncrow_web/pages/device_managment/power_clamp/view/power_clamp_batch_control_view.dart'; import 'package:syncrow_web/pages/device_managment/power_clamp/view/smart_power_device_control.dart'; +import 'package:syncrow_web/pages/device_managment/sos/view/sos_batch_control_view.dart'; +import 'package:syncrow_web/pages/device_managment/sos/view/sos_device_control_view.dart'; import 'package:syncrow_web/pages/device_managment/three_g_glass_switch/view/three_gang_glass_switch_batch_control_view.dart'; import 'package:syncrow_web/pages/device_managment/three_g_glass_switch/view/three_gang_glass_switch_control_view.dart'; import 'package:syncrow_web/pages/device_managment/three_gang_switch/view/living_room_batch_controls.dart'; @@ -100,6 +102,8 @@ mixin RouteControlsBasedCode { return SmartPowerDeviceControl( deviceId: device.uuid!, ); + case 'SOS': + return SosDeviceControlsView(device: device); default: return const SizedBox(); } @@ -123,119 +127,72 @@ mixin RouteControlsBasedCode { switch (devices.first.productType) { case '1G': return WallLightBatchControlView( - deviceIds: devices - .where((e) => (e.productType == '1G')) - .map((e) => e.uuid!) - .toList(), + deviceIds: devices.where((e) => (e.productType == '1G')).map((e) => e.uuid!).toList(), ); case '2G': return TwoGangBatchControlView( - deviceIds: devices - .where((e) => (e.productType == '2G')) - .map((e) => e.uuid!) - .toList(), + deviceIds: devices.where((e) => (e.productType == '2G')).map((e) => e.uuid!).toList(), ); case '3G': return LivingRoomBatchControlsView( - deviceIds: devices - .where((e) => (e.productType == '3G')) - .map((e) => e.uuid!) - .toList(), + deviceIds: devices.where((e) => (e.productType == '3G')).map((e) => e.uuid!).toList(), ); case '1GT': return OneGangGlassSwitchBatchControlView( - deviceIds: devices - .where((e) => (e.productType == '1GT')) - .map((e) => e.uuid!) - .toList(), + deviceIds: devices.where((e) => (e.productType == '1GT')).map((e) => e.uuid!).toList(), ); case '2GT': return TwoGangGlassSwitchBatchControlView( - deviceIds: devices - .where((e) => (e.productType == '2GT')) - .map((e) => e.uuid!) - .toList(), + deviceIds: devices.where((e) => (e.productType == '2GT')).map((e) => e.uuid!).toList(), ); case '3GT': return ThreeGangGlassSwitchBatchControlView( - deviceIds: devices - .where((e) => (e.productType == '3GT')) - .map((e) => e.uuid!) - .toList(), + deviceIds: devices.where((e) => (e.productType == '3GT')).map((e) => e.uuid!).toList(), ); case 'GW': return GatewayBatchControlView( - gatewayIds: devices - .where((e) => (e.productType == 'GW')) - .map((e) => e.uuid!) - .toList(), + gatewayIds: devices.where((e) => (e.productType == 'GW')).map((e) => e.uuid!).toList(), ); case 'DL': return DoorLockBatchControlView( - devicesIds: devices - .where((e) => (e.productType == 'DL')) - .map((e) => e.uuid!) - .toList()); + devicesIds: devices.where((e) => (e.productType == 'DL')).map((e) => e.uuid!).toList()); case 'WPS': return WallSensorBatchControlView( - devicesIds: devices - .where((e) => (e.productType == 'WPS')) - .map((e) => e.uuid!) - .toList()); + devicesIds: devices.where((e) => (e.productType == 'WPS')).map((e) => e.uuid!).toList()); case 'CPS': return CeilingSensorBatchControlView( - devicesIds: devices - .where((e) => (e.productType == 'CPS')) - .map((e) => e.uuid!) - .toList(), + devicesIds: devices.where((e) => (e.productType == 'CPS')).map((e) => e.uuid!).toList(), ); case 'CUR': return CurtainBatchStatusView( - devicesIds: devices - .where((e) => (e.productType == 'CUR')) - .map((e) => e.uuid!) - .toList(), + devicesIds: devices.where((e) => (e.productType == 'CUR')).map((e) => e.uuid!).toList(), ); case 'AC': return AcDeviceBatchControlView( - devicesIds: devices - .where((e) => (e.productType == 'AC')) - .map((e) => e.uuid!) - .toList()); + devicesIds: devices.where((e) => (e.productType == 'AC')).map((e) => e.uuid!).toList()); case 'WH': return WaterHEaterBatchControlView( - deviceIds: devices - .where((e) => (e.productType == 'WH')) - .map((e) => e.uuid!) - .toList(), + deviceIds: devices.where((e) => (e.productType == 'WH')).map((e) => e.uuid!).toList(), ); case 'DS': return MainDoorSensorBatchView( - devicesIds: devices - .where((e) => (e.productType == 'DS')) - .map((e) => e.uuid!) - .toList(), + devicesIds: devices.where((e) => (e.productType == 'DS')).map((e) => e.uuid!).toList(), ); case 'GD': return GarageDoorBatchControlView( - deviceIds: devices - .where((e) => (e.productType == 'GD')) - .map((e) => e.uuid!) - .toList(), + deviceIds: devices.where((e) => (e.productType == 'GD')).map((e) => e.uuid!).toList(), ); case 'WL': return WaterLeakBatchControlView( - deviceIds: devices - .where((e) => (e.productType == 'WL')) - .map((e) => e.uuid!) - .toList(), + deviceIds: devices.where((e) => (e.productType == 'WL')).map((e) => e.uuid!).toList(), ); case 'PC': return PowerClampBatchControlView( - deviceIds: devices - .where((e) => (e.productType == 'PC')) - .map((e) => e.uuid!) - .toList(), + deviceIds: devices.where((e) => (e.productType == 'PC')).map((e) => e.uuid!).toList(), + ); + case 'SOS': + return SOSBatchControlView( + deviceIds: devices.where((e) => (e.productType == 'SOS')).map((e) => e.uuid!).toList(), ); default: return const SizedBox(); diff --git a/lib/pages/device_managment/garage_door/view/garage_door_control_view.dart b/lib/pages/device_managment/garage_door/view/garage_door_control_view.dart index e3c44405..886ca9ae 100644 --- a/lib/pages/device_managment/garage_door/view/garage_door_control_view.dart +++ b/lib/pages/device_managment/garage_door/view/garage_door_control_view.dart @@ -6,6 +6,7 @@ import 'package:syncrow_web/pages/device_managment/garage_door/bloc/garage_door_ import 'package:syncrow_web/pages/device_managment/garage_door/helper/garage_door_helper.dart'; import 'package:syncrow_web/pages/device_managment/garage_door/models/garage_door_model.dart'; import 'package:syncrow_web/pages/device_managment/garage_door/widgets/schedule_garage_view.dart'; +import 'package:syncrow_web/pages/device_managment/shared/icon_name_status_container.dart'; import 'package:syncrow_web/pages/device_managment/shared/table/report_table.dart'; import 'package:syncrow_web/pages/device_managment/shared/toggle_widget.dart'; import 'package:syncrow_web/utils/color_manager.dart'; @@ -13,10 +14,7 @@ import 'package:syncrow_web/utils/constants/assets.dart'; import 'package:syncrow_web/utils/extension/build_context_x.dart'; import 'package:syncrow_web/utils/helpers/responsice_layout_helper/responsive_layout_helper.dart'; -import '../../main_door_sensor/view/main_door_control_view.dart'; - -class GarageDoorControlView extends StatelessWidget - with HelperResponsiveLayout { +class GarageDoorControlView extends StatelessWidget with HelperResponsiveLayout { final String deviceId; const GarageDoorControlView({required this.deviceId, super.key}); @@ -24,8 +22,7 @@ class GarageDoorControlView extends StatelessWidget @override Widget build(BuildContext context) { return BlocProvider( - create: (context) => GarageDoorBloc(deviceId: deviceId) - ..add(GarageDoorInitialEvent(deviceId)), + create: (context) => GarageDoorBloc(deviceId: deviceId)..add(GarageDoorInitialEvent(deviceId)), child: BlocBuilder( builder: (context, state) { if (state is GarageDoorLoadingState) { @@ -37,9 +34,7 @@ class GarageDoorControlView extends StatelessWidget garageDoorSensor: true, onRowTap: (index) {}, onClose: () { - context - .read() - .add(BackToGarageDoorGridViewEvent()); + context.read().add(BackToGarageDoorGridViewEvent()); }, ); } else if (state is GarageDoorLoadedState) { @@ -80,10 +75,7 @@ class GarageDoorControlView extends StatelessWidget icon: status.switch1 ? Assets.openedDoor : Assets.closedDoor, onTap: () { context.read().add( - GarageDoorControlEvent( - deviceId: status.uuid, - value: !status.switch1, - code: 'switch_1'), + GarageDoorControlEvent(deviceId: status.uuid, value: !status.switch1, code: 'switch_1'), ); }, status: status.switch1, @@ -92,8 +84,7 @@ class GarageDoorControlView extends StatelessWidget IconNameStatusContainer( onTap: () { context.read().add( - FetchGarageDoorSchedulesEvent( - deviceId: deviceId, category: 'switch_1'), + FetchGarageDoorSchedulesEvent(deviceId: deviceId, category: 'switch_1'), ); showDialog( context: context, @@ -116,9 +107,7 @@ class GarageDoorControlView extends StatelessWidget children: [ IconButton( onPressed: () { - context - .read() - .add(DecreaseGarageDoorDelayEvent(deviceId: status.uuid)); + context.read().add(DecreaseGarageDoorDelayEvent(deviceId: status.uuid)); }, icon: const Icon( Icons.remove, @@ -136,8 +125,7 @@ class GarageDoorControlView extends StatelessWidget ), Text( 'h', - style: context.textTheme.bodySmall! - .copyWith(color: ColorsManager.blackColor), + style: context.textTheme.bodySmall!.copyWith(color: ColorsManager.blackColor), ), Text( (status.delay.inMinutes % 60).toString().padLeft(2, '0'), @@ -148,14 +136,11 @@ class GarageDoorControlView extends StatelessWidget ), Text( 'm', - style: context.textTheme.bodySmall! - .copyWith(color: ColorsManager.blackColor), + style: context.textTheme.bodySmall!.copyWith(color: ColorsManager.blackColor), ), IconButton( onPressed: () { - context - .read() - .add(IncreaseGarageDoorDelayEvent(deviceId: status.uuid)); + context.read().add(IncreaseGarageDoorDelayEvent(deviceId: status.uuid)); }, icon: const Icon( Icons.add, @@ -173,9 +158,7 @@ class GarageDoorControlView extends StatelessWidget onChange: (value) { context.read().add( GarageDoorControlEvent( - deviceId: status.uuid, - value: value ? status.delay.inSeconds : 0, - code: 'countdown_1'), + deviceId: status.uuid, value: value ? status.delay.inSeconds : 0, code: 'countdown_1'), ); }, ), @@ -184,8 +167,7 @@ class GarageDoorControlView extends StatelessWidget name: 'Records', icon: Assets.records, onTap: () { - context.read().add(FetchGarageDoorRecordsEvent( - code: 'switch_1', deviceId: status.uuid)); + context.read().add(FetchGarageDoorRecordsEvent(code: 'switch_1', deviceId: status.uuid)); }, status: false, textColor: ColorsManager.blackColor, diff --git a/lib/pages/device_managment/main_door_sensor/view/main_door_control_view.dart b/lib/pages/device_managment/main_door_sensor/view/main_door_control_view.dart index fe03c74a..ecdb367a 100644 --- a/lib/pages/device_managment/main_door_sensor/view/main_door_control_view.dart +++ b/lib/pages/device_managment/main_door_sensor/view/main_door_control_view.dart @@ -1,17 +1,15 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:flutter_svg/flutter_svg.dart'; import 'package:syncrow_web/pages/device_managment/all_devices/models/devices_model.dart'; import 'package:syncrow_web/pages/device_managment/main_door_sensor/bloc/main_door_sensor_bloc.dart'; import 'package:syncrow_web/pages/device_managment/main_door_sensor/bloc/main_door_sensor_event.dart'; import 'package:syncrow_web/pages/device_managment/main_door_sensor/bloc/main_door_sensor_state.dart'; import 'package:syncrow_web/pages/device_managment/main_door_sensor/models/main_door_status_model.dart'; import 'package:syncrow_web/pages/device_managment/main_door_sensor/widgets/notification_dialog.dart'; -import 'package:syncrow_web/pages/device_managment/shared/device_controls_container.dart'; +import 'package:syncrow_web/pages/device_managment/shared/icon_name_status_container.dart'; import 'package:syncrow_web/pages/device_managment/shared/table/report_table.dart'; import 'package:syncrow_web/utils/color_manager.dart'; import 'package:syncrow_web/utils/constants/assets.dart'; -import 'package:syncrow_web/utils/extension/build_context_x.dart'; import 'package:syncrow_web/utils/helpers/responsice_layout_helper/responsive_layout_helper.dart'; class MainDoorSensorControlView extends StatelessWidget with HelperResponsiveLayout { @@ -113,71 +111,3 @@ class MainDoorSensorControlView extends StatelessWidget with HelperResponsiveLay ); } } - -class IconNameStatusContainer extends StatelessWidget { - const IconNameStatusContainer({ - super.key, - required this.name, - required this.icon, - required this.onTap, - required this.status, - required this.textColor, - this.paddingAmount = 12, - required this.isFullIcon, - }); - - final String name; - final String icon; - final GestureTapCallback onTap; - final bool status; - final Color textColor; - final double? paddingAmount; - final bool isFullIcon; - - @override - Widget build(BuildContext context) { - return GestureDetector( - onTap: onTap, - child: DeviceControlsContainer( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - if (isFullIcon) - ClipOval( - child: SvgPicture.asset( - icon, - fit: BoxFit.contain, - ), - ) - else - ClipOval( - child: Container( - height: 60, - width: 60, - padding: EdgeInsets.all(paddingAmount ?? 8), - color: ColorsManager.whiteColors, - child: SvgPicture.asset( - icon, - width: 35, - height: 35, - fit: BoxFit.contain, - ), - )), - const Spacer(), - Padding( - padding: const EdgeInsets.symmetric(horizontal: 6), - child: Text( - name, - textAlign: TextAlign.start, - style: context.textTheme.titleMedium!.copyWith( - fontWeight: FontWeight.w400, - color: textColor, - ), - ), - ), - ], - ), - ), - ); - } -} diff --git a/lib/pages/device_managment/shared/device_batch_control_dialog.dart b/lib/pages/device_managment/shared/device_batch_control_dialog.dart index d11b1701..f2dc68f5 100644 --- a/lib/pages/device_managment/shared/device_batch_control_dialog.dart +++ b/lib/pages/device_managment/shared/device_batch_control_dialog.dart @@ -1,14 +1,10 @@ -import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; - -import 'package:syncrow_web/utils/extension/build_context_x.dart'; import 'package:syncrow_web/pages/device_managment/all_devices/helper/route_controls_based_code.dart'; - import 'package:syncrow_web/pages/device_managment/all_devices/models/devices_model.dart'; import 'package:syncrow_web/utils/color_manager.dart'; +import 'package:syncrow_web/utils/extension/build_context_x.dart'; -class DeviceBatchControlDialog extends StatelessWidget - with RouteControlsBasedCode { +class DeviceBatchControlDialog extends StatelessWidget with RouteControlsBasedCode { final List devices; const DeviceBatchControlDialog({super.key, required this.devices}); @@ -139,6 +135,8 @@ String getBatchDialogName(AllDevicesModel device) { return "Garage Door Opener"; case 'WL': return "Water Leak Sensor"; + case 'SOS': + return "SOS"; default: return device.categoryName ?? 'Device Control'; } diff --git a/lib/pages/device_managment/shared/icon_name_status_container.dart b/lib/pages/device_managment/shared/icon_name_status_container.dart new file mode 100644 index 00000000..13b6b594 --- /dev/null +++ b/lib/pages/device_managment/shared/icon_name_status_container.dart @@ -0,0 +1,73 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_svg/flutter_svg.dart'; +import 'package:syncrow_web/pages/device_managment/shared/device_controls_container.dart'; +import 'package:syncrow_web/utils/color_manager.dart'; +import 'package:syncrow_web/utils/extension/build_context_x.dart'; + +class IconNameStatusContainer extends StatelessWidget { + const IconNameStatusContainer({ + super.key, + required this.name, + required this.icon, + required this.onTap, + required this.status, + required this.textColor, + this.paddingAmount = 12, + required this.isFullIcon, + }); + + final String name; + final String icon; + final GestureTapCallback onTap; + final bool status; + final Color textColor; + final double? paddingAmount; + final bool isFullIcon; + + @override + Widget build(BuildContext context) { + return GestureDetector( + onTap: onTap, + child: DeviceControlsContainer( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + if (isFullIcon) + ClipOval( + child: SvgPicture.asset( + icon, + fit: BoxFit.contain, + ), + ) + else + ClipOval( + child: Container( + height: 60, + width: 60, + padding: EdgeInsets.all(paddingAmount ?? 8), + color: ColorsManager.whiteColors, + child: SvgPicture.asset( + icon, + width: 35, + height: 35, + fit: BoxFit.contain, + ), + )), + const Spacer(), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 6), + child: Text( + name, + textAlign: TextAlign.start, + style: context.textTheme.titleMedium!.copyWith( + fontWeight: FontWeight.w400, + color: textColor, + ), + ), + ), + ], + ), + ), + ); + } +} diff --git a/lib/pages/device_managment/sos/bloc/sos_device_bloc.dart b/lib/pages/device_managment/sos/bloc/sos_device_bloc.dart new file mode 100644 index 00000000..14fdf61c --- /dev/null +++ b/lib/pages/device_managment/sos/bloc/sos_device_bloc.dart @@ -0,0 +1,90 @@ +import 'dart:async'; + +import 'package:bloc/bloc.dart'; +import 'package:equatable/equatable.dart'; +import 'package:syncrow_web/pages/device_managment/all_devices/models/device_reports.dart'; +import 'package:syncrow_web/pages/device_managment/all_devices/models/factory_reset_model.dart'; +import 'package:syncrow_web/pages/device_managment/sos/models/sos_status_model.dart'; +import 'package:syncrow_web/services/devices_mang_api.dart'; + +part 'sos_device_event.dart'; +part 'sos_device_state.dart'; + +class SosDeviceBloc extends Bloc { + SosDeviceBloc() : super(SosDeviceInitial()) { + on(_getDeviceStatus); + on(_getBatchStatus); + on(_getDeviceRecords); + on(_getDeviceAutomationRecords); + on(_backToSosStatusView); + on(_sosFactoryReset); + } + + late SosStatusModel deviceStatus; + + FutureOr _getDeviceStatus(GetDeviceStatus event, Emitter emit) async { + emit(SosDeviceLoadingState()); + try { + final status = await DevicesManagementApi().getDeviceStatus(event.uuid); + deviceStatus = SosStatusModel.fromJson(event.uuid, status.status); + emit(SosDeviceLoadedState(deviceStatus)); + } catch (e) { + emit(SosDeviceErrorState(e.toString())); + } + } + + FutureOr _getBatchStatus(GetBatchStatus event, Emitter emit) async { + emit(SosDeviceLoadingState()); + try { + final status = await DevicesManagementApi().getBatchStatus(event.uuids); + deviceStatus = SosStatusModel.fromJson(event.uuids.first, status.status); + emit(SosDeviceLoadedState(deviceStatus)); + } catch (e) { + emit(SosDeviceErrorState(e.toString())); + } + } + + FutureOr _getDeviceRecords(GetDeviceRecords event, Emitter emit) async { + emit(SosReportLoadingState()); + try { + final from = DateTime.now().subtract(const Duration(days: 30)).millisecondsSinceEpoch; + final to = DateTime.now().millisecondsSinceEpoch; + final DeviceReport records = + await DevicesManagementApi.getDeviceReportsByDate(event.uuid, 'sos', from.toString(), to.toString()); + emit(SosReportLoadedState(records)); + } catch (e) { + emit(SosReportErrorState(e.toString())); + } + } + + FutureOr _getDeviceAutomationRecords(GetDeviceAutomationRecords event, Emitter emit) async { + emit(SosAutomationReportLoadingState()); + try { + final from = DateTime.now().subtract(const Duration(days: 30)).millisecondsSinceEpoch; + final to = DateTime.now().millisecondsSinceEpoch; + final DeviceReport records = await DevicesManagementApi.getDeviceReportsByDate( + event.uuid, 'sos_automation', from.toString(), to.toString()); + emit(SosAutomationReportLoadedState(records)); + } catch (e) { + emit(SosAutomationReportErrorState(e.toString())); + } + } + + FutureOr _backToSosStatusView(BackToSosStatusView event, Emitter emit) { + emit(SosDeviceLoadedState(deviceStatus)); + } + + FutureOr _sosFactoryReset(SosFactoryReset event, Emitter emit) async { + emit(SosDeviceLoadingState()); + try { + final response = await DevicesManagementApi().factoryReset(event.factoryReset, event.deviceId); + if (response) { + emit(SosDeviceLoadedState(deviceStatus)); + } else { + emit(const SosDeviceErrorState('Factory reset failed')); + } + } catch (e) { + emit(SosDeviceErrorState(e.toString())); + } + } +} diff --git a/lib/pages/device_managment/sos/bloc/sos_device_event.dart b/lib/pages/device_managment/sos/bloc/sos_device_event.dart new file mode 100644 index 00000000..270360b8 --- /dev/null +++ b/lib/pages/device_managment/sos/bloc/sos_device_event.dart @@ -0,0 +1,49 @@ +part of 'sos_device_bloc.dart'; + +sealed class SosDeviceEvent extends Equatable { + const SosDeviceEvent(); +} + +class GetDeviceStatus extends SosDeviceEvent { + final String uuid; + const GetDeviceStatus(this.uuid); + @override + List get props => [uuid]; +} + +class GetBatchStatus extends SosDeviceEvent { + final List uuids; + const GetBatchStatus(this.uuids); + @override + List get props => [uuids]; +} + +class GetDeviceRecords extends SosDeviceEvent { + final String uuid; + + const GetDeviceRecords(this.uuid); + @override + List get props => [uuid]; +} + +class GetDeviceAutomationRecords extends SosDeviceEvent { + final String uuid; + const GetDeviceAutomationRecords(this.uuid); + @override + List get props => [uuid]; +} + +class BackToSosStatusView extends SosDeviceEvent { + @override + List get props => []; +} + +class SosFactoryReset extends SosDeviceEvent { + final String deviceId; + final FactoryResetModel factoryReset; + + const SosFactoryReset({required this.deviceId, required this.factoryReset}); + + @override + List get props => [deviceId, factoryReset]; +} diff --git a/lib/pages/device_managment/sos/bloc/sos_device_state.dart b/lib/pages/device_managment/sos/bloc/sos_device_state.dart new file mode 100644 index 00000000..b6a1eeec --- /dev/null +++ b/lib/pages/device_managment/sos/bloc/sos_device_state.dart @@ -0,0 +1,82 @@ +part of 'sos_device_bloc.dart'; + +sealed class SosDeviceState extends Equatable { + const SosDeviceState(); +} + +final class SosDeviceInitial extends SosDeviceState { + @override + List get props => []; +} + +final class SosDeviceLoadingState extends SosDeviceState { + @override + List get props => []; +} + +final class SosDeviceLoadedState extends SosDeviceState { + final SosStatusModel sosStatusModel; + + const SosDeviceLoadedState(this.sosStatusModel); + + @override + List get props => [sosStatusModel]; +} + +final class SosDeviceErrorState extends SosDeviceState { + final String message; + + const SosDeviceErrorState(this.message); + + @override + List get props => [message]; +} + +/// report state +final class SosReportLoadingState extends SosDeviceState { + @override + List get props => []; +} + +final class SosReportLoadedState extends SosDeviceState { + final DeviceReport sosReport; + + const SosReportLoadedState(this.sosReport); + + @override + List get props => [sosReport]; +} + +final class SosReportErrorState extends SosDeviceState { + final String message; + + const SosReportErrorState(this.message); + + @override + List get props => [message]; +} + +/// automation reports + +final class SosAutomationReportLoadingState extends SosDeviceState { + @override + List get props => []; +} + +final class SosAutomationReportLoadedState extends SosDeviceState { + final DeviceReport automationReport; + + const SosAutomationReportLoadedState(this.automationReport); + + @override + List get props => [automationReport]; +} + +final class SosAutomationReportErrorState extends SosDeviceState { + final String message; + + const SosAutomationReportErrorState(this.message); + + @override + List get props => [message]; +} diff --git a/lib/pages/device_managment/sos/models/sos_status_model.dart b/lib/pages/device_managment/sos/models/sos_status_model.dart new file mode 100644 index 00000000..ac0de3b0 --- /dev/null +++ b/lib/pages/device_managment/sos/models/sos_status_model.dart @@ -0,0 +1,34 @@ +import 'package:syncrow_web/pages/device_managment/all_devices/models/device_status.dart'; + +class SosStatusModel { + final int batteryLevel; + final String sosStatus; + final String deviceId; + + SosStatusModel({ + required this.batteryLevel, + required this.sosStatus, + required this.deviceId, + }); + + factory SosStatusModel.fromJson(String deviceId, List statuses) { + late int batteryLevel; + late String sosStatus; + + for (var status in statuses) { + switch (status.code) { + case 'battery_percentage': + batteryLevel = status.value; + break; + case 'sos': + sosStatus = status.value; + break; + } + } + return SosStatusModel( + deviceId: deviceId, + batteryLevel: batteryLevel, + sosStatus: sosStatus, + ); + } +} diff --git a/lib/pages/device_managment/sos/view/sos_batch_control_view.dart b/lib/pages/device_managment/sos/view/sos_batch_control_view.dart new file mode 100644 index 00000000..ec15b7d6 --- /dev/null +++ b/lib/pages/device_managment/sos/view/sos_batch_control_view.dart @@ -0,0 +1,41 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:syncrow_web/pages/device_managment/all_devices/models/factory_reset_model.dart'; +import 'package:syncrow_web/pages/device_managment/shared/batch_control/factory_reset.dart'; +import 'package:syncrow_web/pages/device_managment/shared/batch_control/firmware_update.dart'; +import 'package:syncrow_web/pages/device_managment/sos/bloc/sos_device_bloc.dart'; + +class SOSBatchControlView extends StatelessWidget { + const SOSBatchControlView({ + super.key, + required this.deviceIds, + }); + + final List deviceIds; + + @override + Widget build(BuildContext context) { + return Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + SizedBox( + width: 170, + // height: 140, + child: FirmwareUpdateWidget(deviceId: deviceIds.first, version: 2)), + const SizedBox( + width: 12, + ), + SizedBox( + width: 170, + height: 140, + child: FactoryResetWidget( + callFactoryReset: () { + context.read().add( + SosFactoryReset(deviceId: deviceIds.first, factoryReset: FactoryResetModel(devicesUuid: deviceIds))); + }, + ), + ), + ], + ); + } +} diff --git a/lib/pages/device_managment/sos/view/sos_device_control_view.dart b/lib/pages/device_managment/sos/view/sos_device_control_view.dart new file mode 100644 index 00000000..dff67c55 --- /dev/null +++ b/lib/pages/device_managment/sos/view/sos_device_control_view.dart @@ -0,0 +1,138 @@ +// sos device control view + +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:syncrow_web/pages/device_managment/all_devices/models/devices_model.dart'; +import 'package:syncrow_web/pages/device_managment/shared/icon_name_status_container.dart'; +import 'package:syncrow_web/pages/device_managment/shared/table/report_table.dart'; +import 'package:syncrow_web/pages/device_managment/sos/bloc/sos_device_bloc.dart'; +import 'package:syncrow_web/pages/device_managment/sos/widgets/sos_notification_dialog.dart'; +import 'package:syncrow_web/utils/color_manager.dart'; +import 'package:syncrow_web/utils/constants/assets.dart'; +import 'package:syncrow_web/utils/helpers/responsice_layout_helper/responsive_layout_helper.dart'; + +import '../models/sos_status_model.dart'; + +class SosDeviceControlsView extends StatelessWidget with HelperResponsiveLayout { + const SosDeviceControlsView({ + super.key, + required this.device, + }); + + final AllDevicesModel device; + + @override + Widget build(BuildContext context) { + return BlocProvider( + create: (context) => SosDeviceBloc()..add(GetDeviceStatus(device.uuid!)), + child: BlocBuilder( + builder: (context, state) { + if (state is SosDeviceLoadingState) { + return const Center(child: CircularProgressIndicator()); + } else if (state is SosDeviceLoadedState) { + return _buildStatusControls(context, state.sosStatusModel); + } else if (state is SosReportLoadedState) { + return ReportsTable( + report: state.sosReport, + hideValueShowDescription: true, + garageDoorSensor: true, + onRowTap: (index) {}, + onClose: () { + context.read().add(BackToSosStatusView()); + }, + ); + } else if (state is SosAutomationReportLoadedState) { + return ReportsTable( + report: state.automationReport, + hideValueShowDescription: true, + garageDoorSensor: true, + onRowTap: (index) {}, + onClose: () { + context.read().add(BackToSosStatusView()); + }, + ); + } else if (state is SosDeviceErrorState) { + return const Center(child: Text('Error fetching status')); + } else if (state is SosAutomationReportErrorState) { + return Center(child: Text('Error: ${state.message.toString()}')); + } else if (state is SosReportErrorState) { + return Center(child: Text('Error: ${state.message.toString()}')); + } + return const Center(child: CircularProgressIndicator()); + }, + )); + } + + Widget _buildStatusControls(BuildContext context, SosStatusModel deviceStatus) { + final isExtraLarge = isExtraLargeScreenSize(context); + final isLarge = isLargeScreenSize(context); + final isMedium = isMediumScreenSize(context); + return GridView( + padding: const EdgeInsets.symmetric(horizontal: 50), + shrinkWrap: true, + physics: const NeverScrollableScrollPhysics(), + gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( + crossAxisCount: isLarge || isExtraLarge + ? 3 + : isMedium + ? 2 + : 1, + mainAxisExtent: 140, + crossAxisSpacing: 12, + mainAxisSpacing: 12, + ), + children: [ + IconNameStatusContainer( + isFullIcon: false, + name: deviceStatus.sosStatus == 'sos' ? 'SOS' : 'Normal', + icon: deviceStatus.sosStatus == 'sos' ? Assets.sos : Assets.sosNormal, + onTap: () {}, + status: false, + textColor: ColorsManager.blackColor, + ), + IconNameStatusContainer( + isFullIcon: false, + name: 'SOS Records', + icon: Assets.records, + onTap: () { + context.read().add( + GetDeviceRecords( + device.uuid!, + ), + ); + }, + status: false, + textColor: ColorsManager.blackColor, + ), + IconNameStatusContainer( + isFullIcon: false, + name: 'Automation Record', + icon: Assets.automationRecords, + onTap: () { + // context.read().add( + // GetDeviceAutomationRecords( + // device.uuid!, + // ), + // ); + }, + status: false, + textColor: ColorsManager.blackColor, + ), + IconNameStatusContainer( + isFullIcon: false, + name: 'Alarm Settings', + icon: Assets.mainDoorNotifi, + onTap: () { + showDialog( + context: context, + builder: (context) => const SosNotificationDialog(), + ); + }, + status: false, + textColor: ColorsManager.blackColor, + paddingAmount: 14, + ), + ], + ); + } +} diff --git a/lib/pages/device_managment/sos/widgets/sos_notification_dialog.dart b/lib/pages/device_managment/sos/widgets/sos_notification_dialog.dart new file mode 100644 index 00000000..55428107 --- /dev/null +++ b/lib/pages/device_managment/sos/widgets/sos_notification_dialog.dart @@ -0,0 +1,116 @@ +import 'package:flutter/material.dart'; +import 'package:syncrow_web/pages/device_managment/shared/toggle_widget.dart'; +import 'package:syncrow_web/utils/color_manager.dart'; + +class SosNotificationDialog extends StatefulWidget { + const SosNotificationDialog({super.key}); + + @override + State createState() => _NotificationDialogState(); +} + +class _NotificationDialogState extends State { + bool isLowBatteryNotificationEnabled = true; + bool isSosAlarmEnabled = true; + + @override + Widget build(BuildContext context) { + return Dialog( + backgroundColor: Colors.white, + insetPadding: const EdgeInsets.all(20), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(20), + ), + child: SizedBox( + width: 550, + child: SingleChildScrollView( + child: Padding( + padding: const EdgeInsets.all(20.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + const SizedBox(), + Text( + 'Notification Settings', + style: TextStyle( + fontWeight: FontWeight.bold, + fontSize: 22, + color: ColorsManager.dialogBlueTitle, + ), + ), + Container( + width: 25, + decoration: BoxDecoration( + color: Colors.transparent, + shape: BoxShape.circle, + border: Border.all( + color: Colors.grey, + width: 1.0, + ), + ), + child: IconButton( + padding: EdgeInsets.all(1), + icon: const Icon( + Icons.close, + color: Colors.grey, + size: 18, + ), + onPressed: () { + Navigator.of(context).pop(); + }, + ), + ), + ], + ), + const SizedBox(height: 20), + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + SizedBox( + width: 170, + height: 135, + child: ToggleWidget( + value: isSosAlarmEnabled, + code: 'notification', + deviceId: '', + label: 'SOS Alarm', + onChange: (v) { + setState(() { + isSosAlarmEnabled = v; + }); + }, + icon: '-1', + ), + ), + const SizedBox( + width: 16, + ), + SizedBox( + width: 170, + height: 135, + child: ToggleWidget( + value: isLowBatteryNotificationEnabled, + code: 'notification', + deviceId: '', + label: 'Low Battery', + onChange: (v) { + setState(() { + isLowBatteryNotificationEnabled = v; + }); + }, + icon: '-1', + ), + ), + ], + ), + ], + ), + ), + ), + ), + ); + } +} diff --git a/lib/pages/device_managment/water_leak/view/water_leak_control_view.dart b/lib/pages/device_managment/water_leak/view/water_leak_control_view.dart index 58421422..6b9dc564 100644 --- a/lib/pages/device_managment/water_leak/view/water_leak_control_view.dart +++ b/lib/pages/device_managment/water_leak/view/water_leak_control_view.dart @@ -1,13 +1,13 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:syncrow_web/pages/device_managment/main_door_sensor/view/main_door_control_view.dart'; +import 'package:syncrow_web/pages/device_managment/shared/icon_name_status_container.dart'; +import 'package:syncrow_web/pages/device_managment/shared/table/report_table.dart'; import 'package:syncrow_web/pages/device_managment/water_leak/bloc/water_leak_bloc.dart'; import 'package:syncrow_web/pages/device_managment/water_leak/bloc/water_leak_event.dart'; import 'package:syncrow_web/pages/device_managment/water_leak/bloc/water_leak_state.dart'; import 'package:syncrow_web/pages/device_managment/water_leak/widgets/water_leak_notifi_dialog.dart'; import 'package:syncrow_web/utils/color_manager.dart'; import 'package:syncrow_web/utils/constants/assets.dart'; -import 'package:syncrow_web/pages/device_managment/shared/table/report_table.dart'; import 'package:syncrow_web/utils/helpers/responsice_layout_helper/responsive_layout_helper.dart'; class WaterLeakView extends StatelessWidget with HelperResponsiveLayout { @@ -21,8 +21,7 @@ class WaterLeakView extends StatelessWidget with HelperResponsiveLayout { final isLarge = isLargeScreenSize(context); final isMedium = isMediumScreenSize(context); return BlocProvider( - create: (context) => - WaterLeakBloc(deviceId)..add(FetchWaterLeakStatusEvent(deviceId)), + create: (context) => WaterLeakBloc(deviceId)..add(FetchWaterLeakStatusEvent(deviceId)), child: BlocBuilder( builder: (context, state) { if (state is WaterLeakLoadingState) { @@ -45,31 +44,21 @@ class WaterLeakView extends StatelessWidget with HelperResponsiveLayout { children: [ IconNameStatusContainer( isFullIcon: false, - name: state.status.watersensorState == 'normal' - ? 'Normal' - : 'Leak Detection', - icon: state.status.watersensorState == 'normal' - ? Assets.waterLeakNormal - : Assets.waterLeakDetected, + name: state.status.watersensorState == 'normal' ? 'Normal' : 'Leak Detection', + icon: state.status.watersensorState == 'normal' ? Assets.waterLeakNormal : Assets.waterLeakDetected, onTap: () {}, status: state.status.watersensorState == 'normal', - textColor: state.status.watersensorState == 'normal' - ? ColorsManager.blackColor - : ColorsManager.red, + textColor: state.status.watersensorState == 'normal' ? ColorsManager.blackColor : ColorsManager.red, ), IconNameStatusContainer( isFullIcon: false, name: 'Records', icon: Assets.records, onTap: () { - context - .read() - .add(FetchWaterLeakReportsEvent( + context.read().add(FetchWaterLeakReportsEvent( deviceId: deviceId, code: 'watersensor_state', - from: DateTime.now() - .subtract(const Duration(days: 30)) - .millisecondsSinceEpoch, + from: DateTime.now().subtract(const Duration(days: 30)).millisecondsSinceEpoch, to: DateTime.now().millisecondsSinceEpoch, )); }, @@ -109,9 +98,7 @@ class WaterLeakView extends StatelessWidget with HelperResponsiveLayout { waterLeak: true, onRowTap: (index) {}, onClose: () { - context - .read() - .add(FetchWaterLeakStatusEvent(deviceId)); + context.read().add(FetchWaterLeakStatusEvent(deviceId)); }, ); } else if (state is WaterLeakReportsFailedState) { diff --git a/lib/utils/constants/assets.dart b/lib/utils/constants/assets.dart index 168d8f43..dad9b261 100644 --- a/lib/utils/constants/assets.dart +++ b/lib/utils/constants/assets.dart @@ -60,29 +60,23 @@ class Assets { static const String nobodyTime = "assets/icons/nobody_time.svg"; // Automation functions - static const String tempPasswordUnlock = - "assets/icons/automation_functions/temp_password_unlock.svg"; - static const String doorlockNormalOpen = - "assets/icons/automation_functions/doorlock_normal_open.svg"; + static const String tempPasswordUnlock = "assets/icons/automation_functions/temp_password_unlock.svg"; + static const String doorlockNormalOpen = "assets/icons/automation_functions/doorlock_normal_open.svg"; static const String doorbell = "assets/icons/automation_functions/doorbell.svg"; - static const String remoteUnlockViaApp = - "assets/icons/automation_functions/remote_unlock_via_app.svg"; + static const String remoteUnlockViaApp = "assets/icons/automation_functions/remote_unlock_via_app.svg"; static const String doubleLock = "assets/icons/automation_functions/double_lock.svg"; static const String selfTestResult = "assets/icons/automation_functions/self_test_result.svg"; static const String lockAlarm = "assets/icons/automation_functions/lock_alarm.svg"; static const String presenceState = "assets/icons/automation_functions/presence_state.svg"; static const String currentTemp = "assets/icons/automation_functions/current_temp.svg"; static const String presence = "assets/icons/automation_functions/presence.svg"; - static const String residualElectricity = - "assets/icons/automation_functions/residual_electricity.svg"; + static const String residualElectricity = "assets/icons/automation_functions/residual_electricity.svg"; static const String hijackAlarm = "assets/icons/automation_functions/hijack_alarm.svg"; static const String passwordUnlock = "assets/icons/automation_functions/password_unlock.svg"; - static const String remoteUnlockRequest = - "assets/icons/automation_functions/remote_unlock_req.svg"; + static const String remoteUnlockRequest = "assets/icons/automation_functions/remote_unlock_req.svg"; static const String cardUnlock = "assets/icons/automation_functions/card_unlock.svg"; static const String motion = "assets/icons/automation_functions/motion.svg"; - static const String fingerprintUnlock = - "assets/icons/automation_functions/fingerprint_unlock.svg"; + static const String fingerprintUnlock = "assets/icons/automation_functions/fingerprint_unlock.svg"; // Presence Sensor Assets static const String sensorMotionIcon = "assets/icons/sensor_motion_ic.svg"; @@ -192,4 +186,10 @@ class Assets { //assets/icons/logo_grey.svg static const String logoGrey = 'assets/icons/logo-grey.svg'; + + //assets/icons/sos.svg + static const String sos = 'assets/icons/sos.svg'; + + //assets/icons/sos_normal.svg + static const String sosNormal = 'assets/icons/sos_normal.svg'; }