diff --git a/lib/pages/device_managment/all_devices/helper/fake_report_data.dart b/lib/pages/device_managment/all_devices/helper/fake_report_data.dart new file mode 100644 index 00000000..0ffd8a1f --- /dev/null +++ b/lib/pages/device_managment/all_devices/helper/fake_report_data.dart @@ -0,0 +1,31 @@ +import 'package:syncrow_web/pages/device_managment/all_devices/models/device_reports.dart'; + +class FakeDeviceReport { + static DeviceReport generateFakeReport() { + return DeviceReport( + deviceUuid: 'fake-uuid-12345', + startTime: DateTime.now().millisecondsSinceEpoch, + endTime: DateTime.now().add(Duration(hours: 1)).millisecondsSinceEpoch, + data: [ + { + 'date': '13/08/2024', + 'time': '13:05', + 'status': 'Presence', + 'description': 'Description for Presence at 13:05 on 13/08/2024', + }, + { + 'date': '13/08/2024', + 'time': '14:10', + 'status': 'No Presence', + 'description': 'Description for No Presence at 14:10 on 13/08/2024', + }, + { + 'date': '14/08/2024', + 'time': '15:30', + 'status': 'Presence', + 'description': 'Description for Presence at 15:30 on 14/08/2024', + }, + ], + ); + } +} diff --git a/lib/pages/device_managment/all_devices/models/device_reports.dart b/lib/pages/device_managment/all_devices/models/device_reports.dart new file mode 100644 index 00000000..7d83052d --- /dev/null +++ b/lib/pages/device_managment/all_devices/models/device_reports.dart @@ -0,0 +1,26 @@ +class DeviceReport { + final String? deviceUuid; + final int? startTime; + final int? endTime; + final List? data; + + DeviceReport({ + this.deviceUuid, + this.startTime, + this.endTime, + this.data, + }); + + DeviceReport.fromJson(Map json) + : deviceUuid = json['deviceUuid'] as String?, + startTime = json['startTime'] as int?, + endTime = json['endTime'] as int?, + data = json['data'] as List?; + + Map toJson() => { + 'deviceUuid': deviceUuid, + 'startTime': startTime, + 'endTime': endTime, + 'data': data + }; +} diff --git a/lib/pages/device_managment/wall_sensor/bloc/bloc.dart b/lib/pages/device_managment/wall_sensor/bloc/bloc.dart index ba70c7f1..cd5c84b1 100644 --- a/lib/pages/device_managment/wall_sensor/bloc/bloc.dart +++ b/lib/pages/device_managment/wall_sensor/bloc/bloc.dart @@ -1,5 +1,6 @@ import 'dart:async'; import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:syncrow_web/pages/device_managment/all_devices/helper/fake_report_data.dart'; import 'package:syncrow_web/pages/device_managment/all_devices/models/device_status.dart'; import 'package:syncrow_web/pages/device_managment/wall_sensor/bloc/event.dart'; import 'package:syncrow_web/pages/device_managment/wall_sensor/bloc/state.dart'; @@ -14,9 +15,13 @@ class WallSensorBloc extends Bloc { WallSensorBloc({required this.deviceId}) : super(WallSensorInitialState()) { on(_fetchWallSensorStatus); on(_changeValue); + on(_getDeviceReports); + on(_showDescription); + on(_backToGridView); } - void _fetchWallSensorStatus(WallSensorInitialEvent event, Emitter emit) async { + void _fetchWallSensorStatus( + WallSensorInitialEvent event, Emitter emit) async { emit(WallSensorLoadingInitialState()); try { var response = await DevicesManagementApi().getDeviceStatus(deviceId); @@ -48,7 +53,8 @@ class WallSensorBloc extends Bloc { // } catch (_) {} // } - void _changeValue(WallSensorChangeValueEvent event, Emitter emit) async { + void _changeValue( + WallSensorChangeValueEvent event, Emitter emit) async { emit(WallSensorLoadingNewSate(wallSensorModel: deviceStatus)); if (event.code == 'far_detection') { deviceStatus.farDetection = event.value; @@ -60,7 +66,8 @@ class WallSensorBloc extends Bloc { deviceStatus.noBodyTime = event.value; } emit(WallSensorUpdateState(wallSensorModel: deviceStatus)); - await _runDeBouncer(deviceId: deviceId, code: event.code, value: event.value); + await _runDeBouncer( + deviceId: deviceId, code: event.code, value: event.value); } _runDeBouncer({ @@ -73,8 +80,8 @@ class WallSensorBloc extends Bloc { } _timer = Timer(const Duration(seconds: 1), () async { try { - final response = - await DevicesManagementApi().deviceControl(deviceId, Status(code: code, value: value)); + final response = await DevicesManagementApi() + .deviceControl(deviceId, Status(code: code, value: value)); if (!response) { add(WallSensorInitialEvent()); @@ -85,4 +92,30 @@ class WallSensorBloc extends Bloc { } }); } + + FutureOr _getDeviceReports( + GetDeviceReportsEvent event, Emitter emit) async { + emit(DeviceReportsLoadingState()); + + try { + //await DevicesManagementApi.getDeviceReports(deviceId, event.code) + // .then((value) { + final fakeReport = FakeDeviceReport.generateFakeReport(); + emit(DeviceReportsState(deviceReport: fakeReport)); + // }); + } catch (e) { + emit(DeviceReportsFailedState(error: e.toString())); + return; + } + } + + void _showDescription( + ShowDescriptionEvent event, Emitter emit) { + emit(WallSensorShowDescriptionState(description: event.description)); + } + + void _backToGridView( + BackToGridViewEvent event, Emitter emit) { + emit(WallSensorUpdateState(wallSensorModel: deviceStatus)); + } } diff --git a/lib/pages/device_managment/wall_sensor/bloc/event.dart b/lib/pages/device_managment/wall_sensor/bloc/event.dart index 08b655e9..d3c20ba7 100644 --- a/lib/pages/device_managment/wall_sensor/bloc/event.dart +++ b/lib/pages/device_managment/wall_sensor/bloc/event.dart @@ -17,3 +17,22 @@ class WallSensorChangeValueEvent extends WallSensorEvent { @override List get props => [value, code]; } + +class GetDeviceReportsEvent extends WallSensorEvent { + final String deviceUuid; + final String code; + const GetDeviceReportsEvent({ + required this.deviceUuid, + required this.code, + }); + + @override + List get props => [deviceUuid, code]; +} + +class ShowDescriptionEvent extends WallSensorEvent { + final String description; + const ShowDescriptionEvent({required this.description}); +} + +class BackToGridViewEvent extends WallSensorEvent {} diff --git a/lib/pages/device_managment/wall_sensor/bloc/state.dart b/lib/pages/device_managment/wall_sensor/bloc/state.dart index ad6eaca1..67ce0d19 100644 --- a/lib/pages/device_managment/wall_sensor/bloc/state.dart +++ b/lib/pages/device_managment/wall_sensor/bloc/state.dart @@ -1,4 +1,5 @@ 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/wall_sensor/model/wall_sensor_model.dart'; class WallSensorState extends Equatable { @@ -36,3 +37,20 @@ class WallSensorFailedState extends WallSensorState { @override List get props => [error]; } + +class DeviceReportsLoadingState extends WallSensorState {} + +class DeviceReportsState extends WallSensorState { + final DeviceReport deviceReport; + const DeviceReportsState({required this.deviceReport}); +} + +class DeviceReportsFailedState extends WallSensorState { + final String error; + const DeviceReportsFailedState({required this.error}); +} + +class WallSensorShowDescriptionState extends WallSensorState { + final String description; + const WallSensorShowDescriptionState({required this.description}); +} diff --git a/lib/pages/device_managment/wall_sensor/view/wall_sensor_conrtols.dart b/lib/pages/device_managment/wall_sensor/view/wall_sensor_conrtols.dart index bd7e0375..727f1d2e 100644 --- a/lib/pages/device_managment/wall_sensor/view/wall_sensor_conrtols.dart +++ b/lib/pages/device_managment/wall_sensor/view/wall_sensor_conrtols.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:syncrow_web/pages/device_managment/all_devices/models/device_reports.dart'; import 'package:syncrow_web/pages/device_managment/all_devices/models/devices_model.dart'; import 'package:syncrow_web/pages/device_managment/wall_sensor/bloc/bloc.dart'; import 'package:syncrow_web/pages/device_managment/wall_sensor/bloc/event.dart'; @@ -8,6 +9,7 @@ import 'package:syncrow_web/pages/device_managment/shared/sensors_widgets/presen import 'package:syncrow_web/pages/device_managment/shared/sensors_widgets/presence_static_widget.dart'; import 'package:syncrow_web/pages/device_managment/shared/sensors_widgets/presence_status.dart'; import 'package:syncrow_web/pages/device_managment/shared/sensors_widgets/presence_update_data.dart'; +import 'package:syncrow_web/pages/device_managment/wall_sensor/model/wall_sensor_model.dart'; import 'package:syncrow_web/utils/constants/assets.dart'; import 'package:syncrow_web/utils/helpers/responsice_layout_helper/responsive_layout_helper.dart'; @@ -21,120 +23,368 @@ class WallSensorControls extends StatelessWidget with HelperResponsiveLayout { final isLarge = isLargeScreenSize(context); final isMedium = isMediumScreenSize(context); return BlocProvider( - create: (context) => WallSensorBloc(deviceId: device.uuid!)..add(WallSensorInitialEvent()), + create: (context) => + WallSensorBloc(deviceId: device.uuid!)..add(WallSensorInitialEvent()), child: BlocBuilder( builder: (context, state) { if (state is WallSensorLoadingInitialState) { return const Center(child: CircularProgressIndicator()); } else if (state is WallSensorUpdateState) { - return GridView( - padding: const EdgeInsets.symmetric(horizontal: 50), - shrinkWrap: true, - physics: const NeverScrollableScrollPhysics(), - gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( - crossAxisCount: isLarge - ? 3 - : isMedium - ? 2 - : 1, - mainAxisExtent: 133, - crossAxisSpacing: 12, - mainAxisSpacing: 12, - ), - children: [ - PresenceState( - value: state.wallSensorModel.presenceState, - ), - PresenceDisplayValue( - value: state.wallSensorModel.presenceTime.toString(), - postfix: 'min', - description: 'Presence Time', - ), - PresenceDisplayValue( - value: state.wallSensorModel.currentDistance.toString(), - postfix: 'cm', - description: 'Current Distance', - ), - PresenceDisplayValue( - value: state.wallSensorModel.illuminance.toString(), - postfix: 'Lux', - description: 'Illuminance Value', - ), - PresenceUpdateData( - value: state.wallSensorModel.motionSensitivity.toDouble(), - title: 'Motion Detection Sensitivity:', - minValue: 1, - maxValue: 5, - steps: 1, - action: (int value) { - context.read().add( - WallSensorChangeValueEvent( - code: 'motion_sensitivity_value', - value: value, - ), - ); - }, - ), - PresenceUpdateData( - value: state.wallSensorModel.motionlessSensitivity.toDouble(), - title: 'Motionless Detection Sensitivity:', - minValue: 1, - maxValue: 5, - steps: 1, - action: (int value) => context.read().add( - WallSensorChangeValueEvent( - code: 'motionless_sensitivity', - value: value, - ), - ), - ), - PresenceUpdateData( - value: state.wallSensorModel.noBodyTime.toDouble(), - title: 'Nobody Time:', - minValue: 10, - maxValue: 10000, - steps: 1, - description: 'hr', - action: (int value) => - context.read().add(WallSensorChangeValueEvent( - code: 'no_one_time', - value: value, - ))), - PresenceUpdateData( - value: state.wallSensorModel.farDetection.toDouble(), - title: 'Far Detection:', - minValue: 75, - maxValue: 600, - steps: 75, - description: 'cm', - action: (int value) => context.read().add( - WallSensorChangeValueEvent( - code: 'far_detection', - value: value, - ), - ), - ), - GestureDetector( - onTap: () {}, - child: const PresenceStaticWidget( - icon: Assets.illuminanceRecordIcon, - description: 'Illuminance Record', - ), - ), - GestureDetector( - onTap: () {}, - child: const PresenceStaticWidget( - icon: Assets.presenceRecordIcon, - description: 'Presence Record', - ), - ), - ], - ); - } else { - return const Center(child: Text('Error fetching status')); + return _buildGridView( + context, state.wallSensorModel, isLarge, isMedium); + } else if (state is DeviceReportsState) { + return _buildReportsTable(context, state.deviceReport); + } else if (state is WallSensorShowDescriptionState) { + return _buildDescriptionView(context, state.description); + } else if (state is DeviceReportsFailedState) { + final model = context.read().deviceStatus; + return _buildGridView(context, model, isLarge, isMedium); } + return const Center(child: Text('Error fetching status')); }, ), ); } + + Widget _buildGridView(BuildContext context, WallSensorModel model, + bool isLarge, bool isMedium) { + return GridView( + padding: const EdgeInsets.symmetric(horizontal: 50), + shrinkWrap: true, + physics: const NeverScrollableScrollPhysics(), + gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( + crossAxisCount: isLarge + ? 3 + : isMedium + ? 2 + : 1, + mainAxisExtent: 133, + crossAxisSpacing: 12, + mainAxisSpacing: 12, + ), + children: [ + PresenceState( + value: model.presenceState, + ), + PresenceDisplayValue( + value: model.presenceTime.toString(), + postfix: 'min', + description: 'Presence Time', + ), + PresenceDisplayValue( + value: model.currentDistance.toString(), + postfix: 'cm', + description: 'Current Distance', + ), + PresenceDisplayValue( + value: model.illuminance.toString(), + postfix: 'Lux', + description: 'Illuminance Value', + ), + PresenceUpdateData( + value: model.motionSensitivity.toDouble(), + title: 'Motion Detection Sensitivity:', + minValue: 1, + maxValue: 5, + steps: 1, + action: (int value) { + context.read().add( + WallSensorChangeValueEvent( + code: 'motion_sensitivity_value', + value: value, + ), + ); + }, + ), + PresenceUpdateData( + value: model.motionlessSensitivity.toDouble(), + title: 'Motionless Detection Sensitivity:', + minValue: 1, + maxValue: 5, + steps: 1, + action: (int value) => context.read().add( + WallSensorChangeValueEvent( + code: 'motionless_sensitivity', + value: value, + ), + ), + ), + PresenceUpdateData( + value: model.noBodyTime.toDouble(), + title: 'Nobody Time:', + minValue: 10, + maxValue: 10000, + steps: 1, + description: 'hr', + action: (int value) => + context.read().add(WallSensorChangeValueEvent( + code: 'no_one_time', + value: value, + ))), + PresenceUpdateData( + value: model.farDetection.toDouble(), + title: 'Far Detection:', + minValue: 75, + maxValue: 600, + steps: 75, + description: 'cm', + action: (int value) => context.read().add( + WallSensorChangeValueEvent( + code: 'far_detection', + value: value, + ), + ), + ), + GestureDetector( + onTap: () { + context.read().add(GetDeviceReportsEvent( + code: 'illuminance_record', deviceUuid: device.uuid!)); + }, + child: const PresenceStaticWidget( + icon: Assets.illuminanceRecordIcon, + description: 'Illuminance Record', + ), + ), + GestureDetector( + onTap: () { + context.read().add(GetDeviceReportsEvent( + code: 'presence_record', deviceUuid: device.uuid!)); + }, + child: const PresenceStaticWidget( + icon: Assets.presenceRecordIcon, + description: 'Presence Record', + ), + ), + ], + ); + } + + Widget _buildReportsTable(BuildContext context, DeviceReport report) { + return Stack( + children: [ + Padding( + padding: const EdgeInsets.all(20.0), + child: Table( + border: TableBorder.all(color: Colors.grey.shade300, width: 1), + columnWidths: const { + 0: FlexColumnWidth(), + 1: FlexColumnWidth(), + 2: FlexColumnWidth(), + }, + children: [ + TableRow( + decoration: BoxDecoration(color: Colors.grey.shade200), + children: [ + _buildTableHeader('Date'), + _buildTableHeader('Time'), + _buildTableHeader('Status'), + ], + ), + ...report.data!.map((entry) { + return TableRow( + children: [ + _buildTableCell(entry['date']), + _buildTableCell(entry['time']), + GestureDetector( + onTap: () { + context.read().add( + ShowDescriptionEvent( + description: entry['description']), + ); + }, + child: _buildTableCell(entry['status']), + ), + ], + ); + }).toList(), + ], + ), + ), + Positioned( + top: 0, + right: 0, + child: IconButton( + icon: const Icon( + Icons.close, + color: Colors.red, + size: 18, + ), + onPressed: () { + context.read().add(BackToGridViewEvent()); + }, + ), + ), + ], + ); + } + + Widget _buildDescriptionView(BuildContext context, String description) { + return Padding( + padding: const EdgeInsets.symmetric(horizontal: 50), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + 'Help Description', + style: Theme.of(context) + .textTheme + .bodyMedium! + .copyWith(color: Colors.grey), + ), + GestureDetector( + onTap: () { + context.read().add(BackToGridViewEvent()); + }, + child: Text( + 'Close', + style: Theme.of(context) + .textTheme + .bodyMedium! + .copyWith(color: Colors.grey), + ), + ), + ], + ), + const SizedBox(height: 10), + Text(description), + ], + ), + ); + } + + Widget _buildTableHeader(String title) { + return Padding( + padding: const EdgeInsets.all(8.0), + child: Text( + title, + style: const TextStyle(fontWeight: FontWeight.bold), + ), + ); + } + + Widget _buildTableCell(String value) { + return Padding( + padding: const EdgeInsets.all(8.0), + child: Text(value), + ); + } + + // BlocBuilder( + // builder: (context, state) { + // if (state is WallSensorLoadingInitialState) { + // return const Center(child: CircularProgressIndicator()); + // } else if (state is WallSensorUpdateState) { + // return + // GridView( + // padding: const EdgeInsets.symmetric(horizontal: 50), + // shrinkWrap: true, + // physics: const NeverScrollableScrollPhysics(), + // gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( + // crossAxisCount: isLarge + // ? 3 + // : isMedium + // ? 2 + // : 1, + // mainAxisExtent: 133, + // crossAxisSpacing: 12, + // mainAxisSpacing: 12, + // ), + // children: [ + // PresenceState( + // value: state.wallSensorModel.presenceState, + // ), + // PresenceDisplayValue( + // value: state.wallSensorModel.presenceTime.toString(), + // postfix: 'min', + // description: 'Presence Time', + // ), + // PresenceDisplayValue( + // value: state.wallSensorModel.currentDistance.toString(), + // postfix: 'cm', + // description: 'Current Distance', + // ), + // PresenceDisplayValue( + // value: state.wallSensorModel.illuminance.toString(), + // postfix: 'Lux', + // description: 'Illuminance Value', + // ), + // PresenceUpdateData( + // value: state.wallSensorModel.motionSensitivity.toDouble(), + // title: 'Motion Detection Sensitivity:', + // minValue: 1, + // maxValue: 5, + // steps: 1, + // action: (int value) { + // context.read().add( + // WallSensorChangeValueEvent( + // code: 'motion_sensitivity_value', + // value: value, + // ), + // ); + // }, + // ), + // PresenceUpdateData( + // value: state.wallSensorModel.motionlessSensitivity.toDouble(), + // title: 'Motionless Detection Sensitivity:', + // minValue: 1, + // maxValue: 5, + // steps: 1, + // action: (int value) => context.read().add( + // WallSensorChangeValueEvent( + // code: 'motionless_sensitivity', + // value: value, + // ), + // ), + // ), + // PresenceUpdateData( + // value: state.wallSensorModel.noBodyTime.toDouble(), + // title: 'Nobody Time:', + // minValue: 10, + // maxValue: 10000, + // steps: 1, + // description: 'hr', + // action: (int value) => + // context.read().add(WallSensorChangeValueEvent( + // code: 'no_one_time', + // value: value, + // ))), + // PresenceUpdateData( + // value: state.wallSensorModel.farDetection.toDouble(), + // title: 'Far Detection:', + // minValue: 75, + // maxValue: 600, + // steps: 75, + // description: 'cm', + // action: (int value) => context.read().add( + // WallSensorChangeValueEvent( + // code: 'far_detection', + // value: value, + // ), + // ), + // ), + // GestureDetector( + // onTap: () {}, + // child: const PresenceStaticWidget( + // icon: Assets.illuminanceRecordIcon, + // description: 'Illuminance Record', + // ), + // ), + // GestureDetector( + // onTap: () {}, + // child: const PresenceStaticWidget( + // icon: Assets.presenceRecordIcon, + // description: 'Presence Record', + // ), + // ), + // ], + // ); + // } else { + // return const Center(child: Text('Error fetching status')); + // } + // }, + // ), + // ); } diff --git a/lib/services/devices_mang_api.dart b/lib/services/devices_mang_api.dart index c7881a1a..7e2dc4f8 100644 --- a/lib/services/devices_mang_api.dart +++ b/lib/services/devices_mang_api.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import 'package:syncrow_web/pages/device_managment/all_devices/models/device_reports.dart'; import 'package:syncrow_web/pages/device_managment/all_devices/models/device_status.dart'; import 'package:syncrow_web/pages/device_managment/all_devices/models/devices_model.dart'; import 'package:syncrow_web/pages/visitor_password/model/device_model.dart'; @@ -94,6 +95,16 @@ class DevicesManagementApi { return response; } - //https://syncrow-dev.azurewebsites.net/device/report-logs/7e97c359-e822-4507-ac1f-e0ff96b0ca29?code=factory_reset - + static Future getDeviceReports(String uuid, String code) async { + final response = await HTTPService().get( + path: ApiEndpoints.getDeviceLogs + .replaceAll('{uuid}', uuid) + .replaceAll('{code}', code), + showServerMessage: false, + expectedResponseModel: (json) { + return DeviceReport.fromJson(json); + }, + ); + return response; + } } diff --git a/lib/utils/constants/api_const.dart b/lib/utils/constants/api_const.dart index 63e6a7d3..2d85a7f5 100644 --- a/lib/utils/constants/api_const.dart +++ b/lib/utils/constants/api_const.dart @@ -35,4 +35,7 @@ abstract class ApiEndpoints { static const String deviceControl = '$baseUrl/device/{uuid}/control'; static const String gatewayApi = '/device/gateway/{gatewayUuid}/devices'; static const String openDoorLock = '/door-lock/open/{doorLockUuid}'; + + static const String getDeviceLogs = + '$baseUrl/device/report-logs/{uuid}?code={code}'; }