From db513f916fa983e9b4f714e9c62f29c74484ed3c Mon Sep 17 00:00:00 2001 From: mohammad Date: Wed, 18 Jun 2025 16:27:50 +0300 Subject: [PATCH 1/8] Refactor schedule components and update imports for garage door and water heater modules --- .../helper/garage_door_helper.dart | 4 +- .../opening_clsoing_time_dialog_body.dart | 5 +- .../schedule__garage_table.dart | 48 +- .../schedule_garage_header.dart | 0 .../schedule_garage_managment_ui.dart | 2 +- .../schedule_garage_mode_buttons.dart | 0 .../schedule_garage_mode_selector.dart | 0 .../schedule_garage_view.dart | 6 +- .../seconds_picker.dart | 0 .../time_out_alarm_dialog_body.dart | 0 .../view/garage_door_control_view.dart | 2 +- .../schedule_device/bloc/schedule_bloc.dart | 557 ++++++++++++++++++ .../schedule_device/bloc/schedule_event.dart | 221 +++++++ .../schedule_device/bloc/schedule_state.dart | 109 ++++ .../schedule_widgets}/count_down_button.dart | 26 +- .../count_down_inching_view.dart | 185 ++++++ .../inching_mode_buttons.dart | 19 +- .../schedule_widgets/schedual_view.dart | 107 ++++ .../schedule_widgets}/schedule_header.dart | 0 .../schedule_managment_ui.dart | 13 +- .../schedule_mode_buttons.dart | 0 .../schedule_mode_selector.dart | 87 +++ .../schedule_widgets}/schedule_table.dart | 138 +++-- .../helper/add_schedule_dialog_helper.dart | 336 +++++------ .../water_heater/models/schedule_entry.dart | 23 +- .../models/water_heater_status_model.dart | 2 +- .../view/water_heater_device_control.dart | 9 +- .../widgets/count_down_inching_view.dart | 223 ------- .../water_heater/widgets/schedual_view.dart | 117 ---- .../widgets/schedule_mode_selector.dart | 86 --- 30 files changed, 1603 insertions(+), 722 deletions(-) rename lib/pages/device_managment/garage_door/{widgets => schedule_view}/opening_clsoing_time_dialog_body.dart (88%) rename lib/pages/device_managment/garage_door/{widgets => schedule_view}/schedule__garage_table.dart (79%) rename lib/pages/device_managment/garage_door/{widgets => schedule_view}/schedule_garage_header.dart (100%) rename lib/pages/device_managment/garage_door/{widgets => schedule_view}/schedule_garage_managment_ui.dart (93%) rename lib/pages/device_managment/garage_door/{widgets => schedule_view}/schedule_garage_mode_buttons.dart (100%) rename lib/pages/device_managment/garage_door/{widgets => schedule_view}/schedule_garage_mode_selector.dart (100%) rename lib/pages/device_managment/garage_door/{widgets => schedule_view}/schedule_garage_view.dart (91%) rename lib/pages/device_managment/garage_door/{widgets => schedule_view}/seconds_picker.dart (100%) rename lib/pages/device_managment/garage_door/{widgets => schedule_view}/time_out_alarm_dialog_body.dart (100%) create mode 100644 lib/pages/device_managment/schedule_device/bloc/schedule_bloc.dart create mode 100644 lib/pages/device_managment/schedule_device/bloc/schedule_event.dart create mode 100644 lib/pages/device_managment/schedule_device/bloc/schedule_state.dart rename lib/pages/device_managment/{water_heater/widgets => schedule_device/schedule_widgets}/count_down_button.dart (72%) create mode 100644 lib/pages/device_managment/schedule_device/schedule_widgets/count_down_inching_view.dart rename lib/pages/device_managment/{water_heater/widgets => schedule_device/schedule_widgets}/inching_mode_buttons.dart (82%) create mode 100644 lib/pages/device_managment/schedule_device/schedule_widgets/schedual_view.dart rename lib/pages/device_managment/{water_heater/widgets => schedule_device/schedule_widgets}/schedule_header.dart (100%) rename lib/pages/device_managment/{water_heater/widgets => schedule_device/schedule_widgets}/schedule_managment_ui.dart (76%) rename lib/pages/device_managment/{water_heater/widgets => schedule_device/schedule_widgets}/schedule_mode_buttons.dart (100%) create mode 100644 lib/pages/device_managment/schedule_device/schedule_widgets/schedule_mode_selector.dart rename lib/pages/device_managment/{water_heater/widgets => schedule_device/schedule_widgets}/schedule_table.dart (58%) delete mode 100644 lib/pages/device_managment/water_heater/widgets/count_down_inching_view.dart delete mode 100644 lib/pages/device_managment/water_heater/widgets/schedual_view.dart delete mode 100644 lib/pages/device_managment/water_heater/widgets/schedule_mode_selector.dart diff --git a/lib/pages/device_managment/garage_door/helper/garage_door_helper.dart b/lib/pages/device_managment/garage_door/helper/garage_door_helper.dart index 7b133d45..8c9820f9 100644 --- a/lib/pages/device_managment/garage_door/helper/garage_door_helper.dart +++ b/lib/pages/device_managment/garage_door/helper/garage_door_helper.dart @@ -4,8 +4,8 @@ import 'package:syncrow_web/pages/common/buttons/default_button.dart'; import 'package:syncrow_web/pages/device_managment/garage_door/bloc/garage_door_bloc.dart'; import 'package:syncrow_web/pages/device_managment/garage_door/bloc/garage_door_event.dart'; import 'package:syncrow_web/pages/device_managment/garage_door/bloc/garage_door_state.dart'; -import 'package:syncrow_web/pages/device_managment/garage_door/widgets/opening_clsoing_time_dialog_body.dart'; -import 'package:syncrow_web/pages/device_managment/garage_door/widgets/time_out_alarm_dialog_body.dart'; +import 'package:syncrow_web/pages/device_managment/garage_door/schedule_view/opening_clsoing_time_dialog_body.dart'; +import 'package:syncrow_web/pages/device_managment/garage_door/schedule_view/time_out_alarm_dialog_body.dart'; import 'package:syncrow_web/pages/device_managment/shared/sensors_widgets/presence_display_data.dart'; import 'package:syncrow_web/pages/device_managment/shared/toggle_widget.dart'; import 'package:syncrow_web/pages/device_managment/water_heater/models/schedule_model.dart'; diff --git a/lib/pages/device_managment/garage_door/widgets/opening_clsoing_time_dialog_body.dart b/lib/pages/device_managment/garage_door/schedule_view/opening_clsoing_time_dialog_body.dart similarity index 88% rename from lib/pages/device_managment/garage_door/widgets/opening_clsoing_time_dialog_body.dart rename to lib/pages/device_managment/garage_door/schedule_view/opening_clsoing_time_dialog_body.dart index 843bac9b..0cc9485a 100644 --- a/lib/pages/device_managment/garage_door/widgets/opening_clsoing_time_dialog_body.dart +++ b/lib/pages/device_managment/garage_door/schedule_view/opening_clsoing_time_dialog_body.dart @@ -1,14 +1,13 @@ -import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:syncrow_web/pages/device_managment/garage_door/bloc/garage_door_bloc.dart'; import 'package:syncrow_web/pages/device_managment/garage_door/bloc/garage_door_state.dart'; -import 'package:syncrow_web/pages/device_managment/garage_door/widgets/seconds_picker.dart'; +import 'package:syncrow_web/pages/device_managment/garage_door/schedule_view/seconds_picker.dart'; class OpeningAndClosingTimeDialogBody extends StatefulWidget { final ValueChanged onDurationChanged; final GarageDoorBloc bloc; - OpeningAndClosingTimeDialogBody({ + const OpeningAndClosingTimeDialogBody({ required this.onDurationChanged, required this.bloc, }); diff --git a/lib/pages/device_managment/garage_door/widgets/schedule__garage_table.dart b/lib/pages/device_managment/garage_door/schedule_view/schedule__garage_table.dart similarity index 79% rename from lib/pages/device_managment/garage_door/widgets/schedule__garage_table.dart rename to lib/pages/device_managment/garage_door/schedule_view/schedule__garage_table.dart index 07cd9c7a..0bd347ab 100644 --- a/lib/pages/device_managment/garage_door/widgets/schedule__garage_table.dart +++ b/lib/pages/device_managment/garage_door/schedule_view/schedule__garage_table.dart @@ -26,7 +26,8 @@ class ScheduleGarageTableWidget extends StatelessWidget { Table( border: TableBorder.all( color: ColorsManager.graysColor, - borderRadius: const BorderRadius.only(topLeft: Radius.circular(20), topRight: Radius.circular(20)), + borderRadius: const BorderRadius.only( + topLeft: Radius.circular(20), topRight: Radius.circular(20)), ), children: [ TableRow( @@ -50,17 +51,21 @@ class ScheduleGarageTableWidget extends StatelessWidget { BlocBuilder( builder: (context, state) { if (state is ScheduleGarageLoadingState) { - return const SizedBox(height: 200, child: Center(child: CircularProgressIndicator())); + return const SizedBox( + height: 200, + child: Center(child: CircularProgressIndicator())); } - if (state is GarageDoorLoadedState && state.status.schedules?.isEmpty == true) { + if (state is GarageDoorLoadedState && + state.status.schedules?.isEmpty == true) { return _buildEmptyState(context); } else if (state is GarageDoorLoadedState) { return Container( height: 200, decoration: BoxDecoration( border: Border.all(color: ColorsManager.graysColor), - borderRadius: - const BorderRadius.only(bottomLeft: Radius.circular(20), bottomRight: Radius.circular(20)), + borderRadius: const BorderRadius.only( + bottomLeft: Radius.circular(20), + bottomRight: Radius.circular(20)), ), child: _buildTableBody(state, context)); } @@ -78,7 +83,8 @@ class ScheduleGarageTableWidget extends StatelessWidget { height: 200, decoration: BoxDecoration( border: Border.all(color: ColorsManager.graysColor), - borderRadius: const BorderRadius.only(bottomLeft: Radius.circular(20), bottomRight: Radius.circular(20)), + borderRadius: const BorderRadius.only( + bottomLeft: Radius.circular(20), bottomRight: Radius.circular(20)), ), child: Center( child: Column( @@ -112,7 +118,8 @@ class ScheduleGarageTableWidget extends StatelessWidget { children: [ if (state.status.schedules != null) for (int i = 0; i < state.status.schedules!.length; i++) - _buildScheduleRow(state.status.schedules![i], i, context, state), + _buildScheduleRow( + state.status.schedules![i], i, context, state), ], ), ), @@ -134,7 +141,8 @@ class ScheduleGarageTableWidget extends StatelessWidget { ); } - TableRow _buildScheduleRow(ScheduleModel schedule, int index, BuildContext context, GarageDoorLoadedState state) { + TableRow _buildScheduleRow(ScheduleModel schedule, int index, + BuildContext context, GarageDoorLoadedState state) { return TableRow( children: [ Center( @@ -152,7 +160,8 @@ class ScheduleGarageTableWidget extends StatelessWidget { width: 24, height: 24, child: schedule.enable - ? const Icon(Icons.radio_button_checked, color: ColorsManager.blueColor) + ? const Icon(Icons.radio_button_checked, + color: ColorsManager.blueColor) : const Icon( Icons.radio_button_unchecked, color: ColorsManager.grayColor, @@ -160,7 +169,9 @@ class ScheduleGarageTableWidget extends StatelessWidget { ), ), ), - Center(child: Text(_getSelectedDays(ScheduleModel.parseSelectedDays(schedule.days)))), + Center( + child: Text(_getSelectedDays( + ScheduleModel.parseSelectedDays(schedule.days)))), Center(child: Text(formatIsoStringToTime(schedule.time, context))), Center(child: Text(schedule.function.value ? 'On' : 'Off')), Center( @@ -170,18 +181,24 @@ class ScheduleGarageTableWidget extends StatelessWidget { TextButton( style: TextButton.styleFrom(padding: EdgeInsets.zero), onPressed: () { - GarageDoorDialogHelper.showAddGarageDoorScheduleDialog(context, - schedule: schedule, index: index, isEdit: true); + GarageDoorDialogHelper.showAddGarageDoorScheduleDialog( + context, + schedule: schedule, + index: index, + isEdit: true); }, child: Text( 'Edit', - style: context.textTheme.bodySmall!.copyWith(color: ColorsManager.blueColor), + style: context.textTheme.bodySmall! + .copyWith(color: ColorsManager.blueColor), ), ), TextButton( style: TextButton.styleFrom(padding: EdgeInsets.zero), onPressed: () { - context.read().add(DeleteGarageDoorScheduleEvent( + context + .read() + .add(DeleteGarageDoorScheduleEvent( index: index, scheduleId: schedule.scheduleId, deviceId: state.status.uuid, @@ -189,7 +206,8 @@ class ScheduleGarageTableWidget extends StatelessWidget { }, child: Text( 'Delete', - style: context.textTheme.bodySmall!.copyWith(color: ColorsManager.blueColor), + style: context.textTheme.bodySmall! + .copyWith(color: ColorsManager.blueColor), ), ), ], diff --git a/lib/pages/device_managment/garage_door/widgets/schedule_garage_header.dart b/lib/pages/device_managment/garage_door/schedule_view/schedule_garage_header.dart similarity index 100% rename from lib/pages/device_managment/garage_door/widgets/schedule_garage_header.dart rename to lib/pages/device_managment/garage_door/schedule_view/schedule_garage_header.dart diff --git a/lib/pages/device_managment/garage_door/widgets/schedule_garage_managment_ui.dart b/lib/pages/device_managment/garage_door/schedule_view/schedule_garage_managment_ui.dart similarity index 93% rename from lib/pages/device_managment/garage_door/widgets/schedule_garage_managment_ui.dart rename to lib/pages/device_managment/garage_door/schedule_view/schedule_garage_managment_ui.dart index e5819e89..9b2c2b3f 100644 --- a/lib/pages/device_managment/garage_door/widgets/schedule_garage_managment_ui.dart +++ b/lib/pages/device_managment/garage_door/schedule_view/schedule_garage_managment_ui.dart @@ -1,7 +1,7 @@ import 'package:flutter/material.dart'; import 'package:syncrow_web/pages/common/buttons/default_button.dart'; import 'package:syncrow_web/pages/device_managment/garage_door/bloc/garage_door_state.dart'; -import 'package:syncrow_web/pages/device_managment/garage_door/widgets/schedule__garage_table.dart'; +import 'package:syncrow_web/pages/device_managment/garage_door/schedule_view/schedule__garage_table.dart'; import 'package:syncrow_web/utils/color_manager.dart'; import 'package:syncrow_web/utils/extension/build_context_x.dart'; diff --git a/lib/pages/device_managment/garage_door/widgets/schedule_garage_mode_buttons.dart b/lib/pages/device_managment/garage_door/schedule_view/schedule_garage_mode_buttons.dart similarity index 100% rename from lib/pages/device_managment/garage_door/widgets/schedule_garage_mode_buttons.dart rename to lib/pages/device_managment/garage_door/schedule_view/schedule_garage_mode_buttons.dart diff --git a/lib/pages/device_managment/garage_door/widgets/schedule_garage_mode_selector.dart b/lib/pages/device_managment/garage_door/schedule_view/schedule_garage_mode_selector.dart similarity index 100% rename from lib/pages/device_managment/garage_door/widgets/schedule_garage_mode_selector.dart rename to lib/pages/device_managment/garage_door/schedule_view/schedule_garage_mode_selector.dart diff --git a/lib/pages/device_managment/garage_door/widgets/schedule_garage_view.dart b/lib/pages/device_managment/garage_door/schedule_view/schedule_garage_view.dart similarity index 91% rename from lib/pages/device_managment/garage_door/widgets/schedule_garage_view.dart rename to lib/pages/device_managment/garage_door/schedule_view/schedule_garage_view.dart index 107c8e0a..7b772135 100644 --- a/lib/pages/device_managment/garage_door/widgets/schedule_garage_view.dart +++ b/lib/pages/device_managment/garage_door/schedule_view/schedule_garage_view.dart @@ -4,9 +4,9 @@ import 'package:syncrow_web/pages/device_managment/garage_door/bloc/garage_door_ import 'package:syncrow_web/pages/device_managment/garage_door/bloc/garage_door_state.dart'; 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_header.dart'; -import 'package:syncrow_web/pages/device_managment/garage_door/widgets/schedule_garage_managment_ui.dart'; -import 'package:syncrow_web/pages/device_managment/garage_door/widgets/schedule_garage_mode_buttons.dart'; +import 'package:syncrow_web/pages/device_managment/garage_door/schedule_view/schedule_garage_header.dart'; +import 'package:syncrow_web/pages/device_managment/garage_door/schedule_view/schedule_garage_managment_ui.dart'; +import 'package:syncrow_web/pages/device_managment/garage_door/schedule_view/schedule_garage_mode_buttons.dart'; class BuildGarageDoorScheduleView extends StatefulWidget { const BuildGarageDoorScheduleView({super.key, required this.status}); diff --git a/lib/pages/device_managment/garage_door/widgets/seconds_picker.dart b/lib/pages/device_managment/garage_door/schedule_view/seconds_picker.dart similarity index 100% rename from lib/pages/device_managment/garage_door/widgets/seconds_picker.dart rename to lib/pages/device_managment/garage_door/schedule_view/seconds_picker.dart diff --git a/lib/pages/device_managment/garage_door/widgets/time_out_alarm_dialog_body.dart b/lib/pages/device_managment/garage_door/schedule_view/time_out_alarm_dialog_body.dart similarity index 100% rename from lib/pages/device_managment/garage_door/widgets/time_out_alarm_dialog_body.dart rename to lib/pages/device_managment/garage_door/schedule_view/time_out_alarm_dialog_body.dart 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 ae2fc9e4..30d9bf5d 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 @@ -5,7 +5,7 @@ import 'package:syncrow_web/pages/device_managment/garage_door/bloc/garage_door_ import 'package:syncrow_web/pages/device_managment/garage_door/bloc/garage_door_state.dart'; 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/garage_door/schedule_view/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'; diff --git a/lib/pages/device_managment/schedule_device/bloc/schedule_bloc.dart b/lib/pages/device_managment/schedule_device/bloc/schedule_bloc.dart new file mode 100644 index 00000000..e6a2645d --- /dev/null +++ b/lib/pages/device_managment/schedule_device/bloc/schedule_bloc.dart @@ -0,0 +1,557 @@ +import 'dart:async'; +import 'package:bloc/bloc.dart'; +import 'package:equatable/equatable.dart'; +import 'package:flutter/material.dart'; +import 'package:syncrow_web/pages/device_managment/all_devices/models/device_status.dart'; +import 'package:syncrow_web/pages/device_managment/water_heater/models/schedule_entry.dart'; +import 'package:syncrow_web/pages/device_managment/water_heater/models/schedule_model.dart'; +import 'package:syncrow_web/pages/device_managment/water_heater/models/water_heater_status_model.dart'; +import 'package:syncrow_web/services/control_device_service.dart'; +import 'package:syncrow_web/services/devices_mang_api.dart'; +part 'schedule_event.dart'; +part 'schedule_state.dart'; + +class ScheduleBloc extends Bloc { + final String deviceId; + + ScheduleBloc({ + required this.deviceId, + }) : super(ScheduleInitial()) { + on(_initializeAddSchedule); + on(_updateSelectedTime); + on(_updateSelectedDay); + on(_updateFunctionOn); + on(_getSchedule); + on(_onAddSchedule); + on(_onEditSchedule); + on(_onUpdateSchedule); + on(_onUpdateScheduleMode); + on(_onUpdateCountdownTime); + on(_onUpdateInchingTime); + on(_onStartScheduleEvent); + on(_onStopScheduleEvent); + on(_onDecrementCountdown); + on(_fetchStatus); + on(_onDeleteSchedule); + } + Timer? _countdownTimer; + Duration countdownRemaining = Duration.zero; + + void _onStopScheduleEvent( + StopScheduleEvent event, + Emitter emit, + ) { + if (state is ScheduleLoaded) { + final currentState = state as ScheduleLoaded; + _countdownTimer?.cancel(); + + if (event.mode == ScheduleModes.countdown) { + emit(currentState.copyWith( + countdownHours: 0, + countdownMinutes: 0, + isCountdownActive: false, + countdownRemaining: Duration.zero, + )); + } else if (event.mode == ScheduleModes.inching) { + emit(currentState.copyWith( + inchingHours: 0, + inchingMinutes: 0, + isInchingActive: false, + countdownRemaining: Duration.zero, + )); + } + } + } + + void _onUpdateScheduleMode( + UpdateScheduleModeEvent event, + Emitter emit, + ) { + if (state is ScheduleLoaded) { + final currentState = state as ScheduleLoaded; + emit(currentState.copyWith( + scheduleMode: event.scheduleMode, + countdownRemaining: Duration.zero, + )); + } + } + + void _onUpdateCountdownTime( + UpdateCountdownTimeEvent event, + Emitter emit, + ) { + if (state is ScheduleLoaded) { + final currentState = state as ScheduleLoaded; + emit(currentState.copyWith( + countdownHours: event.hours, + countdownMinutes: event.minutes, + inchingHours: 0, + inchingMinutes: 0, + countdownRemaining: Duration.zero, + )); + } + } + + void _onUpdateInchingTime( + UpdateInchingTimeEvent event, + Emitter emit, + ) { + if (state is ScheduleLoaded) { + final currentState = state as ScheduleLoaded; + emit(currentState.copyWith( + inchingHours: event.hours, + inchingMinutes: event.minutes, + countdownRemaining: Duration.zero, + )); + } + } + + void _initializeAddSchedule( + ScheduleInitializeAddEvent event, + Emitter emit, + ) { + if (state is ScheduleLoaded) { + final currentState = state as ScheduleLoaded; + emit(currentState.copyWith( + selectedTime: event.selectedTime, + selectedDays: event.selectedDays ?? List.filled(7, false), + functionOn: event.functionOn ?? false, + isEditing: event.isEditing, + scheduleMode: event.scheduleMode, + countdownRemaining: Duration.zero, + )); + } else { + emit(ScheduleLoaded( + schedules: const [], + selectedTime: event.selectedTime, + selectedDays: event.selectedDays ?? List.filled(7, false), + functionOn: event.functionOn ?? false, + isEditing: event.isEditing, + deviceId: deviceId, + scheduleMode: event.scheduleMode, + countdownHours: 0, + countdownMinutes: 0, + inchingHours: 0, + inchingMinutes: 0, + isCountdownActive: false, + isInchingActive: false, + )); + } + } + + void _updateSelectedTime( + ScheduleUpdateSelectedTimeEvent event, + Emitter emit, + ) { + if (state is ScheduleLoaded) { + final currentState = state as ScheduleLoaded; + emit(currentState.copyWith( + selectedTime: event.selectedTime, + countdownHours: 0, + countdownMinutes: 0, + inchingHours: 0, + inchingMinutes: 0, + countdownRemaining: Duration.zero, + )); + } + } + + void _updateSelectedDay( + ScheduleUpdateSelectedDayEvent event, + Emitter emit, + ) { + if (state is ScheduleLoaded) { + final currentState = state as ScheduleLoaded; + final updatedDays = List.from(currentState.selectedDays); + updatedDays[event.index] = event.value; + emit(currentState.copyWith( + selectedDays: updatedDays, + countdownHours: 0, + countdownMinutes: 0, + inchingHours: 0, + inchingMinutes: 0, + countdownRemaining: Duration.zero, + )); + } + } + + void _updateFunctionOn( + ScheduleUpdateFunctionOnEvent event, + Emitter emit, + ) { + if (state is ScheduleLoaded) { + final currentState = state as ScheduleLoaded; + emit(currentState.copyWith( + functionOn: event.isOn, + countdownHours: 0, + countdownMinutes: 0, + inchingHours: 0, + inchingMinutes: 0, + countdownRemaining: Duration.zero, + )); + } + } + + Future _getSchedule( + ScheduleGetEvent event, + Emitter emit, + ) async { + try { + emit(ScheduleLoading()); + final schedules = await DevicesManagementApi().getDeviceSchedules( + deviceId, + event.category, + ); + + if (state is ScheduleLoaded) { + final currentState = state as ScheduleLoaded; + emit(currentState.copyWith( + schedules: schedules, + selectedTime: null, + selectedDays: List.filled(7, false), + functionOn: false, + isEditing: false, + countdownRemaining: Duration.zero, + )); + } else { + emit(ScheduleLoaded( + schedules: schedules, + selectedTime: null, + selectedDays: List.filled(7, false), + functionOn: false, + isEditing: false, + deviceId: deviceId, + scheduleMode: ScheduleModes.schedule, + countdownHours: 0, + countdownMinutes: 0, + inchingHours: 0, + inchingMinutes: 0, + isCountdownActive: false, + isInchingActive: false, + )); + } + } catch (e) { + emit(ScheduleError('Failed to load schedules: $e')); + } + } + + Future _onAddSchedule( + ScheduleAddEvent event, + Emitter emit, + ) async { + try { + if (state is ScheduleLoaded) { + final newSchedule = ScheduleEntry( + category: event.category, + time: event.time, + function: Status(code: 'switch_1', value: event.functionOn), + days: event.selectedDays); + final success = await DevicesManagementApi().addScheduleRecord( + newSchedule, + deviceId, + ); + + if (success) { + add(const ScheduleGetEvent(category: 'switch_1')); + } else { + emit(const ScheduleError('Failed to add schedule')); + } + } + } catch (e) { + emit(ScheduleError('Failed to add schedule: $e')); + } + } + + Future _onEditSchedule( + ScheduleEditEvent event, + Emitter emit, + ) async { + try { + if (state is ScheduleLoaded) { + final updatedSchedule = ScheduleEntry( + scheduleId: event.scheduleId, + category: event.category, + time: event.time, + function: Status(code: 'switch_1', value: event.functionOn), + days: event.selectedDays, + ); + + final success = await DevicesManagementApi().editScheduleRecord( + deviceId, + updatedSchedule, + ); + + if (success) { + add(const ScheduleGetEvent(category: 'switch_1')); + } else { + emit(const ScheduleError('Failed to update schedule')); + } + } + } catch (e) { + emit(ScheduleError('Failed to update schedule: $e')); + } + } + + Future _onUpdateSchedule( + ScheduleUpdateEntryEvent event, + Emitter emit, + ) async { + try { + if (state is ScheduleLoaded) { + final currentState = state as ScheduleLoaded; + final updatedSchedules = currentState.schedules.map((schedule) { + if (schedule.scheduleId == event.scheduleId) { + return schedule.copyWith( + function: Status(code: 'switch_1', value: event.functionOn), + ); + } + return schedule; + }).toList(); + + final success = await DevicesManagementApi().updateScheduleRecord( + enable: event.enable, + uuid: deviceId, + scheduleId: event.scheduleId, + ); + + if (success) { + emit(currentState.copyWith( + schedules: updatedSchedules, + countdownHours: 0, + countdownMinutes: 0, + inchingHours: 0, + inchingMinutes: 0, + countdownRemaining: Duration.zero, + )); + } else { + emit(const ScheduleError('Failed to update schedule status')); + } + } + } catch (e) { + emit(ScheduleError('Failed to update schedule: $e')); + } + } + + Future _onDeleteSchedule( + ScheduleDeleteEvent event, + Emitter emit, + ) async { + try { + if (state is ScheduleLoaded) { + final currentState = state as ScheduleLoaded; + final success = await DevicesManagementApi().deleteScheduleRecord( + deviceId, + event.scheduleId, + ); + + if (success) { + final updatedSchedules = currentState.schedules + .where((s) => s.scheduleId != event.scheduleId) + .toList(); + emit(currentState.copyWith( + schedules: updatedSchedules, + countdownHours: 0, + countdownMinutes: 0, + inchingHours: 0, + inchingMinutes: 0, + countdownRemaining: Duration.zero, + )); + } else { + emit(const ScheduleError('Failed to delete schedule')); + } + } + } catch (e) { + emit(ScheduleError('Failed to delete schedule: $e')); + } + } + + Duration? _currentCountdown; + + Future _onStartScheduleEvent( + StartScheduleEvent event, + Emitter emit, + ) async { + if (state is ScheduleLoaded) { + final totalSeconds = + Duration(hours: event.hours, minutes: event.minutes).inSeconds; + final code = event.mode == ScheduleModes.countdown + ? 'countdown_1' + : 'switch_inching'; + final currentState = state as ScheduleLoaded; + final duration = Duration(seconds: totalSeconds); + _currentCountdown = duration; + emit(currentState.copyWith( + countdownRemaining: duration, + schedules: currentState.schedules.map((schedule) { + if (schedule.function.code == code) { + return schedule.copyWith( + function: Status(code: code, value: totalSeconds), + ); + } + return schedule; + }).toList(), + countdownHours: event.mode == ScheduleModes.countdown ? event.hours : 0, + )); + + final success = await RemoteControlDeviceService().controlDevice( + deviceUuid: deviceId, + status: Status( + code: code, + value: totalSeconds, + ), + ); + + if (success) { + if (code == 'countdown_1') { + final countdownDuration = Duration(seconds: totalSeconds); + + emit( + currentState.copyWith( + countdownHours: countdownDuration.inHours, + countdownMinutes: countdownDuration.inMinutes % 60, + countdownRemaining: countdownDuration, + isCountdownActive: true, + ), + ); + + if (countdownDuration.inSeconds > 0) { + _startCountdownTimer(emit, countdownDuration); + } else { + _countdownTimer?.cancel(); + emit( + currentState.copyWith( + countdownHours: 0, + countdownMinutes: 0, + countdownRemaining: Duration.zero, + isCountdownActive: false, + ), + ); + } + } else if (code == 'switch_inching') { + final inchingDuration = Duration(seconds: totalSeconds); + emit( + currentState.copyWith( + inchingHours: inchingDuration.inHours, + inchingMinutes: inchingDuration.inMinutes % 60, + isInchingActive: true, + countdownRemaining: inchingDuration, + ), + ); + } + } + } + } + + void _startCountdownTimer( + Emitter emit, + Duration duration, + ) { + _countdownTimer?.cancel(); + _countdownTimer = Timer.periodic(const Duration(seconds: 1), (timer) { + if (_currentCountdown != null && _currentCountdown! > Duration.zero) { + _currentCountdown = _currentCountdown! - const Duration(seconds: 1); + countdownRemaining = _currentCountdown!; + add(const ScheduleDecrementCountdownEvent()); + } else { + timer.cancel(); + add(StopScheduleEvent( + mode: _currentCountdown == null + ? ScheduleModes.countdown + : ScheduleModes.inching, + deviceId: deviceId, + )); + } + }); + } + + void _onDecrementCountdown( + ScheduleDecrementCountdownEvent event, + Emitter emit, + ) { + if (state is ScheduleLoaded) { + final currentState = state as ScheduleLoaded; + emit(currentState.copyWith( + countdownRemaining: countdownRemaining, + )); + } + } + + @override + Future close() { + _countdownTimer?.cancel(); + return super.close(); + } + + Future _fetchStatus( + ScheduleFetchStatusEvent event, + Emitter emit, + ) async { + emit(ScheduleLoading()); + + try { + final status = + await DevicesManagementApi().getDeviceStatus(event.deviceId); + final deviceStatus = + WaterHeaterStatusModel.fromJson(event.deviceId, status.status); + + final scheduleMode = deviceStatus.scheduleMode; + final isCountdown = scheduleMode == ScheduleModes.countdown; + final isInching = scheduleMode == ScheduleModes.inching; + + Duration? countdownRemaining; + var isCountdownActive = false; + var isInchingActive = false; + + if (isCountdown) { + countdownRemaining = Duration( + hours: deviceStatus.countdownHours, + minutes: deviceStatus.countdownMinutes, + ); + isCountdownActive = countdownRemaining > Duration.zero; + } else if (isInching) { + isInchingActive = Duration( + hours: deviceStatus.inchingHours, + minutes: deviceStatus.inchingMinutes, + ) > + Duration.zero; + } + if (state is ScheduleLoaded) { + final currentState = state as ScheduleLoaded; + emit(currentState.copyWith( + scheduleMode: scheduleMode, + countdownHours: deviceStatus.countdownHours, + countdownMinutes: deviceStatus.countdownMinutes, + inchingHours: deviceStatus.inchingHours, + inchingMinutes: deviceStatus.inchingMinutes, + isCountdownActive: isCountdownActive, + isInchingActive: isInchingActive, + countdownRemaining: countdownRemaining ?? Duration.zero, + )); + } else { + emit(ScheduleLoaded( + schedules: const [], + selectedTime: null, + selectedDays: List.filled(7, false), + functionOn: false, + isEditing: false, + deviceId: deviceId, + scheduleMode: scheduleMode, + countdownHours: deviceStatus.countdownHours, + countdownMinutes: deviceStatus.countdownMinutes, + inchingHours: deviceStatus.inchingHours, + inchingMinutes: deviceStatus.inchingMinutes, + isCountdownActive: isCountdownActive, + isInchingActive: isInchingActive, + countdownRemaining: countdownRemaining ?? Duration.zero, + )); + } + + if (isCountdownActive && countdownRemaining != null) { + _startCountdownTimer(emit, countdownRemaining); + } + } catch (e) { + emit(ScheduleError('Failed to fetch device status: $e')); + } + } + +} diff --git a/lib/pages/device_managment/schedule_device/bloc/schedule_event.dart b/lib/pages/device_managment/schedule_device/bloc/schedule_event.dart new file mode 100644 index 00000000..369ca795 --- /dev/null +++ b/lib/pages/device_managment/schedule_device/bloc/schedule_event.dart @@ -0,0 +1,221 @@ +part of 'schedule_bloc.dart'; + +abstract class ScheduleEvent extends Equatable { + const ScheduleEvent(); +} + +class ScheduleInitializeAddEvent extends ScheduleEvent { + final bool isEditing; + final ScheduleModes scheduleMode; + final TimeOfDay? selectedTime; + final List? selectedDays; + final bool? functionOn; + + const ScheduleInitializeAddEvent({ + required this.isEditing, + required this.scheduleMode, + this.selectedTime, + this.selectedDays, + this.functionOn, + }); + + @override + List get props => [ + isEditing, + scheduleMode, + selectedTime, + selectedDays, + functionOn, + ]; +} + +class ScheduleUpdateSelectedTimeEvent extends ScheduleEvent { + final TimeOfDay selectedTime; + + const ScheduleUpdateSelectedTimeEvent(this.selectedTime); + + @override + List get props => [selectedTime]; +} + +class ScheduleUpdateSelectedDayEvent extends ScheduleEvent { + final int index; + final bool value; + + const ScheduleUpdateSelectedDayEvent(this.index, this.value); + + @override + List get props => [index, value]; +} + +class ScheduleUpdateFunctionOnEvent extends ScheduleEvent { + final bool isOn; + + const ScheduleUpdateFunctionOnEvent(this.isOn); + + @override + List get props => [isOn]; +} + +class ScheduleGetEvent extends ScheduleEvent { + final String category; + + const ScheduleGetEvent({required this.category}); + + @override + List get props => [category]; +} + +class ScheduleAddEvent extends ScheduleEvent { + final String category; + final String time; + final List selectedDays; + final bool functionOn; + + const ScheduleAddEvent({ + required this.category, + required this.time, + required this.selectedDays, + required this.functionOn, + }); + + @override + List get props => [category, time, selectedDays, functionOn]; +} + +class ScheduleEditEvent extends ScheduleEvent { + final String scheduleId; + final String category; + final String time; + final List selectedDays; + final bool functionOn; + + const ScheduleEditEvent({ + required this.scheduleId, + required this.category, + required this.time, + required this.selectedDays, + required this.functionOn, + }); + + @override + List get props => [ + scheduleId, + category, + time, + selectedDays, + functionOn, + ]; +} + +class ScheduleDeleteEvent extends ScheduleEvent { + final String scheduleId; + + const ScheduleDeleteEvent(this.scheduleId); + + @override + List get props => [scheduleId]; +} + +class ScheduleUpdateEntryEvent extends ScheduleEvent { + final String scheduleId; + final bool functionOn; + final bool enable; + + const ScheduleUpdateEntryEvent({ + required this.scheduleId, + required this.functionOn, + required this.enable, + }); + + @override + List get props => [scheduleId, functionOn, enable]; +} + +class UpdateScheduleModeEvent extends ScheduleEvent { + final ScheduleModes scheduleMode; + + const UpdateScheduleModeEvent({required this.scheduleMode}); + + @override + List get props => [scheduleMode]; +} + +class UpdateCountdownTimeEvent extends ScheduleEvent { + final int hours; + final int minutes; + + const UpdateCountdownTimeEvent({ + required this.hours, + required this.minutes, + }); + + @override + List get props => [hours, minutes]; +} + +class UpdateInchingTimeEvent extends ScheduleEvent { + final int hours; + final int minutes; + + const UpdateInchingTimeEvent({ + required this.hours, + required this.minutes, + }); + + @override + List get props => [hours, minutes]; +} + +class StartScheduleEvent extends ScheduleEvent { + final ScheduleModes mode; + final int hours; + final int minutes; + + const StartScheduleEvent({ + required this.mode, + required this.hours, + required this.minutes, + }); + + @override + List get props => [mode, hours, minutes]; +} + +class StopScheduleEvent extends ScheduleEvent { + final ScheduleModes mode; + final String deviceId; + + const StopScheduleEvent({ + required this.mode, + required this.deviceId, + }); + + @override + List get props => [mode, deviceId]; +} + +class ScheduleDecrementCountdownEvent extends ScheduleEvent { + const ScheduleDecrementCountdownEvent(); + + @override + List get props => []; +} + +class ScheduleFetchStatusEvent extends ScheduleEvent { + final String deviceId; + + const ScheduleFetchStatusEvent(this.deviceId); + + @override + List get props => [deviceId]; +} + +class DeleteScheduleEvent extends ScheduleEvent { + final String scheduleId; + + const DeleteScheduleEvent(this.scheduleId); + + @override + List get props => [scheduleId]; +} diff --git a/lib/pages/device_managment/schedule_device/bloc/schedule_state.dart b/lib/pages/device_managment/schedule_device/bloc/schedule_state.dart new file mode 100644 index 00000000..10cd7611 --- /dev/null +++ b/lib/pages/device_managment/schedule_device/bloc/schedule_state.dart @@ -0,0 +1,109 @@ +part of 'schedule_bloc.dart'; + +abstract class ScheduleState extends Equatable { + const ScheduleState(); +} + +class ScheduleInitial extends ScheduleState { + @override + List get props => []; +} + +class ScheduleLoading extends ScheduleState { + @override + List get props => []; +} + +class ScheduleLoaded extends ScheduleState { + final List schedules; + final TimeOfDay? selectedTime; + final List selectedDays; + final bool functionOn; + final bool isEditing; + final String deviceId; + final int countdownHours; + final int countdownMinutes; + final bool isCountdownActive; + final int inchingHours; + final int inchingMinutes; + final bool isInchingActive; + final ScheduleModes scheduleMode; + final Duration? countdownRemaining; + + const ScheduleLoaded({ + required this.schedules, + this.selectedTime, + required this.selectedDays, + required this.functionOn, + required this.isEditing, + required this.deviceId, + this.countdownHours = 0, + this.countdownMinutes = 0, + this.isCountdownActive = false, + this.inchingHours = 0, + this.inchingMinutes = 0, + this.isInchingActive = false, + this.scheduleMode = ScheduleModes.countdown, + this.countdownRemaining, + }); + + ScheduleLoaded copyWith({ + List? schedules, + TimeOfDay? selectedTime, + List? selectedDays, + bool? functionOn, + bool? isEditing, + int? countdownHours, + int? countdownMinutes, + bool? isCountdownActive, + int? inchingHours, + int? inchingMinutes, + bool? isInchingActive, + ScheduleModes? scheduleMode, + Duration? countdownRemaining, + }) { + return ScheduleLoaded( + schedules: schedules ?? this.schedules, + selectedTime: selectedTime ?? this.selectedTime, + selectedDays: selectedDays ?? this.selectedDays, + functionOn: functionOn ?? this.functionOn, + isEditing: isEditing ?? this.isEditing, + deviceId: deviceId, + countdownHours: countdownHours ?? this.countdownHours, + countdownMinutes: countdownMinutes ?? this.countdownMinutes, + isCountdownActive: isCountdownActive ?? this.isCountdownActive, + inchingHours: inchingHours ?? this.inchingHours, + inchingMinutes: inchingMinutes ?? this.inchingMinutes, + isInchingActive: isInchingActive ?? this.isInchingActive, + scheduleMode: scheduleMode ?? this.scheduleMode, + countdownRemaining: countdownRemaining ?? this.countdownRemaining, + ); + } + + @override + List get props => [ + schedules, + selectedTime, + selectedDays, + functionOn, + isEditing, + deviceId, + countdownHours, + countdownMinutes, + isCountdownActive, + inchingHours, + inchingMinutes, + isInchingActive, + scheduleMode, + countdownRemaining, + ]; +} + +class ScheduleError extends ScheduleState { + final String error; + + const ScheduleError(this.error); + + @override + List get props => [error]; +} diff --git a/lib/pages/device_managment/water_heater/widgets/count_down_button.dart b/lib/pages/device_managment/schedule_device/schedule_widgets/count_down_button.dart similarity index 72% rename from lib/pages/device_managment/water_heater/widgets/count_down_button.dart rename to lib/pages/device_managment/schedule_device/schedule_widgets/count_down_button.dart index e60c7def..4919018c 100644 --- a/lib/pages/device_managment/water_heater/widgets/count_down_button.dart +++ b/lib/pages/device_managment/schedule_device/schedule_widgets/count_down_button.dart @@ -1,8 +1,9 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:syncrow_web/utils/color_manager.dart'; import 'package:syncrow_web/pages/common/buttons/default_button.dart'; -import 'package:syncrow_web/pages/device_managment/water_heater/bloc/water_heater_bloc.dart'; +import 'package:syncrow_web/pages/device_managment/schedule_device/bloc/schedule_bloc.dart'; +import 'package:syncrow_web/pages/device_managment/water_heater/models/water_heater_status_model.dart'; +import 'package:syncrow_web/utils/color_manager.dart'; import 'package:syncrow_web/utils/extension/build_context_x.dart'; class CountdownModeButtons extends StatelessWidget { @@ -38,14 +39,10 @@ class CountdownModeButtons extends StatelessWidget { ? DefaultButton( height: 40, onPressed: () { - context - .read() - .add(StopScheduleEvent(deviceId)); - context.read().add( - ToggleWaterHeaterEvent( + context.read().add( + StopScheduleEvent( + mode: ScheduleModes.countdown, deviceId: deviceId, - code: 'countdown_1', - value: 0, ), ); }, @@ -55,12 +52,11 @@ class CountdownModeButtons extends StatelessWidget { : DefaultButton( height: 40, onPressed: () { - context.read().add( - ToggleWaterHeaterEvent( - deviceId: deviceId, - code: 'countdown_1', - value: Duration(hours: hours, minutes: minutes) - .inSeconds, + context.read().add( + StartScheduleEvent( + mode: ScheduleModes.countdown, + hours: hours, + minutes: minutes, ), ); }, diff --git a/lib/pages/device_managment/schedule_device/schedule_widgets/count_down_inching_view.dart b/lib/pages/device_managment/schedule_device/schedule_widgets/count_down_inching_view.dart new file mode 100644 index 00000000..d45073ec --- /dev/null +++ b/lib/pages/device_managment/schedule_device/schedule_widgets/count_down_inching_view.dart @@ -0,0 +1,185 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:syncrow_web/pages/device_managment/schedule_device/bloc/schedule_bloc.dart'; +import 'package:syncrow_web/pages/device_managment/water_heater/models/water_heater_status_model.dart'; +import 'package:syncrow_web/utils/color_manager.dart'; +import 'package:syncrow_web/utils/extension/build_context_x.dart'; + +class CountdownInchingView extends StatefulWidget { + const CountdownInchingView({super.key}); + + @override + State createState() => _CountdownInchingViewState(); +} + +class _CountdownInchingViewState extends State { + late FixedExtentScrollController _hoursController; + late FixedExtentScrollController _minutesController; + + int _lastHours = -1; + int _lastMinutes = -1; + + @override + void initState() { + super.initState(); + _hoursController = FixedExtentScrollController(); + _minutesController = FixedExtentScrollController(); + } + + @override + void dispose() { + _hoursController.dispose(); + _minutesController.dispose(); + super.dispose(); + } + + void _updateControllers(int displayHours, int displayMinutes) { + if (_lastHours != displayHours) { + WidgetsBinding.instance.addPostFrameCallback((_) { + if (_hoursController.hasClients) { + _hoursController.jumpToItem(displayHours); + } + }); + _lastHours = displayHours; + } + if (_lastMinutes != displayMinutes) { + WidgetsBinding.instance.addPostFrameCallback((_) { + if (_minutesController.hasClients) { + _minutesController.jumpToItem(displayMinutes); + } + }); + _lastMinutes = displayMinutes; + } + } + + @override + Widget build(BuildContext context) { + return BlocBuilder( + builder: (context, state) { + if (state is! ScheduleLoaded) return const SizedBox.shrink(); + + final isCountDown = state.scheduleMode == ScheduleModes.countdown; + final isActive = + isCountDown ? state.isCountdownActive : state.isInchingActive; + final displayHours = isActive && state.countdownRemaining != null + ? state.countdownRemaining!.inHours + : (isCountDown ? state.countdownHours : state.inchingHours); + final displayMinutes = isActive && state.countdownRemaining != null + ? state.countdownRemaining!.inMinutes.remainder(60) + : (isCountDown ? state.countdownMinutes : state.inchingMinutes); + + _updateControllers(displayHours, displayMinutes); + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + isCountDown ? 'Countdown:' : 'Inching:', + style: context.textTheme.bodySmall!.copyWith( + fontSize: 13, + color: ColorsManager.grayColor, + ), + ), + const SizedBox(height: 8), + Visibility( + visible: !isCountDown, + child: const Text( + 'Once enabled this feature, each time the device is turned on, ' + 'it will automatically turn off after a preset time.', + ), + ), + const SizedBox(height: 8), + Row( + mainAxisAlignment: MainAxisAlignment.start, + children: [ + _buildPickerColumn( + context, + 'h', + displayHours, + 100, + _hoursController, + (value) { + if (!isActive) { + context.read().add(UpdateCountdownTimeEvent( + hours: value, minutes: displayMinutes)); + } + }, + isActive: isActive, + ), + const SizedBox(width: 10), + _buildPickerColumn( + context, + 'm', + displayMinutes, + 60, + _minutesController, + (value) { + if (!isActive) { + context.read().add(UpdateCountdownTimeEvent( + hours: displayHours, minutes: value)); + } + }, + isActive: isActive, + ), + ], + ), + ], + ); + }, + ); + } + + Widget _buildPickerColumn( + BuildContext context, + String label, + int initialValue, + int itemCount, + FixedExtentScrollController controller, + ValueChanged onSelected, { + required bool isActive, + }) { + return Row( + mainAxisSize: MainAxisSize.min, + children: [ + Container( + height: 40, + width: 80, + padding: const EdgeInsets.symmetric(horizontal: 16), + decoration: BoxDecoration( + color: ColorsManager.boxColor, + borderRadius: BorderRadius.circular(8), + ), + child: ListWheelScrollView.useDelegate( + controller: controller, + itemExtent: 40.0, + physics: isActive + ? const NeverScrollableScrollPhysics() + : const FixedExtentScrollPhysics(), + onSelectedItemChanged: isActive ? null : onSelected, + childDelegate: ListWheelChildBuilderDelegate( + builder: (context, index) { + return Center( + child: Text( + index.toString().padLeft(2, '0'), + style: TextStyle( + fontSize: 24, + color: isActive ? ColorsManager.grayColor : Colors.black, + ), + ), + ); + }, + childCount: itemCount, + ), + ), + ), + const SizedBox(width: 8), + Text( + label, + style: const TextStyle( + color: ColorsManager.grayColor, + fontSize: 18, + ), + ), + ], + ); + } +} diff --git a/lib/pages/device_managment/water_heater/widgets/inching_mode_buttons.dart b/lib/pages/device_managment/schedule_device/schedule_widgets/inching_mode_buttons.dart similarity index 82% rename from lib/pages/device_managment/water_heater/widgets/inching_mode_buttons.dart rename to lib/pages/device_managment/schedule_device/schedule_widgets/inching_mode_buttons.dart index 8eec5cca..e75c5d46 100644 --- a/lib/pages/device_managment/water_heater/widgets/inching_mode_buttons.dart +++ b/lib/pages/device_managment/schedule_device/schedule_widgets/inching_mode_buttons.dart @@ -1,8 +1,11 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:syncrow_web/utils/color_manager.dart'; import 'package:syncrow_web/pages/common/buttons/default_button.dart'; -import 'package:syncrow_web/pages/device_managment/water_heater/bloc/water_heater_bloc.dart'; +import 'package:syncrow_web/pages/device_managment/schedule_device/bloc/schedule_bloc.dart'; +import 'package:syncrow_web/pages/device_managment/water_heater/models/water_heater_status_model.dart'; +import 'package:syncrow_web/utils/color_manager.dart'; +import 'package:syncrow_web/pages/device_managment/water_heater/bloc/water_heater_bloc.dart' + hide StopScheduleEvent; import 'package:syncrow_web/utils/extension/build_context_x.dart'; class InchingModeButtons extends StatelessWidget { @@ -38,15 +41,9 @@ class InchingModeButtons extends StatelessWidget { ? DefaultButton( height: 40, onPressed: () { - context - .read() - .add(StopScheduleEvent(deviceId)); - context.read().add( - ToggleWaterHeaterEvent( - deviceId: deviceId, - code: 'switch_inching', - value: 0, - ), + context.read().add( + StopScheduleEvent( + deviceId: deviceId, mode: ScheduleModes.inching), ); }, backgroundColor: Colors.red, diff --git a/lib/pages/device_managment/schedule_device/schedule_widgets/schedual_view.dart b/lib/pages/device_managment/schedule_device/schedule_widgets/schedual_view.dart new file mode 100644 index 00000000..591b114f --- /dev/null +++ b/lib/pages/device_managment/schedule_device/schedule_widgets/schedual_view.dart @@ -0,0 +1,107 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:syncrow_web/pages/device_managment/schedule_device/bloc/schedule_bloc.dart'; +import 'package:syncrow_web/pages/device_managment/schedule_device/schedule_widgets/count_down_button.dart'; +import 'package:syncrow_web/pages/device_managment/schedule_device/schedule_widgets/count_down_inching_view.dart'; +import 'package:syncrow_web/pages/device_managment/schedule_device/schedule_widgets/inching_mode_buttons.dart'; +import 'package:syncrow_web/pages/device_managment/schedule_device/schedule_widgets/schedule_header.dart'; +import 'package:syncrow_web/pages/device_managment/schedule_device/schedule_widgets/schedule_managment_ui.dart'; +import 'package:syncrow_web/pages/device_managment/schedule_device/schedule_widgets/schedule_mode_buttons.dart'; +import 'package:syncrow_web/pages/device_managment/schedule_device/schedule_widgets/schedule_mode_selector.dart'; +import 'package:syncrow_web/pages/device_managment/water_heater/helper/add_schedule_dialog_helper.dart'; +import 'package:syncrow_web/pages/device_managment/water_heater/models/water_heater_status_model.dart'; + +class BuildScheduleView extends StatelessWidget { + const BuildScheduleView({super.key, required this.deviceUuid}); + final String deviceUuid; + + @override + Widget build(BuildContext context) { + return BlocProvider( + create: (_) => ScheduleBloc( + deviceId: deviceUuid, + ) + ..add(const ScheduleGetEvent(category: "switch_1")) + ..add(ScheduleFetchStatusEvent(deviceUuid)), + child: Dialog( + backgroundColor: Colors.white, + insetPadding: const EdgeInsets.all(20), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(20), + ), + child: SizedBox( + width: 700, + child: SingleChildScrollView( + child: Padding( + padding: + const EdgeInsets.symmetric(horizontal: 40.0, vertical: 20), + child: BlocBuilder( + builder: (context, state) { + if (state is ScheduleLoaded) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const ScheduleHeader(), + const SizedBox(height: 20), + ScheduleModeSelector( + currentMode: state.scheduleMode, + ), + const SizedBox(height: 20), + if (state.scheduleMode == ScheduleModes.schedule) + ScheduleManagementUI( + deviceUuid: deviceUuid, + onAddSchedule: () async { + final entry = await ScheduleDialogHelper + .showAddScheduleDialog( + context, + schedule: null, + isEdit: false, + ); + if (entry != null) { + context.read().add( + ScheduleAddEvent( + category: entry.category, + time: entry.time, + functionOn: entry.function.value, + selectedDays: entry.days, + ), + ); + } + }, + ), + if (state.scheduleMode == ScheduleModes.countdown || + state.scheduleMode == ScheduleModes.inching) + const CountdownInchingView(), + const SizedBox(height: 20), + if (state.scheduleMode == ScheduleModes.countdown) + CountdownModeButtons( + isActive: state.isCountdownActive, + deviceId: deviceUuid, + hours: state.countdownHours, + minutes: state.countdownMinutes, + ), + if (state.scheduleMode == ScheduleModes.inching) + InchingModeButtons( + isActive: state.isInchingActive, + deviceId: deviceUuid, + hours: state.inchingHours, + minutes: state.inchingMinutes, + ), + if (state.scheduleMode != ScheduleModes.countdown && + state.scheduleMode != ScheduleModes.inching) + ScheduleModeButtons( + onSave: () => Navigator.pop(context), + ), + ], + ); + } + return const Center(child: CircularProgressIndicator()); + }, + ), + ), + ), + ), + ), + ); + } +} diff --git a/lib/pages/device_managment/water_heater/widgets/schedule_header.dart b/lib/pages/device_managment/schedule_device/schedule_widgets/schedule_header.dart similarity index 100% rename from lib/pages/device_managment/water_heater/widgets/schedule_header.dart rename to lib/pages/device_managment/schedule_device/schedule_widgets/schedule_header.dart diff --git a/lib/pages/device_managment/water_heater/widgets/schedule_managment_ui.dart b/lib/pages/device_managment/schedule_device/schedule_widgets/schedule_managment_ui.dart similarity index 76% rename from lib/pages/device_managment/water_heater/widgets/schedule_managment_ui.dart rename to lib/pages/device_managment/schedule_device/schedule_widgets/schedule_managment_ui.dart index 1710c439..b60f00b9 100644 --- a/lib/pages/device_managment/water_heater/widgets/schedule_managment_ui.dart +++ b/lib/pages/device_managment/schedule_device/schedule_widgets/schedule_managment_ui.dart @@ -1,17 +1,16 @@ import 'package:flutter/material.dart'; import 'package:syncrow_web/pages/common/buttons/default_button.dart'; -import 'package:syncrow_web/pages/device_managment/water_heater/bloc/water_heater_bloc.dart'; -import 'package:syncrow_web/pages/device_managment/water_heater/widgets/schedule_table.dart'; +import 'package:syncrow_web/pages/device_managment/schedule_device/schedule_widgets/schedule_table.dart'; import 'package:syncrow_web/utils/color_manager.dart'; import 'package:syncrow_web/utils/extension/build_context_x.dart'; class ScheduleManagementUI extends StatelessWidget { - final WaterHeaterDeviceStatusLoaded state; - final Function onAddSchedule; + final String deviceUuid; + final VoidCallback onAddSchedule; const ScheduleManagementUI({ super.key, - required this.state, + required this.deviceUuid, required this.onAddSchedule, }); @@ -28,7 +27,7 @@ class ScheduleManagementUI extends StatelessWidget { padding: 2, backgroundColor: ColorsManager.graysColor, borderRadius: 15, - onPressed: () => onAddSchedule(), + onPressed: onAddSchedule, child: Row( children: [ const Icon(Icons.add, color: ColorsManager.primaryColor), @@ -43,7 +42,7 @@ class ScheduleManagementUI extends StatelessWidget { ), ), const SizedBox(height: 20), - ScheduleTableWidget(state: state), + ScheduleTableWidget(deviceUuid: deviceUuid), ], ); } diff --git a/lib/pages/device_managment/water_heater/widgets/schedule_mode_buttons.dart b/lib/pages/device_managment/schedule_device/schedule_widgets/schedule_mode_buttons.dart similarity index 100% rename from lib/pages/device_managment/water_heater/widgets/schedule_mode_buttons.dart rename to lib/pages/device_managment/schedule_device/schedule_widgets/schedule_mode_buttons.dart diff --git a/lib/pages/device_managment/schedule_device/schedule_widgets/schedule_mode_selector.dart b/lib/pages/device_managment/schedule_device/schedule_widgets/schedule_mode_selector.dart new file mode 100644 index 00000000..2bcc0957 --- /dev/null +++ b/lib/pages/device_managment/schedule_device/schedule_widgets/schedule_mode_selector.dart @@ -0,0 +1,87 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:syncrow_web/pages/device_managment/schedule_device/bloc/schedule_bloc.dart'; +import 'package:syncrow_web/pages/device_managment/water_heater/models/water_heater_status_model.dart'; +import 'package:syncrow_web/utils/color_manager.dart'; +import 'package:syncrow_web/utils/extension/build_context_x.dart'; + +class ScheduleModeSelector extends StatelessWidget { + final ScheduleModes currentMode; + + const ScheduleModeSelector({ + super.key, + required this.currentMode, + }); + + @override + Widget build(BuildContext context) { + final currentMode = context.select( + (bloc) => bloc.state is ScheduleLoaded && + (bloc.state as ScheduleLoaded).scheduleMode != null + ? (bloc.state as ScheduleLoaded).scheduleMode + : ScheduleModes.schedule, + ); + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + 'Type:', + style: context.textTheme.bodySmall!.copyWith( + fontSize: 13, + color: ColorsManager.grayColor, + ), + ), + const SizedBox(height: 4), + Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + _buildRadioTile( + context, 'Countdown', ScheduleModes.countdown, currentMode), + _buildRadioTile( + context, 'Schedule', ScheduleModes.schedule, currentMode), + _buildRadioTile( + context, 'Circulate', ScheduleModes.circulate, currentMode), + _buildRadioTile( + context, 'Inching', ScheduleModes.inching, currentMode), + ], + ), + ], + ); + } + + Widget _buildRadioTile( + BuildContext context, + String label, + ScheduleModes mode, + ScheduleModes currentMode, + ) { + return Flexible( + child: ListTile( + contentPadding: EdgeInsets.zero, + title: Text( + label, + style: context.textTheme.bodySmall!.copyWith( + fontSize: 13, + color: ColorsManager.blackColor, + ), + ), + leading: Radio( + value: mode, + groupValue: currentMode, + onChanged: (ScheduleModes? value) { + if (value != null) { + context.read().add( + UpdateScheduleModeEvent(scheduleMode: value), + ); + if (value == ScheduleModes.schedule) { + context.read().add( + const ScheduleGetEvent(category: 'switch_1'), + ); + } + } + }, + ), + ), + ); + } +} diff --git a/lib/pages/device_managment/water_heater/widgets/schedule_table.dart b/lib/pages/device_managment/schedule_device/schedule_widgets/schedule_table.dart similarity index 58% rename from lib/pages/device_managment/water_heater/widgets/schedule_table.dart rename to lib/pages/device_managment/schedule_device/schedule_widgets/schedule_table.dart index 18cbbe5a..4d36e0e2 100644 --- a/lib/pages/device_managment/water_heater/widgets/schedule_table.dart +++ b/lib/pages/device_managment/schedule_device/schedule_widgets/schedule_table.dart @@ -1,23 +1,38 @@ 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/water_heater/bloc/water_heater_bloc.dart'; +import 'package:flutter_svg/svg.dart'; +import 'package:syncrow_web/pages/device_managment/schedule_device/bloc/schedule_bloc.dart'; +import 'package:syncrow_web/pages/device_managment/water_heater/helper/add_schedule_dialog_helper.dart'; +import 'package:syncrow_web/pages/device_managment/water_heater/models/schedule_entry.dart'; import 'package:syncrow_web/pages/device_managment/water_heater/models/schedule_model.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/format_date_time.dart'; -import '../helper/add_schedule_dialog_helper.dart'; - class ScheduleTableWidget extends StatelessWidget { - final WaterHeaterDeviceStatusLoaded state; + final String deviceUuid; + final String category; const ScheduleTableWidget({ super.key, - required this.state, + required this.deviceUuid, + this.category = 'switch_1', }); + @override + Widget build(BuildContext context) { + return BlocProvider( + create: (_) => ScheduleBloc( + deviceId: deviceUuid, + )..add(ScheduleGetEvent(category: category)), + child: _ScheduleTableView(), + ); + } +} + +class _ScheduleTableView extends StatelessWidget { + const _ScheduleTableView(); + @override Widget build(BuildContext context) { return Column( @@ -47,17 +62,17 @@ class ScheduleTableWidget extends StatelessWidget { ), ], ), - BlocBuilder( + BlocBuilder( builder: (context, state) { - if (state is ScheduleLoadingState) { + if (state is ScheduleLoading) { return const SizedBox( height: 200, child: Center(child: CircularProgressIndicator())); } - if (state is WaterHeaterDeviceStatusLoaded && - state.schedules.isEmpty) { + if (state is ScheduleLoaded && state.schedules.isEmpty) { return _buildEmptyState(context); - } else if (state is WaterHeaterDeviceStatusLoaded) { + } + if (state is ScheduleLoaded) { return Container( height: 200, decoration: BoxDecoration( @@ -66,11 +81,12 @@ class ScheduleTableWidget extends StatelessWidget { bottomLeft: Radius.circular(20), bottomRight: Radius.circular(20)), ), - child: _buildTableBody(state, context)); + child: _buildTableBody(state.schedules, context)); } - return const SizedBox( - height: 200, - ); + if (state is ScheduleError) { + return Center(child: Text(state.error)); + } + return const SizedBox(height: 200); }, ), ], @@ -95,10 +111,10 @@ class ScheduleTableWidget extends StatelessWidget { padding: const EdgeInsets.all(8.0), child: Text( 'No schedules added yet', - style: context.textTheme.bodySmall!.copyWith( - fontSize: 13, - color: ColorsManager.grayColor, - ), + style: Theme.of(context).textTheme.bodySmall!.copyWith( + fontSize: 13, + color: ColorsManager.grayColor, + ), ), ), ], @@ -107,8 +123,7 @@ class ScheduleTableWidget extends StatelessWidget { ); } - Widget _buildTableBody( - WaterHeaterDeviceStatusLoaded state, BuildContext context) { + Widget _buildTableBody(List schedules, BuildContext context) { return SizedBox( height: 200, child: SingleChildScrollView( @@ -116,8 +131,8 @@ class ScheduleTableWidget extends StatelessWidget { border: TableBorder.all(color: ColorsManager.graysColor), defaultVerticalAlignment: TableCellVerticalAlignment.middle, children: [ - for (int i = 0; i < state.schedules.length; i++) - _buildScheduleRow(state.schedules[i], i, context, state), + for (int i = 0; i < schedules.length; i++) + _buildScheduleRow(schedules[i], i, context), ], ), ), @@ -139,20 +154,23 @@ class ScheduleTableWidget extends StatelessWidget { ); } - TableRow _buildScheduleRow(ScheduleModel schedule, int index, - BuildContext context, WaterHeaterDeviceStatusLoaded state) { + TableRow _buildScheduleRow( + ScheduleModel schedule, int index, BuildContext context) { return TableRow( children: [ Center( child: GestureDetector( onTap: () { - context.read().add(UpdateScheduleEntryEvent( - index: index, - enable: !schedule.enable, - scheduleId: schedule.scheduleId, - deviceId: state.status.uuid, - functionOn: schedule.function.value, - )); + ///TODO: Implement toggle functionality + + // Toggle enabled state using ScheduleBloc + // context.read().add( + // UpdateScheduleEvent( + // scheduleId: schedule.scheduleId, + // functionOn: schedule.function.value, + // enable: !schedule.enable, + // ), + // ); }, child: SizedBox( width: 24, @@ -179,26 +197,46 @@ class ScheduleTableWidget extends StatelessWidget { TextButton( style: TextButton.styleFrom(padding: EdgeInsets.zero), onPressed: () { - ScheduleDialogHelper.showAddScheduleDialog(context, - schedule: schedule, index: index, isEdit: true); + ScheduleDialogHelper.showAddScheduleDialog( + context, + schedule: ScheduleEntry.fromScheduleModel(schedule), + isEdit: true, + ).then((updatedSchedule) { + print('updatedSchedule : $updatedSchedule'); + if (updatedSchedule != null) { + context.read().add( + ScheduleEditEvent( + scheduleId: schedule.scheduleId, + category: schedule.category, + time: updatedSchedule.time, + functionOn: updatedSchedule.function.value, + selectedDays: updatedSchedule.days), + ); + } + }); }, child: Text( 'Edit', - style: context.textTheme.bodySmall! + style: Theme.of(context) + .textTheme + .bodySmall! .copyWith(color: ColorsManager.blueColor), ), ), TextButton( style: TextButton.styleFrom(padding: EdgeInsets.zero), onPressed: () { - context.read().add(DeleteScheduleEvent( - index: index, - scheduleId: schedule.scheduleId, - )); + context.read().add( + DeleteScheduleEvent( + schedule.scheduleId, + ), + ); }, child: Text( 'Delete', - style: context.textTheme.bodySmall! + style: Theme.of(context) + .textTheme + .bodySmall! .copyWith(color: ColorsManager.blueColor), ), ), @@ -210,13 +248,15 @@ class ScheduleTableWidget extends StatelessWidget { } String _getSelectedDays(List selectedDays) { - final days = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat']; - List selectedDaysStr = []; - for (int i = 0; i < selectedDays.length; i++) { - if (selectedDays[i]) { - selectedDaysStr.add(days[i]); - } - } - return selectedDaysStr.join(', '); + // Use the same order as in ScheduleDialogHelper + const days = ScheduleDialogHelper.allDays; + return selectedDays + .asMap() + .entries + .where((entry) => entry.value) + .map((entry) => days[entry.key]) + .join(', '); } + + // Removed allDays from here as it is now in ScheduleDialogHelper } diff --git a/lib/pages/device_managment/water_heater/helper/add_schedule_dialog_helper.dart b/lib/pages/device_managment/water_heater/helper/add_schedule_dialog_helper.dart index 9278e396..b09cb48c 100644 --- a/lib/pages/device_managment/water_heater/helper/add_schedule_dialog_helper.dart +++ b/lib/pages/device_managment/water_heater/helper/add_schedule_dialog_helper.dart @@ -1,240 +1,210 @@ import 'package:flutter/material.dart'; -import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:syncrow_web/pages/common/buttons/default_button.dart'; -import 'package:syncrow_web/pages/device_managment/water_heater/bloc/water_heater_bloc.dart'; -import 'package:syncrow_web/pages/device_managment/water_heater/models/schedule_model.dart'; -import 'package:syncrow_web/utils/color_manager.dart'; -import 'package:syncrow_web/utils/extension/build_context_x.dart'; +import 'package:syncrow_web/pages/device_managment/water_heater/models/schedule_entry.dart'; +import 'package:syncrow_web/pages/device_managment/all_devices/models/device_status.dart'; class ScheduleDialogHelper { - static void showAddScheduleDialog(BuildContext context, {ScheduleModel? schedule, int? index, bool? isEdit}) { - final bloc = context.read(); + static const List allDays = [ + 'Sun', + 'Mon', + 'Tue', + 'Wed', + 'Thu', + 'Fri', + 'Sat' + ]; - if (schedule == null) { - bloc.add((const UpdateSelectedTimeEvent(null))); - bloc.add(InitializeAddScheduleEvent( - selectedTime: null, - selectedDays: List.filled(7, false), - functionOn: false, - isEditing: false, - )); - } else { - final time = _convertStringToTimeOfDay(schedule.time); - final selectedDays = _convertDaysStringToBooleans(schedule.days); + static Future showAddScheduleDialog( + BuildContext context, { + ScheduleEntry? schedule, + bool isEdit = false, + }) { + final initialTime = schedule != null + ? _convertStringToTimeOfDay(schedule.time) + : TimeOfDay.now(); + final initialDays = schedule != null + ? _convertDaysStringToBooleans(schedule.days) + : List.filled(7, false); + bool? functionOn = schedule?.function.value ?? true; + TimeOfDay selectedTime = initialTime; + List selectedDays = List.of(initialDays); - bloc.add(InitializeAddScheduleEvent( - selectedTime: time, - selectedDays: selectedDays, - functionOn: schedule.function.value, - isEditing: true, - index: index, - )); - } - - showDialog( + return showDialog( context: context, builder: (ctx) { - return BlocProvider.value( - value: bloc, - child: BlocBuilder( - builder: (context, state) { - if (state is WaterHeaterDeviceStatusLoaded) { - return AlertDialog( - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(20), - ), - content: Column( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.start, + return StatefulBuilder( + builder: (ctx, setState) { + return AlertDialog( + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(20), + ), + content: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - const SizedBox(), - Text( - 'Scheduling', - style: context.textTheme.titleLarge!.copyWith( - color: ColorsManager.dialogBlueTitle, + const SizedBox(), + Text( + isEdit ? 'Edit Schedule' : 'Add Schedule', + style: Theme.of(context).textTheme.titleLarge!.copyWith( + color: Colors.blue, fontWeight: FontWeight.bold, ), - ), - const SizedBox(), - ], ), - const SizedBox(height: 24), - SizedBox( - width: 150, - height: 40, - child: DefaultButton( - padding: 8, - backgroundColor: ColorsManager.boxColor, - borderRadius: 15, - onPressed: () async { - TimeOfDay? time = await showTimePicker( - context: context, - initialTime: state.selectedTime ?? TimeOfDay.now(), - builder: (context, child) { - return Theme( - data: Theme.of(context).copyWith( - colorScheme: const ColorScheme.light( - primary: ColorsManager.primaryColor, - ), - ), - child: child!, - ); - }, - ); - if (time != null) { - bloc.add(UpdateSelectedTimeEvent(time)); - } - }, - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Text( - state.selectedTime == null ? 'Time' : state.selectedTime!.format(context), - style: context.textTheme.bodySmall!.copyWith( - color: ColorsManager.grayColor, - ), - ), - const Icon( - Icons.access_time, - color: ColorsManager.grayColor, - size: 18, - ), - ], - ), - ), - ), - const SizedBox(height: 16), - _buildDayCheckboxes(context, state.selectedDays, isEdit: isEdit), - const SizedBox(height: 16), - _buildFunctionSwitch(context, state.functionOn, isEdit), + const SizedBox(), ], ), - actions: [ - SizedBox( - width: 200, - child: DefaultButton( - height: 40, - onPressed: () { - Navigator.pop(context); - }, - backgroundColor: ColorsManager.boxColor, - child: Text( - 'Cancel', - style: context.textTheme.bodyMedium, + const SizedBox(height: 24), + SizedBox( + width: 150, + height: 40, + child: ElevatedButton( + style: ElevatedButton.styleFrom( + backgroundColor: Colors.grey[200], + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(15), ), ), - ), - SizedBox( - width: 200, - child: DefaultButton( - height: 40, - onPressed: () { - if (state.selectedTime != null) { - if (state.isEditing && index != null) { - bloc.add(EditWaterHeaterScheduleEvent( - scheduleId: schedule?.scheduleId ?? '', - category: 'switch_1', - time: state.selectedTime!, - selectedDays: state.selectedDays, - functionOn: state.functionOn, - )); - } else { - bloc.add(AddScheduleEvent( - category: 'switch_1', - time: state.selectedTime!, - selectedDays: state.selectedDays, - functionOn: state.functionOn, - )); - } - Navigator.pop(context); - } - }, - backgroundColor: ColorsManager.primaryColor, - child: const Text('Save'), + onPressed: () async { + TimeOfDay? time = await showTimePicker( + context: ctx, + initialTime: selectedTime, + ); + if (time != null) { + setState(() => selectedTime = time); + } + }, + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + selectedTime.format(context), + style: Theme.of(context) + .textTheme + .bodySmall! + .copyWith(color: Colors.grey), + ), + const Icon(Icons.access_time, + color: Colors.grey, size: 18), + ], ), ), - ], - ); - } - return const SizedBox(); - }, - ), + ), + const SizedBox(height: 16), + _buildDayCheckboxes(ctx, selectedDays, (i, v) { + setState(() => selectedDays[i] = v); + }), + const SizedBox(height: 16), + _buildFunctionSwitch(ctx, functionOn, (v) { + setState(() => functionOn = v); + }), + ], + ), + actions: [ + SizedBox( + width: 100, + child: OutlinedButton( + onPressed: () { + Navigator.pop(ctx, null); + }, + child: const Text('Cancel'), + ), + ), + SizedBox( + width: 100, + child: ElevatedButton( + onPressed: () { + final entry = ScheduleEntry( + category: schedule?.category ?? 'switch_1', + time: _formatTimeOfDayToISO(selectedTime), + function: Status(code: 'switch_1', value: functionOn), + days: _convertSelectedDaysToStrings(selectedDays), + scheduleId: schedule?.scheduleId, + ); + Navigator.pop(ctx, entry); + }, + child: const Text('Save'), + ), + ), + ], + ); + }, ); }, ); } - static TimeOfDay _convertStringToTimeOfDay(String timeString) { - final regex = RegExp(r'^(\d{2}):(\d{2})$'); - final match = regex.firstMatch(timeString); - if (match != null) { - final hour = int.parse(match.group(1)!); - final minute = int.parse(match.group(2)!); - return TimeOfDay(hour: hour, minute: minute); - } else { - throw const FormatException('Invalid time format'); - } + static TimeOfDay _convertStringToTimeOfDay(String iso) { + final dt = DateTime.tryParse(iso); + if (dt != null) return TimeOfDay(hour: dt.hour, minute: dt.minute); + return const TimeOfDay(hour: 9, minute: 0); } static List _convertDaysStringToBooleans(List selectedDays) { final daysOfWeek = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat']; - List daysBoolean = List.filled(7, false); - - for (int i = 0; i < daysOfWeek.length; i++) { - if (selectedDays.contains(daysOfWeek[i])) { - daysBoolean[i] = true; - } - } - - return daysBoolean; + return daysOfWeek + .map((d) => + selectedDays.map((e) => e.toLowerCase()).contains(d.toLowerCase())) + .toList(); } - static Widget _buildDayCheckboxes(BuildContext context, List selectedDays, {bool? isEdit}) { - final dayLabels = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat']; + static String _formatTimeOfDayToISO(TimeOfDay t) { + final now = DateTime.now(); + final dt = DateTime(now.year, now.month, now.day, t.hour, t.minute); + return dt.toIso8601String(); + } + static List _convertSelectedDaysToStrings(List selectedDays) { + const allDays = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat']; + List result = []; + for (int i = 0; i < selectedDays.length; i++) { + if (selectedDays[i]) result.add(allDays[i]); + } + return result; + } + + static Widget _buildDayCheckboxes(BuildContext ctx, List selectedDays, + Function(int, bool) onChanged) { + final dayLabels = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat']; return Row( - children: List.generate(7, (index) { - return Row( + mainAxisAlignment: MainAxisAlignment.start, + children: List.generate( + 7, + (index) => Row( children: [ Checkbox( value: selectedDays[index], - onChanged: (bool? value) { - context.read().add(UpdateSelectedDayEvent(index, value!)); - }, + onChanged: (val) => onChanged(index, val!), ), Text(dayLabels[index]), ], - ); - }), + ), + ), ); } - static Widget _buildFunctionSwitch(BuildContext context, bool isOn, bool? isEdit) { + static Widget _buildFunctionSwitch( + BuildContext ctx, bool isOn, Function(bool) onChanged) { return Row( children: [ Text( 'Function:', - style: context.textTheme.bodySmall!.copyWith(color: ColorsManager.grayColor), + style: + Theme.of(ctx).textTheme.bodySmall!.copyWith(color: Colors.grey), ), const SizedBox(width: 10), Radio( value: true, groupValue: isOn, - onChanged: (bool? value) { - context.read().add(const UpdateFunctionOnEvent(true)); - }, + onChanged: (val) => onChanged(true), ), const Text('On'), const SizedBox(width: 10), Radio( value: false, groupValue: isOn, - onChanged: (bool? value) { - context.read().add(const UpdateFunctionOnEvent(false)); - }, + onChanged: (val) => onChanged(false), ), const Text('Off'), ], diff --git a/lib/pages/device_managment/water_heater/models/schedule_entry.dart b/lib/pages/device_managment/water_heater/models/schedule_entry.dart index a2a109af..d6a530bb 100644 --- a/lib/pages/device_managment/water_heater/models/schedule_entry.dart +++ b/lib/pages/device_managment/water_heater/models/schedule_entry.dart @@ -2,6 +2,7 @@ import 'dart:convert'; import 'package:flutter/foundation.dart'; import 'package:syncrow_web/pages/device_managment/all_devices/models/device_status.dart'; +import 'package:syncrow_web/pages/device_managment/water_heater/models/schedule_model.dart'; class ScheduleEntry { final String category; @@ -58,7 +59,8 @@ class ScheduleEntry { String toJson() => json.encode(toMap()); - factory ScheduleEntry.fromJson(String source) => ScheduleEntry.fromMap(json.decode(source)); + factory ScheduleEntry.fromJson(String source) => + ScheduleEntry.fromMap(json.decode(source)); @override bool operator ==(Object other) { @@ -73,6 +75,23 @@ class ScheduleEntry { @override int get hashCode { - return category.hashCode ^ time.hashCode ^ function.hashCode ^ days.hashCode; + return category.hashCode ^ + time.hashCode ^ + function.hashCode ^ + days.hashCode; + } + + // Existing properties and methods + + // Add the fromScheduleModel method + + static ScheduleEntry fromScheduleModel(ScheduleModel scheduleModel) { + return ScheduleEntry( + days: scheduleModel.days, + time: scheduleModel.time, + function: scheduleModel.function, + category: scheduleModel.category, + scheduleId: scheduleModel.scheduleId, + ); } } diff --git a/lib/pages/device_managment/water_heater/models/water_heater_status_model.dart b/lib/pages/device_managment/water_heater/models/water_heater_status_model.dart index c535bda2..bf9ab2cd 100644 --- a/lib/pages/device_managment/water_heater/models/water_heater_status_model.dart +++ b/lib/pages/device_managment/water_heater/models/water_heater_status_model.dart @@ -16,7 +16,7 @@ class WaterHeaterStatusModel extends Equatable { final String cycleTiming; final List schedules; - const WaterHeaterStatusModel({ + const WaterHeaterStatusModel({ required this.uuid, required this.heaterSwitch, required this.countdownHours, diff --git a/lib/pages/device_managment/water_heater/view/water_heater_device_control.dart b/lib/pages/device_managment/water_heater/view/water_heater_device_control.dart index f1e56136..1d80fd9f 100644 --- a/lib/pages/device_managment/water_heater/view/water_heater_device_control.dart +++ b/lib/pages/device_managment/water_heater/view/water_heater_device_control.dart @@ -7,7 +7,7 @@ import 'package:syncrow_web/pages/device_managment/shared/toggle_widget.dart'; import 'package:syncrow_web/pages/device_managment/water_heater/bloc/water_heater_bloc.dart'; import 'package:syncrow_web/pages/device_managment/water_heater/factories/water_heater_bloc_factory.dart'; import 'package:syncrow_web/pages/device_managment/water_heater/models/water_heater_status_model.dart'; -import 'package:syncrow_web/pages/device_managment/water_heater/widgets/schedual_view.dart'; +import 'package:syncrow_web/pages/device_managment/schedule_device/schedule_widgets/schedual_view.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'; @@ -35,7 +35,8 @@ class WaterHeaterDeviceControlView extends StatelessWidget state is WaterHeaterBatchFailedState) { return const Center(child: Text('Error fetching status')); } else { - return const SizedBox(height: 200, child: Center(child: SizedBox())); + return const SizedBox( + height: 200, child: Center(child: SizedBox())); } }, )); @@ -79,7 +80,9 @@ class WaterHeaterDeviceControlView extends StatelessWidget context: context, builder: (ctx) => BlocProvider.value( value: BlocProvider.of(context), - child: BuildScheduleView(status: status), + child: BuildScheduleView( + deviceUuid: device.uuid ?? '', + ), )); }, child: DeviceControlsContainer( diff --git a/lib/pages/device_managment/water_heater/widgets/count_down_inching_view.dart b/lib/pages/device_managment/water_heater/widgets/count_down_inching_view.dart deleted file mode 100644 index 9c28d4d6..00000000 --- a/lib/pages/device_managment/water_heater/widgets/count_down_inching_view.dart +++ /dev/null @@ -1,223 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:syncrow_web/pages/device_managment/water_heater/bloc/water_heater_bloc.dart'; -import 'package:syncrow_web/pages/device_managment/water_heater/models/water_heater_status_model.dart'; -import 'package:syncrow_web/utils/color_manager.dart'; -import 'package:syncrow_web/utils/extension/build_context_x.dart'; - -class CountdownInchingView extends StatelessWidget { - final WaterHeaterDeviceStatusLoaded state; - - const CountdownInchingView({ - super.key, - required this.state, - }); - - @override - Widget build(BuildContext context) { - final isCountDown = - state.scheduleMode?.name == ScheduleModes.countdown.name; - - return Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - isCountDown ? 'Countdown:' : 'Inching:', - style: context.textTheme.bodySmall!.copyWith( - fontSize: 13, - color: ColorsManager.grayColor, - ), - ), - const SizedBox(height: 8), - Visibility( - visible: !isCountDown, - child: const Text( - 'Once enabled this feature, each time the device is turned on, it will automatically turn off after a preset time.'), - ), - const SizedBox(height: 8), - _hourMinutesWheel(context, state), - ], - ); - } - - Row _hourMinutesWheel( - BuildContext context, WaterHeaterDeviceStatusLoaded state) { - final isCountDown = - state.scheduleMode?.name == ScheduleModes.countdown.name; - late bool isActive; - if (isCountDown && - state.countdownRemaining != null && - state.isCountdownActive == true) { - isActive = true; - } else if (!isCountDown && - state.countdownRemaining != null && - state.isInchingActive == true) { - isActive = true; - } else { - isActive = false; - } - - return Row( - mainAxisAlignment: MainAxisAlignment.start, - children: [ - _buildPickerColumn( - context, - 'h', - isCountDown - ? (state.countdownHours ?? 0) - : (state.inchingHours ?? 0), - 24, (value) { - context.read().add(UpdateScheduleEvent( - scheduleMode: state.scheduleMode ?? ScheduleModes.countdown, - hours: value, - minutes: isCountDown - ? (state.countdownMinutes ?? 0) - : (state.inchingMinutes ?? 0), - )); - }, isActive: isActive), - const SizedBox(width: 10), - _buildPickerColumn( - context, - 'm', - isCountDown - ? (state.countdownMinutes ?? 0) - : (state.inchingMinutes ?? 0), - 60, (value) { - context.read().add(UpdateScheduleEvent( - scheduleMode: state.scheduleMode ?? ScheduleModes.countdown, - hours: isCountDown - ? (state.countdownHours ?? 0) - : (state.inchingHours ?? 0), - minutes: value, - )); - }, isActive: isActive), - ], - ); - } - - Row _hourMinutesSecondWheel( - BuildContext context, WaterHeaterDeviceStatusLoaded state) { - final isCountDown = - state.scheduleMode?.name == ScheduleModes.countdown.name; - late bool isActive; - if (isCountDown && - state.countdownRemaining != null && - state.isCountdownActive == true) { - isActive = true; - } else if (!isCountDown && - state.countdownRemaining != null && - state.isInchingActive == true) { - isActive = true; - } else { - isActive = false; - } - - return Row( - mainAxisAlignment: MainAxisAlignment.start, - children: [ - _buildPickerColumn( - context, - 'h', - isCountDown - ? (state.countdownHours ?? 0) - : (state.inchingHours ?? 0), - 24, (value) { - context.read().add(UpdateScheduleEvent( - scheduleMode: state.scheduleMode ?? ScheduleModes.countdown, - hours: value, - minutes: isCountDown - ? (state.countdownMinutes ?? 0) - : (state.inchingMinutes ?? 0), - )); - }, isActive: isActive), - const SizedBox(width: 10), - _buildPickerColumn( - context, - 'm', - isCountDown - ? (state.countdownMinutes ?? 0) - : (state.inchingMinutes ?? 0), - 60, (value) { - context.read().add(UpdateScheduleEvent( - scheduleMode: state.scheduleMode ?? ScheduleModes.countdown, - hours: isCountDown - ? (state.countdownHours ?? 0) - : (state.inchingHours ?? 0), - minutes: value, - )); - }, isActive: isActive), - const SizedBox(width: 10), - _buildPickerColumn( - context, - 'S', - isCountDown - ? (state.countdownMinutes ?? 0) - : (state.inchingMinutes ?? 0), - 60, (value) { - context.read().add(UpdateScheduleEvent( - scheduleMode: state.scheduleMode ?? ScheduleModes.countdown, - hours: isCountDown - ? (state.countdownHours ?? 0) - : (state.inchingHours ?? 0), - minutes: value, - )); - }, isActive: isActive), - ], - ); - } - - Widget _buildPickerColumn( - BuildContext context, - String label, - int initialValue, - int itemCount, - ValueChanged onSelected, { - required bool isActive, - }) { - return Row( - mainAxisSize: MainAxisSize.min, - children: [ - Container( - height: 40, - width: 80, - padding: const EdgeInsets.symmetric(horizontal: 16), - decoration: BoxDecoration( - color: ColorsManager.boxColor, - borderRadius: BorderRadius.circular(8), - ), - child: ListWheelScrollView.useDelegate( - key: ValueKey('$label-$initialValue'), - controller: FixedExtentScrollController( - initialItem: initialValue, - ), - itemExtent: 40.0, - physics: const FixedExtentScrollPhysics(), - onSelectedItemChanged: onSelected, - childDelegate: ListWheelChildBuilderDelegate( - builder: (context, index) { - return Center( - child: Text( - index.toString().padLeft(2, '0'), - style: TextStyle( - fontSize: 24, - color: isActive ? ColorsManager.grayColor : Colors.black, - ), - ), - ); - }, - childCount: itemCount, - ), - ), - ), - const SizedBox(width: 8), - Text( - label, - style: const TextStyle( - color: ColorsManager.grayColor, - fontSize: 18, - ), - ), - ], - ); - } -} diff --git a/lib/pages/device_managment/water_heater/widgets/schedual_view.dart b/lib/pages/device_managment/water_heater/widgets/schedual_view.dart deleted file mode 100644 index 9d4a2497..00000000 --- a/lib/pages/device_managment/water_heater/widgets/schedual_view.dart +++ /dev/null @@ -1,117 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:syncrow_web/pages/device_managment/water_heater/bloc/water_heater_bloc.dart'; -import 'package:syncrow_web/pages/device_managment/water_heater/helper/add_schedule_dialog_helper.dart'; -import 'package:syncrow_web/pages/device_managment/water_heater/models/water_heater_status_model.dart'; -import 'package:syncrow_web/pages/device_managment/water_heater/widgets/count_down_button.dart'; -import 'package:syncrow_web/pages/device_managment/water_heater/widgets/count_down_inching_view.dart'; -import 'package:syncrow_web/pages/device_managment/water_heater/widgets/inching_mode_buttons.dart'; -import 'package:syncrow_web/pages/device_managment/water_heater/widgets/schedule_header.dart'; -import 'package:syncrow_web/pages/device_managment/water_heater/widgets/schedule_managment_ui.dart'; -import 'package:syncrow_web/pages/device_managment/water_heater/widgets/schedule_mode_buttons.dart'; -import 'package:syncrow_web/pages/device_managment/water_heater/widgets/schedule_mode_selector.dart'; - -class BuildScheduleView extends StatefulWidget { - const BuildScheduleView({super.key, required this.status}); - - final WaterHeaterStatusModel status; - - @override - State createState() => _BuildScheduleViewState(); -} - -class _BuildScheduleViewState extends State { - @override - Widget build(BuildContext context) { - final bloc = BlocProvider.of(context); - - return BlocProvider.value( - value: bloc, - child: Dialog( - backgroundColor: Colors.white, - insetPadding: const EdgeInsets.all(20), - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(20), - ), - child: SizedBox( - width: 700, - child: SingleChildScrollView( - child: Padding( - padding: - const EdgeInsets.symmetric(horizontal: 40.0, vertical: 20), - child: BlocBuilder( - builder: (context, state) { - if (state is WaterHeaterDeviceStatusLoaded) { - return Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - const ScheduleHeader(), - const SizedBox(height: 20), - ScheduleModeSelector(state: state), - const SizedBox(height: 20), - if (state.scheduleMode == ScheduleModes.schedule) - ScheduleManagementUI( - state: state, - onAddSchedule: () { - ScheduleDialogHelper.showAddScheduleDialog( - context, - schedule: null, - index: null, - isEdit: false); - }, - ), - if (state.scheduleMode == ScheduleModes.countdown || - state.scheduleMode == ScheduleModes.inching) - CountdownInchingView(state: state), - const SizedBox(height: 20), - if (state.scheduleMode == ScheduleModes.countdown) - CountdownModeButtons( - isActive: state.isCountdownActive ?? false, - deviceId: widget.status.uuid, - hours: state.countdownHours ?? 0, - minutes: state.countdownMinutes ?? 0, - ), - if (state.scheduleMode == ScheduleModes.inching) - InchingModeButtons( - isActive: state.isInchingActive ?? false, - deviceId: widget.status.uuid, - hours: state.inchingHours ?? 0, - minutes: state.inchingMinutes ?? 0, - ), - if (state.scheduleMode != ScheduleModes.countdown && - state.scheduleMode != ScheduleModes.inching) - ScheduleModeButtons( - onSave: () { - Navigator.pop(context); - }, - ), - ], - ); - } - if (state is WaterHeaterLoadingState) { - return const SizedBox( - height: 200, - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - ScheduleHeader(), - SizedBox( - height: 20, - ), - Center(child: CircularProgressIndicator()), - ], - )); - } - return const SizedBox( - height: 200, - child: ScheduleHeader(), - ); - }, - ), - ), - ), - ), - ), - ); - } -} diff --git a/lib/pages/device_managment/water_heater/widgets/schedule_mode_selector.dart b/lib/pages/device_managment/water_heater/widgets/schedule_mode_selector.dart deleted file mode 100644 index bb9ddc8f..00000000 --- a/lib/pages/device_managment/water_heater/widgets/schedule_mode_selector.dart +++ /dev/null @@ -1,86 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:syncrow_web/pages/device_managment/water_heater/bloc/water_heater_bloc.dart'; -import 'package:syncrow_web/utils/color_manager.dart'; -import 'package:syncrow_web/utils/extension/build_context_x.dart'; -import 'package:syncrow_web/pages/device_managment/water_heater/models/water_heater_status_model.dart'; - -class ScheduleModeSelector extends StatelessWidget { - final WaterHeaterDeviceStatusLoaded state; - - const ScheduleModeSelector({super.key, required this.state}); - - @override - Widget build(BuildContext context) { - return Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - 'Type:', - style: context.textTheme.bodySmall!.copyWith( - fontSize: 13, - color: ColorsManager.grayColor, - ), - ), - const SizedBox(height: 4), - Row( - mainAxisAlignment: MainAxisAlignment.spaceEvenly, - children: [ - _buildRadioTile( - context, 'Countdown', ScheduleModes.countdown, state), - _buildRadioTile(context, 'Schedule', ScheduleModes.schedule, state), - _buildRadioTile( - context, 'Circulate', ScheduleModes.circulate, state), - _buildRadioTile(context, 'Inching', ScheduleModes.inching, state), - ], - ), - ], - ); - } - - Widget _buildRadioTile(BuildContext context, String label, ScheduleModes mode, - WaterHeaterDeviceStatusLoaded state) { - return Flexible( - child: ListTile( - contentPadding: EdgeInsets.zero, - title: Text( - label, - style: context.textTheme.bodySmall!.copyWith( - fontSize: 13, - color: ColorsManager.blackColor, - ), - ), - leading: Radio( - value: mode, - groupValue: state.scheduleMode, - onChanged: (ScheduleModes? value) { - if (value != null) { - if (value == ScheduleModes.countdown) { - context.read().add(UpdateScheduleEvent( - scheduleMode: value, - hours: state.countdownHours ?? 0, - minutes: state.countdownMinutes ?? 0, - )); - } else if (value == ScheduleModes.inching) { - context.read().add(UpdateScheduleEvent( - scheduleMode: value, - hours: state.inchingHours ?? 0, - minutes: state.inchingMinutes ?? 0, - )); - } - - if (value == ScheduleModes.schedule) { - context.read().add( - GetSchedulesEvent( - category: 'switch_1', - uuid: state.status.uuid, - ), - ); - } - } - }, - ), - ), - ); - } -} From 3d95f2bef0ab4817e92f2fb40cd346ef2d878b6e Mon Sep 17 00:00:00 2001 From: mohammad Date: Wed, 18 Jun 2025 16:40:13 +0300 Subject: [PATCH 2/8] Fix null safety issue by adding null check for functionOn in schedule dialog helper --- .../water_heater/helper/add_schedule_dialog_helper.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/pages/device_managment/water_heater/helper/add_schedule_dialog_helper.dart b/lib/pages/device_managment/water_heater/helper/add_schedule_dialog_helper.dart index b09cb48c..ae7feac9 100644 --- a/lib/pages/device_managment/water_heater/helper/add_schedule_dialog_helper.dart +++ b/lib/pages/device_managment/water_heater/helper/add_schedule_dialog_helper.dart @@ -96,7 +96,7 @@ class ScheduleDialogHelper { setState(() => selectedDays[i] = v); }), const SizedBox(height: 16), - _buildFunctionSwitch(ctx, functionOn, (v) { + _buildFunctionSwitch(ctx, functionOn!, (v) { setState(() => functionOn = v); }), ], From d895ed74d2a5b02ab5c6e19e79131a0c16f7d89c Mon Sep 17 00:00:00 2001 From: mohammad Date: Thu, 19 Jun 2025 10:49:06 +0300 Subject: [PATCH 3/8] Add scheduling functionality to device control views with dialog integration --- .../one_gang_glass_switch_control_view.dart | 58 ++++++++++-- .../view/wall_light_device_control.dart | 52 ++++++++++- .../three_gang_glass_switch_control_view.dart | 88 ++++++++++++++++--- .../view/living_room_device_control.dart | 50 +++++++++++ .../two_gang_glass_switch_control_view.dart | 72 ++++++++++++--- .../view/wall_light_device_control.dart | 56 ++++++++++++ 6 files changed, 342 insertions(+), 34 deletions(-) diff --git a/lib/pages/device_managment/one_g_glass_switch/view/one_gang_glass_switch_control_view.dart b/lib/pages/device_managment/one_g_glass_switch/view/one_gang_glass_switch_control_view.dart index 997be513..694c2f6e 100644 --- a/lib/pages/device_managment/one_g_glass_switch/view/one_gang_glass_switch_control_view.dart +++ b/lib/pages/device_managment/one_g_glass_switch/view/one_gang_glass_switch_control_view.dart @@ -1,10 +1,15 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:flutter_svg/svg.dart'; import 'package:syncrow_web/pages/device_managment/one_g_glass_switch/bloc/one_gang_glass_switch_bloc.dart'; import 'package:syncrow_web/pages/device_managment/one_g_glass_switch/factories/one_gang_glass_switch_bloc_factory.dart'; import 'package:syncrow_web/pages/device_managment/one_g_glass_switch/models/once_gang_glass_status_model.dart'; +import 'package:syncrow_web/pages/device_managment/schedule_device/schedule_widgets/schedual_view.dart'; +import 'package:syncrow_web/pages/device_managment/shared/device_controls_container.dart'; import 'package:syncrow_web/pages/device_managment/shared/toggle_widget.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 OneGangGlassSwitchControlView extends StatelessWidget with HelperResponsiveLayout { @@ -76,15 +81,50 @@ class OneGangGlassSwitchControlView extends StatelessWidget with HelperResponsiv onChange: (value) {}, showToggle: false, ), - ToggleWidget( - value: false, - code: '', - deviceId: deviceId, - label: 'Scheduling', - icon: Assets.scheduling, - onChange: (value) {}, - showToggle: false, - ), + GestureDetector( + onTap: () { + showDialog( + context: context, + builder: (ctx) => BlocProvider.value( + value: BlocProvider.of(context), + child: BuildScheduleView( + deviceUuid: deviceId, + ), + )); + }, + child: DeviceControlsContainer( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Container( + width: 60, + height: 60, + decoration: const BoxDecoration( + shape: BoxShape.circle, + color: ColorsManager.whiteColors, + ), + margin: const EdgeInsets.symmetric(horizontal: 4), + padding: const EdgeInsets.all(12), + child: ClipOval( + child: SvgPicture.asset( + Assets.scheduling, + fit: BoxFit.fill, + ), + ), + ), + const Spacer(), + Text( + 'Scheduling', + textAlign: TextAlign.center, + style: context.textTheme.titleMedium!.copyWith( + fontWeight: FontWeight.w400, + color: ColorsManager.blackColor, + ), + ), + ], + ), + ), + ) ], ); } diff --git a/lib/pages/device_managment/one_gang_switch/view/wall_light_device_control.dart b/lib/pages/device_managment/one_gang_switch/view/wall_light_device_control.dart index f1861c55..e590adea 100644 --- a/lib/pages/device_managment/one_gang_switch/view/wall_light_device_control.dart +++ b/lib/pages/device_managment/one_gang_switch/view/wall_light_device_control.dart @@ -1,11 +1,17 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:flutter_svg/svg.dart'; import 'package:syncrow_web/pages/device_managment/one_gang_switch/bloc/wall_light_switch_bloc.dart'; import 'package:syncrow_web/pages/device_managment/one_gang_switch/bloc/wall_light_switch_event.dart'; import 'package:syncrow_web/pages/device_managment/one_gang_switch/bloc/wall_light_switch_state.dart'; import 'package:syncrow_web/pages/device_managment/one_gang_switch/factories/wall_light_switch_bloc_factory.dart'; import 'package:syncrow_web/pages/device_managment/one_gang_switch/models/wall_light_status_model.dart'; +import 'package:syncrow_web/pages/device_managment/schedule_device/schedule_widgets/schedual_view.dart'; +import 'package:syncrow_web/pages/device_managment/shared/device_controls_container.dart'; import 'package:syncrow_web/pages/device_managment/shared/toggle_widget.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 WallLightDeviceControl extends StatelessWidget @@ -55,7 +61,6 @@ class WallLightDeviceControl extends StatelessWidget mainAxisSpacing: 12, ), children: [ - const SizedBox(), ToggleWidget( value: status.switch1, code: 'switch_1', @@ -69,7 +74,50 @@ class WallLightDeviceControl extends StatelessWidget )); }, ), - const SizedBox(), + GestureDetector( + onTap: () { + showDialog( + context: context, + builder: (ctx) => BlocProvider.value( + value: BlocProvider.of(context), + child: BuildScheduleView( + deviceUuid: deviceId, + ), + )); + }, + child: DeviceControlsContainer( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Container( + width: 60, + height: 60, + decoration: const BoxDecoration( + shape: BoxShape.circle, + color: ColorsManager.whiteColors, + ), + margin: const EdgeInsets.symmetric(horizontal: 4), + padding: const EdgeInsets.all(12), + child: ClipOval( + child: SvgPicture.asset( + Assets.scheduling, + fit: BoxFit.fill, + ), + ), + ), + const Spacer(), + Text( + 'Scheduling', + textAlign: TextAlign.center, + style: context.textTheme.titleMedium!.copyWith( + fontWeight: FontWeight.w400, + color: ColorsManager.blackColor, + ), + ), + ], + ), + ), + ) ], ); } diff --git a/lib/pages/device_managment/three_g_glass_switch/view/three_gang_glass_switch_control_view.dart b/lib/pages/device_managment/three_g_glass_switch/view/three_gang_glass_switch_control_view.dart index 21a81df0..7d80f289 100644 --- a/lib/pages/device_managment/three_g_glass_switch/view/three_gang_glass_switch_control_view.dart +++ b/lib/pages/device_managment/three_g_glass_switch/view/three_gang_glass_switch_control_view.dart @@ -1,14 +1,20 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:flutter_svg/svg.dart'; +import 'package:syncrow_web/pages/device_managment/schedule_device/schedule_widgets/schedual_view.dart'; +import 'package:syncrow_web/pages/device_managment/shared/device_controls_container.dart'; import 'package:syncrow_web/pages/device_managment/shared/toggle_widget.dart'; import 'package:syncrow_web/pages/device_managment/three_g_glass_switch/bloc/three_gang_glass_switch_bloc.dart'; import 'package:syncrow_web/pages/device_managment/three_g_glass_switch/factories/three_gang_glass_switch_bloc_factory.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'; import '../models/three_gang_glass_switch.dart'; -class ThreeGangGlassSwitchControlView extends StatelessWidget with HelperResponsiveLayout { +class ThreeGangGlassSwitchControlView extends StatelessWidget + with HelperResponsiveLayout { final String deviceId; const ThreeGangGlassSwitchControlView({required this.deviceId, super.key}); @@ -17,7 +23,8 @@ class ThreeGangGlassSwitchControlView extends StatelessWidget with HelperRespons Widget build(BuildContext context) { return BlocProvider( create: (context) => - ThreeGangGlassSwitchBlocFactory.create(deviceId: deviceId)..add(ThreeGangGlassSwitchFetchDeviceEvent(deviceId)), + ThreeGangGlassSwitchBlocFactory.create(deviceId: deviceId) + ..add(ThreeGangGlassSwitchFetchDeviceEvent(deviceId)), child: BlocBuilder( builder: (context, state) { if (state is ThreeGangGlassSwitchLoading) { @@ -34,7 +41,8 @@ class ThreeGangGlassSwitchControlView extends StatelessWidget with HelperRespons ); } - Widget _buildStatusControls(BuildContext context, ThreeGangGlassStatusModel status) { + Widget _buildStatusControls( + BuildContext context, ThreeGangGlassStatusModel status) { final isExtraLarge = isExtraLargeScreenSize(context); final isLarge = isLargeScreenSize(context); final isMedium = isMediumScreenSize(context); @@ -107,15 +115,71 @@ class ThreeGangGlassSwitchControlView extends StatelessWidget with HelperRespons onChange: (value) {}, showToggle: false, ), - ToggleWidget( - value: false, - code: '', - deviceId: deviceId, - label: 'Scheduling', - icon: Assets.scheduling, - onChange: (value) {}, - showToggle: false, - ), + + GestureDetector( + onTap: () { + showDialog( + context: context, + builder: (ctx) => BlocProvider.value( + value: BlocProvider.of(context), + child: BuildScheduleView( + deviceUuid: deviceId, + ), + )); + }, + child: DeviceControlsContainer( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Container( + width: 60, + height: 60, + decoration: const BoxDecoration( + shape: BoxShape.circle, + color: ColorsManager.whiteColors, + ), + margin: const EdgeInsets.symmetric(horizontal: 4), + padding: const EdgeInsets.all(12), + child: ClipOval( + child: SvgPicture.asset( + Assets.scheduling, + fit: BoxFit.fill, + ), + ), + ), + const Spacer(), + Text( + 'Scheduling', + textAlign: TextAlign.center, + style: context.textTheme.titleMedium!.copyWith( + fontWeight: FontWeight.w400, + color: ColorsManager.blackColor, + ), + ), + ], + ), + ), + ) + // ToggleWidget( + // value: true, + // code: '', + // deviceId: deviceId, + // label: 'Scheduling', + // icon: Assets.scheduling, + // onChange: (value) { + // print('Scheduling clicked'); + // showDialog( + // context: context, + // builder: (ctx) => BlocProvider.value( + // value: BlocProvider.of(context), + // child: BuildScheduleView( + // deviceUuid: deviceId, + // ), + // ), + // ); + // }, + // showToggle: false, + // ), ], ); } diff --git a/lib/pages/device_managment/three_gang_switch/view/living_room_device_control.dart b/lib/pages/device_managment/three_gang_switch/view/living_room_device_control.dart index 731b354c..f4739b88 100644 --- a/lib/pages/device_managment/three_gang_switch/view/living_room_device_control.dart +++ b/lib/pages/device_managment/three_gang_switch/view/living_room_device_control.dart @@ -1,9 +1,15 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:flutter_svg/svg.dart'; +import 'package:syncrow_web/pages/device_managment/schedule_device/schedule_widgets/schedual_view.dart'; +import 'package:syncrow_web/pages/device_managment/shared/device_controls_container.dart'; import 'package:syncrow_web/pages/device_managment/three_gang_switch/bloc/living_room_bloc.dart'; import 'package:syncrow_web/pages/device_managment/three_gang_switch/factories/living_room_bloc_factory.dart'; import 'package:syncrow_web/pages/device_managment/three_gang_switch/models/living_room_model.dart'; import 'package:syncrow_web/pages/device_managment/shared/toggle_widget.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 LivingRoomDeviceControlsView extends StatelessWidget @@ -90,6 +96,50 @@ class LivingRoomDeviceControlsView extends StatelessWidget ); }, ), + GestureDetector( + onTap: () { + showDialog( + context: context, + builder: (ctx) => BlocProvider.value( + value: BlocProvider.of(context), + child: BuildScheduleView( + deviceUuid: deviceId, + ), + )); + }, + child: DeviceControlsContainer( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Container( + width: 60, + height: 60, + decoration: const BoxDecoration( + shape: BoxShape.circle, + color: ColorsManager.whiteColors, + ), + margin: const EdgeInsets.symmetric(horizontal: 4), + padding: const EdgeInsets.all(12), + child: ClipOval( + child: SvgPicture.asset( + Assets.scheduling, + fit: BoxFit.fill, + ), + ), + ), + const Spacer(), + Text( + 'Scheduling', + textAlign: TextAlign.center, + style: context.textTheme.titleMedium!.copyWith( + fontWeight: FontWeight.w400, + color: ColorsManager.blackColor, + ), + ), + ], + ), + ), + ), ], ); } diff --git a/lib/pages/device_managment/two_g_glass_switch/view/two_gang_glass_switch_control_view.dart b/lib/pages/device_managment/two_g_glass_switch/view/two_gang_glass_switch_control_view.dart index 575deeac..cd80f528 100644 --- a/lib/pages/device_managment/two_g_glass_switch/view/two_gang_glass_switch_control_view.dart +++ b/lib/pages/device_managment/two_g_glass_switch/view/two_gang_glass_switch_control_view.dart @@ -1,10 +1,15 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:flutter_svg/svg.dart'; +import 'package:syncrow_web/pages/device_managment/schedule_device/schedule_widgets/schedual_view.dart'; +import 'package:syncrow_web/pages/device_managment/shared/device_controls_container.dart'; import 'package:syncrow_web/pages/device_managment/shared/toggle_widget.dart'; import 'package:syncrow_web/pages/device_managment/two_g_glass_switch/bloc/two_gang_glass_switch_bloc.dart'; import 'package:syncrow_web/pages/device_managment/two_g_glass_switch/factories/two_gang_glass_switch_bloc_factory.dart'; import 'package:syncrow_web/pages/device_managment/two_g_glass_switch/models/two_gang_glass_status_model.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 TwoGangGlassSwitchControlView extends StatelessWidget @@ -16,8 +21,9 @@ class TwoGangGlassSwitchControlView extends StatelessWidget @override Widget build(BuildContext context) { return BlocProvider( - create: (context) => TwoGangGlassSwitchBlocFactory.create(deviceId: deviceId) - ..add(TwoGangGlassSwitchFetchDeviceEvent(deviceId)), + create: (context) => + TwoGangGlassSwitchBlocFactory.create(deviceId: deviceId) + ..add(TwoGangGlassSwitchFetchDeviceEvent(deviceId)), child: BlocBuilder( builder: (context, state) { if (state is TwoGangGlassSwitchLoading) { @@ -92,15 +98,59 @@ class TwoGangGlassSwitchControlView extends StatelessWidget onChange: (value) {}, showToggle: false, ), - ToggleWidget( - value: false, - code: '', - deviceId: deviceId, - label: 'Scheduling', - icon: Assets.scheduling, - onChange: (value) {}, - showToggle: false, - ), + // ToggleWidget( + // value: false, + // code: '', + // deviceId: deviceId, + // label: 'Scheduling', + // icon: Assets.scheduling, + // onChange: (value) {}, + // showToggle: false, + // ), + GestureDetector( + onTap: () { + showDialog( + context: context, + builder: (ctx) => BlocProvider.value( + value: BlocProvider.of(context), + child: BuildScheduleView( + deviceUuid: deviceId, + ), + )); + }, + child: DeviceControlsContainer( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Container( + width: 60, + height: 60, + decoration: const BoxDecoration( + shape: BoxShape.circle, + color: ColorsManager.whiteColors, + ), + margin: const EdgeInsets.symmetric(horizontal: 4), + padding: const EdgeInsets.all(12), + child: ClipOval( + child: SvgPicture.asset( + Assets.scheduling, + fit: BoxFit.fill, + ), + ), + ), + const Spacer(), + Text( + 'Scheduling', + textAlign: TextAlign.center, + style: context.textTheme.titleMedium!.copyWith( + fontWeight: FontWeight.w400, + color: ColorsManager.blackColor, + ), + ), + ], + ), + ), + ) ], ); } diff --git a/lib/pages/device_managment/two_gang_switch/view/wall_light_device_control.dart b/lib/pages/device_managment/two_gang_switch/view/wall_light_device_control.dart index 882aac3e..05a02a69 100644 --- a/lib/pages/device_managment/two_gang_switch/view/wall_light_device_control.dart +++ b/lib/pages/device_managment/two_gang_switch/view/wall_light_device_control.dart @@ -1,11 +1,17 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:flutter_svg/svg.dart'; +import 'package:syncrow_web/pages/device_managment/schedule_device/schedule_widgets/schedual_view.dart'; +import 'package:syncrow_web/pages/device_managment/shared/device_controls_container.dart'; import 'package:syncrow_web/pages/device_managment/shared/toggle_widget.dart'; import 'package:syncrow_web/pages/device_managment/two_gang_switch/bloc/two_gang_switch_bloc.dart'; import 'package:syncrow_web/pages/device_managment/two_gang_switch/bloc/two_gang_switch_event.dart'; import 'package:syncrow_web/pages/device_managment/two_gang_switch/bloc/two_gang_switch_state.dart'; import 'package:syncrow_web/pages/device_managment/two_gang_switch/factories/two_gang_switch_bloc_factory.dart'; import 'package:syncrow_web/pages/device_managment/two_gang_switch/models/two_gang_status_model.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 TwoGangDeviceControlView extends StatelessWidget @@ -44,6 +50,7 @@ class TwoGangDeviceControlView extends StatelessWidget children: [ SizedBox( width: 200, + height: 120, child: ToggleWidget( value: status.switch1, code: 'switch_1', @@ -60,6 +67,7 @@ class TwoGangDeviceControlView extends StatelessWidget ), SizedBox( width: 200, + height: 120, child: ToggleWidget( value: status.switch2, code: 'switch_2', @@ -74,6 +82,54 @@ class TwoGangDeviceControlView extends StatelessWidget }, ), ), + SizedBox( + width: 200, + height: 120, + child: GestureDetector( + onTap: () { + showDialog( + context: context, + builder: (ctx) => BlocProvider.value( + value: BlocProvider.of(context), + child: BuildScheduleView( + deviceUuid: deviceId, + ), + )); + }, + child: DeviceControlsContainer( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Container( + width: 60, + height: 60, + decoration: const BoxDecoration( + shape: BoxShape.circle, + color: ColorsManager.whiteColors, + ), + margin: const EdgeInsets.symmetric(horizontal: 4), + padding: const EdgeInsets.all(12), + child: ClipOval( + child: SvgPicture.asset( + Assets.scheduling, + fit: BoxFit.fill, + ), + ), + ), + const Spacer(), + Text( + 'Scheduling', + textAlign: TextAlign.center, + style: context.textTheme.titleMedium!.copyWith( + fontWeight: FontWeight.w400, + color: ColorsManager.blackColor, + ), + ), + ], + ), + ), + ), + ) ], ), ); From ed2a8f6ba28e63951ef5fa469d69614292aac3d0 Mon Sep 17 00:00:00 2001 From: mohammad Date: Thu, 19 Jun 2025 11:02:23 +0300 Subject: [PATCH 4/8] Refactor border radius implementation in ScheduleGarageTableWidget for consistency --- .../schedule_view/schedule__garage_table.dart | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/lib/pages/device_managment/garage_door/schedule_view/schedule__garage_table.dart b/lib/pages/device_managment/garage_door/schedule_view/schedule__garage_table.dart index 0bd347ab..525e79c9 100644 --- a/lib/pages/device_managment/garage_door/schedule_view/schedule__garage_table.dart +++ b/lib/pages/device_managment/garage_door/schedule_view/schedule__garage_table.dart @@ -26,8 +26,7 @@ class ScheduleGarageTableWidget extends StatelessWidget { Table( border: TableBorder.all( color: ColorsManager.graysColor, - borderRadius: const BorderRadius.only( - topLeft: Radius.circular(20), topRight: Radius.circular(20)), + borderRadius: const BorderRadius.vertical(top: Radius.circular(20)), ), children: [ TableRow( @@ -56,16 +55,15 @@ class ScheduleGarageTableWidget extends StatelessWidget { child: Center(child: CircularProgressIndicator())); } if (state is GarageDoorLoadedState && - state.status.schedules?.isEmpty == true) { + state.status.schedules!.isEmpty) { return _buildEmptyState(context); } else if (state is GarageDoorLoadedState) { return Container( height: 200, decoration: BoxDecoration( border: Border.all(color: ColorsManager.graysColor), - borderRadius: const BorderRadius.only( - bottomLeft: Radius.circular(20), - bottomRight: Radius.circular(20)), + borderRadius: const BorderRadius.vertical( + bottom: Radius.circular(20)), ), child: _buildTableBody(state, context)); } @@ -83,8 +81,7 @@ class ScheduleGarageTableWidget extends StatelessWidget { height: 200, decoration: BoxDecoration( border: Border.all(color: ColorsManager.graysColor), - borderRadius: const BorderRadius.only( - bottomLeft: Radius.circular(20), bottomRight: Radius.circular(20)), + borderRadius: const BorderRadius.vertical(top: Radius.circular(20)), ), child: Center( child: Column( From 2267d95795413182bdb882b878d5c1575aa1dfb6 Mon Sep 17 00:00:00 2001 From: mohammad Date: Thu, 19 Jun 2025 15:46:40 +0300 Subject: [PATCH 5/8] Add schedule saving functionality and update schedule events --- .../schedule_device/bloc/schedule_bloc.dart | 125 +++++++++++++----- .../schedule_device/bloc/schedule_event.dart | 11 +- .../schedule_widgets/schedule_table.dart | 80 ++++++----- lib/services/devices_mang_api.dart | 31 +++++ lib/utils/constants/api_const.dart | 1 + 5 files changed, 186 insertions(+), 62 deletions(-) diff --git a/lib/pages/device_managment/schedule_device/bloc/schedule_bloc.dart b/lib/pages/device_managment/schedule_device/bloc/schedule_bloc.dart index e6a2645d..c4e731db 100644 --- a/lib/pages/device_managment/schedule_device/bloc/schedule_bloc.dart +++ b/lib/pages/device_managment/schedule_device/bloc/schedule_bloc.dart @@ -37,28 +37,39 @@ class ScheduleBloc extends Bloc { Timer? _countdownTimer; Duration countdownRemaining = Duration.zero; - void _onStopScheduleEvent( + Future _onStopScheduleEvent( StopScheduleEvent event, Emitter emit, - ) { + ) async { if (state is ScheduleLoaded) { final currentState = state as ScheduleLoaded; - _countdownTimer?.cancel(); - if (event.mode == ScheduleModes.countdown) { - emit(currentState.copyWith( - countdownHours: 0, - countdownMinutes: 0, - isCountdownActive: false, - countdownRemaining: Duration.zero, - )); - } else if (event.mode == ScheduleModes.inching) { - emit(currentState.copyWith( - inchingHours: 0, - inchingMinutes: 0, - isInchingActive: false, - countdownRemaining: Duration.zero, - )); + final success = await RemoteControlDeviceService().controlDevice( + deviceUuid: deviceId, + status: Status( + code: 'countdown_1', + value: 0, + ), + ); + if (success) { + _countdownTimer?.cancel(); + if (event.mode == ScheduleModes.countdown) { + emit(currentState.copyWith( + countdownHours: 0, + countdownMinutes: 0, + isCountdownActive: false, + countdownRemaining: Duration.zero, + )); + } else if (event.mode == ScheduleModes.inching) { + emit(currentState.copyWith( + inchingHours: 0, + inchingMinutes: 0, + isInchingActive: false, + countdownRemaining: Duration.zero, + )); + } + } else { + emit(const ScheduleError('Failed to stop schedule')); } } } @@ -241,16 +252,14 @@ class ScheduleBloc extends Bloc { ) async { try { if (state is ScheduleLoaded) { - final newSchedule = ScheduleEntry( + final dateTime = DateTime.parse(event.time); + final success = await DevicesManagementApi().postSchedule( category: event.category, - time: event.time, - function: Status(code: 'switch_1', value: event.functionOn), + deviceId: deviceId, + time: getTimeStampWithoutSeconds(dateTime).toString(), + code: 'switch_1', + value: event.functionOn, days: event.selectedDays); - final success = await DevicesManagementApi().addScheduleRecord( - newSchedule, - deviceId, - ); - if (success) { add(const ScheduleGetEvent(category: 'switch_1')); } else { @@ -268,14 +277,14 @@ class ScheduleBloc extends Bloc { ) async { try { if (state is ScheduleLoaded) { + final dateTime = DateTime.parse(event.time); final updatedSchedule = ScheduleEntry( scheduleId: event.scheduleId, category: event.category, - time: event.time, + time: getTimeStampWithoutSeconds(dateTime).toString(), function: Status(code: 'switch_1', value: event.functionOn), days: event.selectedDays, ); - final success = await DevicesManagementApi().editScheduleRecord( deviceId, updatedSchedule, @@ -299,10 +308,12 @@ class ScheduleBloc extends Bloc { try { if (state is ScheduleLoaded) { final currentState = state as ScheduleLoaded; + final updatedSchedules = currentState.schedules.map((schedule) { if (schedule.scheduleId == event.scheduleId) { return schedule.copyWith( function: Status(code: 'switch_1', value: event.functionOn), + enable: event.enable, ); } return schedule; @@ -491,10 +502,16 @@ class ScheduleBloc extends Bloc { try { final status = await DevicesManagementApi().getDeviceStatus(event.deviceId); + print(status.status); final deviceStatus = WaterHeaterStatusModel.fromJson(event.deviceId, status.status); - final scheduleMode = deviceStatus.scheduleMode; + final scheduleMode = + deviceStatus.countdownHours > 0 || deviceStatus.countdownMinutes > 0 + ? ScheduleModes.countdown + : deviceStatus.inchingHours > 0 || deviceStatus.inchingMinutes > 0 + ? ScheduleModes.inching + : ScheduleModes.schedule; final isCountdown = scheduleMode == ScheduleModes.countdown; final isInching = scheduleMode == ScheduleModes.inching; @@ -516,6 +533,10 @@ class ScheduleBloc extends Bloc { Duration.zero; } if (state is ScheduleLoaded) { + print('Updating existing state with fetched status'); + print('scheduleMode: $scheduleMode'); + print('countdownRemaining: $countdownRemaining'); + print('isCountdownActive: $isCountdownActive'); final currentState = state as ScheduleLoaded; emit(currentState.copyWith( scheduleMode: scheduleMode, @@ -546,12 +567,54 @@ class ScheduleBloc extends Bloc { )); } - if (isCountdownActive && countdownRemaining != null) { - _startCountdownTimer(emit, countdownRemaining); - } + // if (isCountdownActive && countdownRemaining != null) { + // _startCountdownTimer(emit, countdownRemaining); + // } } catch (e) { emit(ScheduleError('Failed to fetch device status: $e')); } } + String extractTime(String isoDateTime) { + // Example input: "2025-06-19T15:45:00.000" + return isoDateTime.split('T')[1].split('.')[0]; // gives "15:45:00" + } + + int? getTimeStampWithoutSeconds(DateTime? dateTime) { + if (dateTime == null) return null; + DateTime dateTimeWithoutSeconds = DateTime(dateTime.year, dateTime.month, + dateTime.day, dateTime.hour, dateTime.minute); + return dateTimeWithoutSeconds.millisecondsSinceEpoch ~/ 1000; + } + + // Future _updateScheduleEvent( + // StatusUpdatedScheduleEvent event, + // Emitter emit, + // ) async { + // if (state is ScheduleLoaded) { + // final currentState = state as ScheduleLoaded; + + // final updatedSchedules = currentState.schedules.map((schedule) { + // if (schedule.scheduleId == event.scheduleId) { + // return schedule.copyWith( + // function: Status(code: 'switch_1', value: event.functionOn), + // enable: event.enable, + // ); + // } + // return schedule; + // }).toList(); + + // bool success = await DevicesManagementApi().updateScheduleRecord( + // enable: event.enable, + // uuid: currentState.status.uuid, + // scheduleId: event.scheduleId, + // ); + + // if (success) { + // emit(currentState.copyWith(schedules: updatedSchedules)); + // } else { + // emit(currentState); + // } + // } + // } } diff --git a/lib/pages/device_managment/schedule_device/bloc/schedule_event.dart b/lib/pages/device_managment/schedule_device/bloc/schedule_event.dart index 369ca795..5099679c 100644 --- a/lib/pages/device_managment/schedule_device/bloc/schedule_event.dart +++ b/lib/pages/device_managment/schedule_device/bloc/schedule_event.dart @@ -68,7 +68,7 @@ class ScheduleGetEvent extends ScheduleEvent { class ScheduleAddEvent extends ScheduleEvent { final String category; - final String time; + final String time; final List selectedDays; final bool functionOn; @@ -219,3 +219,12 @@ class DeleteScheduleEvent extends ScheduleEvent { @override List get props => [scheduleId]; } + +class StatusUpdatedScheduleEvent extends ScheduleEvent { + final String id; + + const StatusUpdatedScheduleEvent(this.id); + + @override + List get props => [id]; +} diff --git a/lib/pages/device_managment/schedule_device/schedule_widgets/schedule_table.dart b/lib/pages/device_managment/schedule_device/schedule_widgets/schedule_table.dart index 4d36e0e2..97ca03e1 100644 --- a/lib/pages/device_managment/schedule_device/schedule_widgets/schedule_table.dart +++ b/lib/pages/device_managment/schedule_device/schedule_widgets/schedule_table.dart @@ -160,28 +160,26 @@ class _ScheduleTableView extends StatelessWidget { children: [ Center( child: GestureDetector( + behavior: HitTestBehavior.opaque, onTap: () { - ///TODO: Implement toggle functionality - - // Toggle enabled state using ScheduleBloc - // context.read().add( - // UpdateScheduleEvent( - // scheduleId: schedule.scheduleId, - // functionOn: schedule.function.value, - // enable: !schedule.enable, - // ), - // ); - }, - child: SizedBox( - width: 24, - height: 24, - child: schedule.enable - ? const Icon(Icons.radio_button_checked, - color: ColorsManager.blueColor) - : const Icon( - Icons.radio_button_unchecked, - color: ColorsManager.grayColor, + context.read().add( + ScheduleUpdateEntryEvent( + scheduleId: schedule.scheduleId, + functionOn: schedule.function.value, + enable: !schedule.enable, ), + ); + }, + child: Center( + child: SizedBox( + width: 24, + height: 24, + child: schedule.enable + ? const Icon(Icons.radio_button_checked, + color: ColorsManager.blueColor) + : const Icon(Icons.radio_button_unchecked, + color: ColorsManager.grayColor), + ), ), ), ), @@ -202,7 +200,6 @@ class _ScheduleTableView extends StatelessWidget { schedule: ScheduleEntry.fromScheduleModel(schedule), isEdit: true, ).then((updatedSchedule) { - print('updatedSchedule : $updatedSchedule'); if (updatedSchedule != null) { context.read().add( ScheduleEditEvent( @@ -225,12 +222,38 @@ class _ScheduleTableView extends StatelessWidget { ), TextButton( style: TextButton.styleFrom(padding: EdgeInsets.zero), - onPressed: () { - context.read().add( - DeleteScheduleEvent( - schedule.scheduleId, - ), + onPressed: () async { + final confirmed = await showDialog( + context: context, + builder: (BuildContext dialogContext) { + return AlertDialog( + title: const Text('Confirm Delete'), + content: const Text( + 'Are you sure you want to delete this schedule?'), + actions: [ + TextButton( + onPressed: () => + Navigator.of(dialogContext).pop(false), + child: Text('Cancel'), + ), + TextButton( + onPressed: () => + Navigator.of(dialogContext).pop(true), + child: const Text( + 'Delete', + style: TextStyle(color: Colors.red), + ), + ), + ], ); + }, + ); + + if (confirmed == true) { + context.read().add( + ScheduleDeleteEvent(schedule.scheduleId), + ); + } }, child: Text( 'Delete', @@ -239,7 +262,7 @@ class _ScheduleTableView extends StatelessWidget { .bodySmall! .copyWith(color: ColorsManager.blueColor), ), - ), + ) ], ), ), @@ -248,7 +271,6 @@ class _ScheduleTableView extends StatelessWidget { } String _getSelectedDays(List selectedDays) { - // Use the same order as in ScheduleDialogHelper const days = ScheduleDialogHelper.allDays; return selectedDays .asMap() @@ -257,6 +279,4 @@ class _ScheduleTableView extends StatelessWidget { .map((entry) => days[entry.key]) .join(', '); } - - // Removed allDays from here as it is now in ScheduleDialogHelper } diff --git a/lib/services/devices_mang_api.dart b/lib/services/devices_mang_api.dart index 6f60e34f..6fb27daf 100644 --- a/lib/services/devices_mang_api.dart +++ b/lib/services/devices_mang_api.dart @@ -1,3 +1,4 @@ +import 'dart:convert'; import 'dart:core'; import 'package:flutter/material.dart'; import 'package:syncrow_web/pages/device_managment/all_devices/models/device_reports.dart'; @@ -386,4 +387,34 @@ class DevicesManagementApi { ); return response; } + + Future postSchedule({ + required String category, + required String deviceId, + required String time, + required String code, + required bool value, + required List days, + }) async { + final response = await HTTPService().post( + path: ApiEndpoints.saveSchedule.replaceAll('{deviceUuid}', deviceId), + showServerMessage: false, + body: jsonEncode( + { + 'category': category, + 'time': time, + 'function': { + 'code': code, + 'value': value, + }, + 'days': days + }, + ), + expectedResponseModel: (json) { + return json['success'] ?? false; + }, + ); + return response; + } + } diff --git a/lib/utils/constants/api_const.dart b/lib/utils/constants/api_const.dart index 411e72a5..79fd013e 100644 --- a/lib/utils/constants/api_const.dart +++ b/lib/utils/constants/api_const.dart @@ -136,4 +136,5 @@ abstract class ApiEndpoints { static const String assignDeviceToRoom = '/projects/{projectUuid}/communities/{communityUuid}/spaces/{spaceUuid}/subspaces/{subSpaceUuid}/devices/{deviceUuid}'; + static const String saveSchedule = '/schedule/{deviceUuid}'; } From 0b774a6dfca22f356801d83b8630b236661b6076 Mon Sep 17 00:00:00 2001 From: mohammad Date: Thu, 19 Jun 2025 16:20:46 +0300 Subject: [PATCH 6/8] Add scheduling category parameter to BuildScheduleView and update device control dialogs --- .../one_gang_glass_switch_control_view.dart | 3 +- .../view/wall_light_device_control.dart | 3 +- .../schedule_widgets/schedual_view.dart | 6 +- .../three_gang_glass_switch_control_view.dart | 24 +- .../view/living_room_device_control.dart | 143 +++++++++- .../two_gang_glass_switch_control_view.dart | 46 ++++ .../view/wall_light_device_control.dart | 256 ++++++++++++------ .../view/water_heater_device_control.dart | 3 +- 8 files changed, 368 insertions(+), 116 deletions(-) diff --git a/lib/pages/device_managment/one_g_glass_switch/view/one_gang_glass_switch_control_view.dart b/lib/pages/device_managment/one_g_glass_switch/view/one_gang_glass_switch_control_view.dart index 694c2f6e..3f4e6024 100644 --- a/lib/pages/device_managment/one_g_glass_switch/view/one_gang_glass_switch_control_view.dart +++ b/lib/pages/device_managment/one_g_glass_switch/view/one_gang_glass_switch_control_view.dart @@ -83,12 +83,13 @@ class OneGangGlassSwitchControlView extends StatelessWidget with HelperResponsiv ), GestureDetector( onTap: () { - showDialog( + showDialog( context: context, builder: (ctx) => BlocProvider.value( value: BlocProvider.of(context), child: BuildScheduleView( deviceUuid: deviceId, + category: 'switch_1', ), )); }, diff --git a/lib/pages/device_managment/one_gang_switch/view/wall_light_device_control.dart b/lib/pages/device_managment/one_gang_switch/view/wall_light_device_control.dart index e590adea..e5f2358e 100644 --- a/lib/pages/device_managment/one_gang_switch/view/wall_light_device_control.dart +++ b/lib/pages/device_managment/one_gang_switch/view/wall_light_device_control.dart @@ -76,12 +76,13 @@ class WallLightDeviceControl extends StatelessWidget ), GestureDetector( onTap: () { - showDialog( + showDialog( context: context, builder: (ctx) => BlocProvider.value( value: BlocProvider.of(context), child: BuildScheduleView( deviceUuid: deviceId, + category: 'switch_1', ), )); }, diff --git a/lib/pages/device_managment/schedule_device/schedule_widgets/schedual_view.dart b/lib/pages/device_managment/schedule_device/schedule_widgets/schedual_view.dart index 591b114f..2ae5b869 100644 --- a/lib/pages/device_managment/schedule_device/schedule_widgets/schedual_view.dart +++ b/lib/pages/device_managment/schedule_device/schedule_widgets/schedual_view.dart @@ -12,8 +12,10 @@ import 'package:syncrow_web/pages/device_managment/water_heater/helper/add_sched import 'package:syncrow_web/pages/device_managment/water_heater/models/water_heater_status_model.dart'; class BuildScheduleView extends StatelessWidget { - const BuildScheduleView({super.key, required this.deviceUuid}); + const BuildScheduleView( + {super.key, required this.deviceUuid, required this.category}); final String deviceUuid; + final String category; @override Widget build(BuildContext context) { @@ -21,7 +23,7 @@ class BuildScheduleView extends StatelessWidget { create: (_) => ScheduleBloc( deviceId: deviceUuid, ) - ..add(const ScheduleGetEvent(category: "switch_1")) + ..add(ScheduleGetEvent(category: category)) ..add(ScheduleFetchStatusEvent(deviceUuid)), child: Dialog( backgroundColor: Colors.white, diff --git a/lib/pages/device_managment/three_g_glass_switch/view/three_gang_glass_switch_control_view.dart b/lib/pages/device_managment/three_g_glass_switch/view/three_gang_glass_switch_control_view.dart index 7d80f289..79c2138b 100644 --- a/lib/pages/device_managment/three_g_glass_switch/view/three_gang_glass_switch_control_view.dart +++ b/lib/pages/device_managment/three_g_glass_switch/view/three_gang_glass_switch_control_view.dart @@ -115,14 +115,14 @@ class ThreeGangGlassSwitchControlView extends StatelessWidget onChange: (value) {}, showToggle: false, ), - GestureDetector( onTap: () { - showDialog( + showDialog( context: context, builder: (ctx) => BlocProvider.value( value: BlocProvider.of(context), child: BuildScheduleView( + category: 'switch_1', deviceUuid: deviceId, ), )); @@ -160,26 +160,6 @@ class ThreeGangGlassSwitchControlView extends StatelessWidget ), ), ) - // ToggleWidget( - // value: true, - // code: '', - // deviceId: deviceId, - // label: 'Scheduling', - // icon: Assets.scheduling, - // onChange: (value) { - // print('Scheduling clicked'); - // showDialog( - // context: context, - // builder: (ctx) => BlocProvider.value( - // value: BlocProvider.of(context), - // child: BuildScheduleView( - // deviceUuid: deviceId, - // ), - // ), - // ); - // }, - // showToggle: false, - // ), ], ); } diff --git a/lib/pages/device_managment/three_gang_switch/view/living_room_device_control.dart b/lib/pages/device_managment/three_gang_switch/view/living_room_device_control.dart index f4739b88..57c4d397 100644 --- a/lib/pages/device_managment/three_gang_switch/view/living_room_device_control.dart +++ b/lib/pages/device_managment/three_gang_switch/view/living_room_device_control.dart @@ -98,12 +98,13 @@ class LivingRoomDeviceControlsView extends StatelessWidget ), GestureDetector( onTap: () { - showDialog( + showDialog( context: context, builder: (ctx) => BlocProvider.value( value: BlocProvider.of(context), child: BuildScheduleView( deviceUuid: deviceId, + category: 'switch_1', ), )); }, @@ -128,13 +129,141 @@ class LivingRoomDeviceControlsView extends StatelessWidget ), ), const Spacer(), - Text( - 'Scheduling', - textAlign: TextAlign.center, - style: context.textTheme.titleMedium!.copyWith( - fontWeight: FontWeight.w400, - color: ColorsManager.blackColor, + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + 'Wall Light', + style: context.textTheme.titleMedium!.copyWith( + fontWeight: FontWeight.w200, + fontSize: 12, + color: ColorsManager.blackColor, + ), + ), + Text( + 'Scheduling', + style: context.textTheme.titleMedium!.copyWith( + fontWeight: FontWeight.w400, + color: ColorsManager.blackColor, + ), + ), + ], + ), + ], + ), + ), + ), + GestureDetector( + onTap: () { + showDialog( + context: context, + builder: (ctx) => BlocProvider.value( + value: BlocProvider.of(context), + child: BuildScheduleView( + deviceUuid: deviceId, + category: 'switch_2', + ), + )); + }, + child: DeviceControlsContainer( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Container( + width: 60, + height: 60, + decoration: const BoxDecoration( + shape: BoxShape.circle, + color: ColorsManager.whiteColors, ), + margin: const EdgeInsets.symmetric(horizontal: 4), + padding: const EdgeInsets.all(12), + child: ClipOval( + child: SvgPicture.asset( + Assets.scheduling, + fit: BoxFit.fill, + ), + ), + ), + const Spacer(), + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + 'Ceiling Light', + style: context.textTheme.titleMedium!.copyWith( + fontWeight: FontWeight.w200, + fontSize: 12, + color: ColorsManager.blackColor, + ), + ), + Text( + 'Scheduling', + textAlign: TextAlign.center, + style: context.textTheme.titleMedium!.copyWith( + fontWeight: FontWeight.w400, + color: ColorsManager.blackColor, + ), + ), + ], + ), + ], + ), + ), + ), + GestureDetector( + onTap: () { + showDialog( + context: context, + builder: (ctx) => BlocProvider.value( + value: BlocProvider.of(context), + child: BuildScheduleView( + deviceUuid: deviceId, + category: 'switch_3', + ), + )); + }, + child: DeviceControlsContainer( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Container( + width: 60, + height: 60, + decoration: const BoxDecoration( + shape: BoxShape.circle, + color: ColorsManager.whiteColors, + ), + margin: const EdgeInsets.symmetric(horizontal: 4), + padding: const EdgeInsets.all(12), + child: ClipOval( + child: SvgPicture.asset( + Assets.scheduling, + fit: BoxFit.fill, + ), + ), + ), + const Spacer(), + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + 'Spotlight', + style: context.textTheme.titleMedium!.copyWith( + fontWeight: FontWeight.w200, + fontSize: 12, + color: ColorsManager.blackColor, + ), + ), + Text( + 'Scheduling', + textAlign: TextAlign.center, + style: context.textTheme.titleMedium!.copyWith( + fontWeight: FontWeight.w400, + color: ColorsManager.blackColor, + ), + ), + ], ), ], ), diff --git a/lib/pages/device_managment/two_g_glass_switch/view/two_gang_glass_switch_control_view.dart b/lib/pages/device_managment/two_g_glass_switch/view/two_gang_glass_switch_control_view.dart index cd80f528..c4df483b 100644 --- a/lib/pages/device_managment/two_g_glass_switch/view/two_gang_glass_switch_control_view.dart +++ b/lib/pages/device_managment/two_g_glass_switch/view/two_gang_glass_switch_control_view.dart @@ -115,6 +115,52 @@ class TwoGangGlassSwitchControlView extends StatelessWidget value: BlocProvider.of(context), child: BuildScheduleView( deviceUuid: deviceId, + category: 'switch_1', + ), + )); + }, + child: DeviceControlsContainer( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Container( + width: 60, + height: 60, + decoration: const BoxDecoration( + shape: BoxShape.circle, + color: ColorsManager.whiteColors, + ), + margin: const EdgeInsets.symmetric(horizontal: 4), + padding: const EdgeInsets.all(12), + child: ClipOval( + child: SvgPicture.asset( + Assets.scheduling, + fit: BoxFit.fill, + ), + ), + ), + const Spacer(), + Text( + 'Scheduling', + textAlign: TextAlign.center, + style: context.textTheme.titleMedium!.copyWith( + fontWeight: FontWeight.w400, + color: ColorsManager.blackColor, + ), + ), + ], + ), + ), + ), + GestureDetector( + onTap: () { + showDialog( + context: context, + builder: (ctx) => BlocProvider.value( + value: BlocProvider.of(context), + child: BuildScheduleView( + deviceUuid: deviceId, + category: 'switch_2', ), )); }, diff --git a/lib/pages/device_managment/two_gang_switch/view/wall_light_device_control.dart b/lib/pages/device_managment/two_gang_switch/view/wall_light_device_control.dart index 05a02a69..d1f75564 100644 --- a/lib/pages/device_managment/two_gang_switch/view/wall_light_device_control.dart +++ b/lib/pages/device_managment/two_gang_switch/view/wall_light_device_control.dart @@ -43,92 +43,184 @@ class TwoGangDeviceControlView extends StatelessWidget Widget _buildStatusControls(BuildContext context, TwoGangStatusModel status) { return Center( - child: Wrap( - alignment: WrapAlignment.center, - spacing: 12, - runSpacing: 12, + child: Column( + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisAlignment: MainAxisAlignment.center, children: [ - SizedBox( - width: 200, - height: 120, - child: ToggleWidget( - value: status.switch1, - code: 'switch_1', - deviceId: deviceId, - label: 'Wall Light', - onChange: (value) { - context.read().add(TwoGangSwitchControl( - deviceId: deviceId, - code: 'switch_1', - value: value, - )); - }, - ), - ), - SizedBox( - width: 200, - height: 120, - child: ToggleWidget( - value: status.switch2, - code: 'switch_2', - deviceId: deviceId, - label: 'Ceiling Light', - onChange: (value) { - context.read().add(TwoGangSwitchControl( - deviceId: deviceId, - code: 'switch_2', - value: value, - )); - }, - ), - ), - SizedBox( - width: 200, - height: 120, - child: GestureDetector( - onTap: () { - showDialog( - context: context, - builder: (ctx) => BlocProvider.value( - value: BlocProvider.of(context), - child: BuildScheduleView( - deviceUuid: deviceId, - ), + Row( + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + SizedBox( + width: 200, + height: 150, + child: ToggleWidget( + value: status.switch1, + code: 'switch_1', + deviceId: deviceId, + label: 'Wall Light', + onChange: (value) { + context.read().add(TwoGangSwitchControl( + deviceId: deviceId, + code: 'switch_1', + value: value, )); - }, - child: DeviceControlsContainer( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Container( - width: 60, - height: 60, - decoration: const BoxDecoration( - shape: BoxShape.circle, - color: ColorsManager.whiteColors, - ), - margin: const EdgeInsets.symmetric(horizontal: 4), - padding: const EdgeInsets.all(12), - child: ClipOval( - child: SvgPicture.asset( - Assets.scheduling, - fit: BoxFit.fill, - ), - ), - ), - const Spacer(), - Text( - 'Scheduling', - textAlign: TextAlign.center, - style: context.textTheme.titleMedium!.copyWith( - fontWeight: FontWeight.w400, - color: ColorsManager.blackColor, - ), - ), - ], + }, ), ), - ), + const SizedBox(width: 10), + SizedBox( + width: 200, + height: 150, + child: ToggleWidget( + value: status.switch2, + code: 'switch_2', + deviceId: deviceId, + label: 'Ceiling Light', + onChange: (value) { + context.read().add(TwoGangSwitchControl( + deviceId: deviceId, + code: 'switch_2', + value: value, + )); + }, + ), + ), + ], + ), + const SizedBox(height: 20), + Row( + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + SizedBox( + width: 200, + height: 150, + child: GestureDetector( + onTap: () { + showDialog( + context: context, + builder: (ctx) => BlocProvider.value( + value: + BlocProvider.of(context), + child: BuildScheduleView( + deviceUuid: deviceId, + category: 'switch_1', + ), + )); + }, + child: DeviceControlsContainer( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Container( + width: 60, + height: 60, + decoration: const BoxDecoration( + shape: BoxShape.circle, + color: ColorsManager.whiteColors, + ), + margin: const EdgeInsets.symmetric(horizontal: 4), + padding: const EdgeInsets.all(12), + child: ClipOval( + child: SvgPicture.asset( + Assets.scheduling, + fit: BoxFit.fill, + ), + ), + ), + const Spacer(), + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + 'Wall Light', + textAlign: TextAlign.center, + style: context.textTheme.titleMedium!.copyWith( + fontWeight: FontWeight.w200, + color: ColorsManager.blackColor, + fontSize: 12), + ), + Text( + 'Scheduling', + textAlign: TextAlign.center, + style: context.textTheme.titleMedium!.copyWith( + fontWeight: FontWeight.w400, + color: ColorsManager.blackColor, + ), + ), + ], + ), + ], + ), + ), + ), + ), + const SizedBox(width: 10), + SizedBox( + width: 200, + height: 150, + child: GestureDetector( + onTap: () { + showDialog( + context: context, + builder: (ctx) => BlocProvider.value( + value: + BlocProvider.of(context), + child: BuildScheduleView( + deviceUuid: deviceId, + category: 'switch_2', + ), + )); + }, + child: DeviceControlsContainer( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Container( + width: 60, + height: 60, + decoration: const BoxDecoration( + shape: BoxShape.circle, + color: ColorsManager.whiteColors, + ), + margin: const EdgeInsets.symmetric(horizontal: 4), + padding: const EdgeInsets.all(12), + child: ClipOval( + child: SvgPicture.asset( + Assets.scheduling, + fit: BoxFit.fill, + ), + ), + ), + const Spacer(), + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + 'Ceiling Light', + textAlign: TextAlign.center, + style: context.textTheme.titleMedium!.copyWith( + fontWeight: FontWeight.w200, + color: ColorsManager.blackColor, + fontSize: 12), + ), + Text( + 'Scheduling', + textAlign: TextAlign.center, + style: context.textTheme.titleMedium!.copyWith( + fontWeight: FontWeight.w400, + color: ColorsManager.blackColor, + ), + ), + ], + ), + ], + ), + ), + ), + ), + ], ) ], ), diff --git a/lib/pages/device_managment/water_heater/view/water_heater_device_control.dart b/lib/pages/device_managment/water_heater/view/water_heater_device_control.dart index 1d80fd9f..a847e315 100644 --- a/lib/pages/device_managment/water_heater/view/water_heater_device_control.dart +++ b/lib/pages/device_managment/water_heater/view/water_heater_device_control.dart @@ -76,12 +76,13 @@ class WaterHeaterDeviceControlView extends StatelessWidget ), GestureDetector( onTap: () { - showDialog( + showDialog( context: context, builder: (ctx) => BlocProvider.value( value: BlocProvider.of(context), child: BuildScheduleView( deviceUuid: device.uuid ?? '', + category: 'switch_1', ), )); }, From 8cf73e3efc1aa6afd2ac0c86a5e5a79036240b19 Mon Sep 17 00:00:00 2001 From: mohammad Date: Thu, 19 Jun 2025 16:38:45 +0300 Subject: [PATCH 7/8] Enhance scheduling UI in glass switch control views with improved layout and dialog integration --- .../three_gang_glass_switch_control_view.dart | 164 ++++++++++++++++-- .../two_gang_glass_switch_control_view.dart | 71 +++++--- 2 files changed, 193 insertions(+), 42 deletions(-) diff --git a/lib/pages/device_managment/three_g_glass_switch/view/three_gang_glass_switch_control_view.dart b/lib/pages/device_managment/three_g_glass_switch/view/three_gang_glass_switch_control_view.dart index 79c2138b..bfc0a73e 100644 --- a/lib/pages/device_managment/three_g_glass_switch/view/three_gang_glass_switch_control_view.dart +++ b/lib/pages/device_managment/three_g_glass_switch/view/three_gang_glass_switch_control_view.dart @@ -106,14 +106,123 @@ class ThreeGangGlassSwitchControlView extends StatelessWidget ); }, ), - ToggleWidget( - value: false, - code: '', - deviceId: deviceId, - label: 'Preferences', - icon: Assets.preferences, - onChange: (value) {}, - showToggle: false, + GestureDetector( + onTap: () { + showDialog( + context: context, + builder: (ctx) => BlocProvider.value( + value: BlocProvider.of(context), + child: BuildScheduleView( + category: 'switch_1', + deviceUuid: deviceId, + ), + )); + }, + child: DeviceControlsContainer( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Container( + width: 60, + height: 60, + decoration: const BoxDecoration( + shape: BoxShape.circle, + color: ColorsManager.whiteColors, + ), + margin: const EdgeInsets.symmetric(horizontal: 4), + padding: const EdgeInsets.all(12), + child: ClipOval( + child: SvgPicture.asset( + Assets.scheduling, + fit: BoxFit.fill, + ), + ), + ), + const Spacer(), + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + 'Wall Light', + textAlign: TextAlign.center, + style: context.textTheme.titleMedium!.copyWith( + fontWeight: FontWeight.w200, + fontSize: 12, + color: ColorsManager.blackColor, + ), + ), + Text( + 'Scheduling', + textAlign: TextAlign.center, + style: context.textTheme.titleMedium!.copyWith( + fontWeight: FontWeight.w400, + color: ColorsManager.blackColor, + ), + ), + ], + ), + ], + ), + ), + ), + GestureDetector( + onTap: () { + showDialog( + context: context, + builder: (ctx) => BlocProvider.value( + value: BlocProvider.of(context), + child: BuildScheduleView( + category: 'switch_2', + deviceUuid: deviceId, + ), + )); + }, + child: DeviceControlsContainer( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Container( + width: 60, + height: 60, + decoration: const BoxDecoration( + shape: BoxShape.circle, + color: ColorsManager.whiteColors, + ), + margin: const EdgeInsets.symmetric(horizontal: 4), + padding: const EdgeInsets.all(12), + child: ClipOval( + child: SvgPicture.asset( + Assets.scheduling, + fit: BoxFit.fill, + ), + ), + ), + const Spacer(), + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + 'Ceiling Light', + textAlign: TextAlign.center, + style: context.textTheme.titleMedium!.copyWith( + fontWeight: FontWeight.w200, + fontSize: 12, + color: ColorsManager.blackColor, + ), + ), + Text( + 'Scheduling', + textAlign: TextAlign.center, + style: context.textTheme.titleMedium!.copyWith( + fontWeight: FontWeight.w400, + color: ColorsManager.blackColor, + ), + ), + ], + ), + ], + ), + ), ), GestureDetector( onTap: () { @@ -148,18 +257,41 @@ class ThreeGangGlassSwitchControlView extends StatelessWidget ), ), const Spacer(), - Text( - 'Scheduling', - textAlign: TextAlign.center, - style: context.textTheme.titleMedium!.copyWith( - fontWeight: FontWeight.w400, - color: ColorsManager.blackColor, - ), + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + 'SpotLight', + textAlign: TextAlign.center, + style: context.textTheme.titleMedium!.copyWith( + fontWeight: FontWeight.w200, + fontSize: 12, + color: ColorsManager.blackColor, + ), + ), + Text( + 'Scheduling', + textAlign: TextAlign.center, + style: context.textTheme.titleMedium!.copyWith( + fontWeight: FontWeight.w400, + color: ColorsManager.blackColor, + ), + ), + ], ), ], ), ), - ) + ), + ToggleWidget( + value: false, + code: '', + deviceId: deviceId, + label: 'Preferences', + icon: Assets.preferences, + onChange: (value) {}, + showToggle: false, + ), ], ); } diff --git a/lib/pages/device_managment/two_g_glass_switch/view/two_gang_glass_switch_control_view.dart b/lib/pages/device_managment/two_g_glass_switch/view/two_gang_glass_switch_control_view.dart index c4df483b..3c6d7551 100644 --- a/lib/pages/device_managment/two_g_glass_switch/view/two_gang_glass_switch_control_view.dart +++ b/lib/pages/device_managment/two_g_glass_switch/view/two_gang_glass_switch_control_view.dart @@ -98,18 +98,9 @@ class TwoGangGlassSwitchControlView extends StatelessWidget onChange: (value) {}, showToggle: false, ), - // ToggleWidget( - // value: false, - // code: '', - // deviceId: deviceId, - // label: 'Scheduling', - // icon: Assets.scheduling, - // onChange: (value) {}, - // showToggle: false, - // ), GestureDetector( onTap: () { - showDialog( + showDialog( context: context, builder: (ctx) => BlocProvider.value( value: BlocProvider.of(context), @@ -140,21 +131,35 @@ class TwoGangGlassSwitchControlView extends StatelessWidget ), ), const Spacer(), - Text( - 'Scheduling', - textAlign: TextAlign.center, - style: context.textTheme.titleMedium!.copyWith( - fontWeight: FontWeight.w400, - color: ColorsManager.blackColor, - ), + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + 'Wall Light', + textAlign: TextAlign.center, + style: context.textTheme.titleMedium!.copyWith( + fontWeight: FontWeight.w200, + fontSize: 12, + color: ColorsManager.blackColor, + ), + ), + Text( + 'Scheduling', + textAlign: TextAlign.center, + style: context.textTheme.titleMedium!.copyWith( + fontWeight: FontWeight.w400, + color: ColorsManager.blackColor, + ), + ), + ], ), ], ), ), ), - GestureDetector( + GestureDetector( onTap: () { - showDialog( + showDialog( context: context, builder: (ctx) => BlocProvider.value( value: BlocProvider.of(context), @@ -185,13 +190,27 @@ class TwoGangGlassSwitchControlView extends StatelessWidget ), ), const Spacer(), - Text( - 'Scheduling', - textAlign: TextAlign.center, - style: context.textTheme.titleMedium!.copyWith( - fontWeight: FontWeight.w400, - color: ColorsManager.blackColor, - ), + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + 'Ceiling Light', + textAlign: TextAlign.center, + style: context.textTheme.titleMedium!.copyWith( + fontWeight: FontWeight.w200, + fontSize: 12, + color: ColorsManager.blackColor, + ), + ), + Text( + 'Scheduling', + textAlign: TextAlign.center, + style: context.textTheme.titleMedium!.copyWith( + fontWeight: FontWeight.w400, + color: ColorsManager.blackColor, + ), + ), + ], ), ], ), From d2713c590285b0bc67b65175cb66720fe8bab9a9 Mon Sep 17 00:00:00 2001 From: mohammad Date: Sun, 22 Jun 2025 12:23:09 +0300 Subject: [PATCH 8/8] Add ScheduleControlButton widget and integrate it into water heater and wall light device controls --- .../one_gang_glass_switch_control_view.dart | 55 ++---- .../view/wall_light_device_control.dart | 46 +---- .../schedule_control_button.dart | 72 ++++++++ .../three_gang_glass_switch_control_view.dart | 161 ++---------------- .../view/living_room_device_control.dart | 154 ++--------------- .../two_gang_glass_switch_control_view.dart | 109 ++---------- .../view/wall_light_batch_control.dart | 47 ++++- .../view/wall_light_device_control.dart | 105 +----------- .../view/water_heater_device_control.dart | 40 +---- 9 files changed, 187 insertions(+), 602 deletions(-) create mode 100644 lib/pages/device_managment/schedule_device/schedule_widgets/schedule_control_button.dart diff --git a/lib/pages/device_managment/one_g_glass_switch/view/one_gang_glass_switch_control_view.dart b/lib/pages/device_managment/one_g_glass_switch/view/one_gang_glass_switch_control_view.dart index 3f4e6024..1ad5d43b 100644 --- a/lib/pages/device_managment/one_g_glass_switch/view/one_gang_glass_switch_control_view.dart +++ b/lib/pages/device_managment/one_g_glass_switch/view/one_gang_glass_switch_control_view.dart @@ -1,18 +1,16 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:flutter_svg/svg.dart'; import 'package:syncrow_web/pages/device_managment/one_g_glass_switch/bloc/one_gang_glass_switch_bloc.dart'; import 'package:syncrow_web/pages/device_managment/one_g_glass_switch/factories/one_gang_glass_switch_bloc_factory.dart'; import 'package:syncrow_web/pages/device_managment/one_g_glass_switch/models/once_gang_glass_status_model.dart'; import 'package:syncrow_web/pages/device_managment/schedule_device/schedule_widgets/schedual_view.dart'; -import 'package:syncrow_web/pages/device_managment/shared/device_controls_container.dart'; +import 'package:syncrow_web/pages/device_managment/schedule_device/schedule_widgets/schedule_control_button.dart'; import 'package:syncrow_web/pages/device_managment/shared/toggle_widget.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 OneGangGlassSwitchControlView extends StatelessWidget with HelperResponsiveLayout { +class OneGangGlassSwitchControlView extends StatelessWidget + with HelperResponsiveLayout { final String deviceId; const OneGangGlassSwitchControlView({required this.deviceId, super.key}); @@ -21,7 +19,8 @@ class OneGangGlassSwitchControlView extends StatelessWidget with HelperResponsiv Widget build(BuildContext context) { return BlocProvider( create: (context) => - OneGangGlassSwitchBlocFactory.create(deviceId: deviceId)..add(OneGangGlassSwitchFetchDeviceEvent(deviceId)), + OneGangGlassSwitchBlocFactory.create(deviceId: deviceId) + ..add(OneGangGlassSwitchFetchDeviceEvent(deviceId)), child: BlocBuilder( builder: (context, state) { if (state is OneGangGlassSwitchLoading) { @@ -38,7 +37,8 @@ class OneGangGlassSwitchControlView extends StatelessWidget with HelperResponsiv ); } - Widget _buildStatusControls(BuildContext context, OneGangGlassStatusModel status) { + Widget _buildStatusControls( + BuildContext context, OneGangGlassStatusModel status) { final isExtraLarge = isExtraLargeScreenSize(context); final isLarge = isLargeScreenSize(context); final isMedium = isMediumScreenSize(context); @@ -81,51 +81,22 @@ class OneGangGlassSwitchControlView extends StatelessWidget with HelperResponsiv onChange: (value) {}, showToggle: false, ), - GestureDetector( + ScheduleControlButton( onTap: () { showDialog( context: context, builder: (ctx) => BlocProvider.value( value: BlocProvider.of(context), child: BuildScheduleView( - deviceUuid: deviceId, category: 'switch_1', + deviceUuid: deviceId, ), )); }, - child: DeviceControlsContainer( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Container( - width: 60, - height: 60, - decoration: const BoxDecoration( - shape: BoxShape.circle, - color: ColorsManager.whiteColors, - ), - margin: const EdgeInsets.symmetric(horizontal: 4), - padding: const EdgeInsets.all(12), - child: ClipOval( - child: SvgPicture.asset( - Assets.scheduling, - fit: BoxFit.fill, - ), - ), - ), - const Spacer(), - Text( - 'Scheduling', - textAlign: TextAlign.center, - style: context.textTheme.titleMedium!.copyWith( - fontWeight: FontWeight.w400, - color: ColorsManager.blackColor, - ), - ), - ], - ), - ), - ) + mainText: '', + subtitle: 'Scheduling', + iconPath: Assets.scheduling, + ), ], ); } diff --git a/lib/pages/device_managment/one_gang_switch/view/wall_light_device_control.dart b/lib/pages/device_managment/one_gang_switch/view/wall_light_device_control.dart index e5f2358e..2f6008d2 100644 --- a/lib/pages/device_managment/one_gang_switch/view/wall_light_device_control.dart +++ b/lib/pages/device_managment/one_gang_switch/view/wall_light_device_control.dart @@ -1,17 +1,14 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:flutter_svg/svg.dart'; import 'package:syncrow_web/pages/device_managment/one_gang_switch/bloc/wall_light_switch_bloc.dart'; import 'package:syncrow_web/pages/device_managment/one_gang_switch/bloc/wall_light_switch_event.dart'; import 'package:syncrow_web/pages/device_managment/one_gang_switch/bloc/wall_light_switch_state.dart'; import 'package:syncrow_web/pages/device_managment/one_gang_switch/factories/wall_light_switch_bloc_factory.dart'; import 'package:syncrow_web/pages/device_managment/one_gang_switch/models/wall_light_status_model.dart'; import 'package:syncrow_web/pages/device_managment/schedule_device/schedule_widgets/schedual_view.dart'; -import 'package:syncrow_web/pages/device_managment/shared/device_controls_container.dart'; +import 'package:syncrow_web/pages/device_managment/schedule_device/schedule_widgets/schedule_control_button.dart'; import 'package:syncrow_web/pages/device_managment/shared/toggle_widget.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 WallLightDeviceControl extends StatelessWidget @@ -74,51 +71,22 @@ class WallLightDeviceControl extends StatelessWidget )); }, ), - GestureDetector( + ScheduleControlButton( onTap: () { showDialog( context: context, builder: (ctx) => BlocProvider.value( value: BlocProvider.of(context), child: BuildScheduleView( - deviceUuid: deviceId, category: 'switch_1', + deviceUuid: deviceId, ), )); }, - child: DeviceControlsContainer( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Container( - width: 60, - height: 60, - decoration: const BoxDecoration( - shape: BoxShape.circle, - color: ColorsManager.whiteColors, - ), - margin: const EdgeInsets.symmetric(horizontal: 4), - padding: const EdgeInsets.all(12), - child: ClipOval( - child: SvgPicture.asset( - Assets.scheduling, - fit: BoxFit.fill, - ), - ), - ), - const Spacer(), - Text( - 'Scheduling', - textAlign: TextAlign.center, - style: context.textTheme.titleMedium!.copyWith( - fontWeight: FontWeight.w400, - color: ColorsManager.blackColor, - ), - ), - ], - ), - ), - ) + mainText: '', + subtitle: 'Scheduling', + iconPath: Assets.scheduling, + ), ], ); } diff --git a/lib/pages/device_managment/schedule_device/schedule_widgets/schedule_control_button.dart b/lib/pages/device_managment/schedule_device/schedule_widgets/schedule_control_button.dart new file mode 100644 index 00000000..86fc5ba5 --- /dev/null +++ b/lib/pages/device_managment/schedule_device/schedule_widgets/schedule_control_button.dart @@ -0,0 +1,72 @@ +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'; + +class ScheduleControlButton extends StatelessWidget { + final VoidCallback onTap; + final String mainText; + final String subtitle; + final String iconPath; + + const ScheduleControlButton({ + super.key, + required this.onTap, + required this.mainText, + required this.subtitle, + required this.iconPath, + }); + + @override + Widget build(BuildContext context) { + return GestureDetector( + onTap: onTap, + child: DeviceControlsContainer( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Container( + width: 60, + height: 60, + decoration: const BoxDecoration( + shape: BoxShape.circle, + color: ColorsManager.whiteColors, + ), + margin: const EdgeInsets.symmetric(horizontal: 4), + padding: const EdgeInsets.all(12), + child: ClipOval( + child: SvgPicture.asset( + iconPath, + fit: BoxFit.fill, + ), + ), + ), + const Spacer(), + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + mainText, + textAlign: TextAlign.center, + style: Theme.of(context).textTheme.titleMedium!.copyWith( + fontWeight: FontWeight.w200, + fontSize: 12, + color: ColorsManager.blackColor, + ), + ), + Text( + subtitle, + textAlign: TextAlign.center, + style: Theme.of(context).textTheme.titleMedium!.copyWith( + fontWeight: FontWeight.w400, + color: ColorsManager.blackColor, + ), + ), + ], + ), + ], + ), + ), + ); + } +} diff --git a/lib/pages/device_managment/three_g_glass_switch/view/three_gang_glass_switch_control_view.dart b/lib/pages/device_managment/three_g_glass_switch/view/three_gang_glass_switch_control_view.dart index bfc0a73e..72435b74 100644 --- a/lib/pages/device_managment/three_g_glass_switch/view/three_gang_glass_switch_control_view.dart +++ b/lib/pages/device_managment/three_g_glass_switch/view/three_gang_glass_switch_control_view.dart @@ -1,16 +1,12 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:flutter_svg/svg.dart'; import 'package:syncrow_web/pages/device_managment/schedule_device/schedule_widgets/schedual_view.dart'; -import 'package:syncrow_web/pages/device_managment/shared/device_controls_container.dart'; +import 'package:syncrow_web/pages/device_managment/schedule_device/schedule_widgets/schedule_control_button.dart'; import 'package:syncrow_web/pages/device_managment/shared/toggle_widget.dart'; import 'package:syncrow_web/pages/device_managment/three_g_glass_switch/bloc/three_gang_glass_switch_bloc.dart'; import 'package:syncrow_web/pages/device_managment/three_g_glass_switch/factories/three_gang_glass_switch_bloc_factory.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'; - import '../models/three_gang_glass_switch.dart'; class ThreeGangGlassSwitchControlView extends StatelessWidget @@ -106,7 +102,7 @@ class ThreeGangGlassSwitchControlView extends StatelessWidget ); }, ), - GestureDetector( + ScheduleControlButton( onTap: () { showDialog( context: context, @@ -118,54 +114,11 @@ class ThreeGangGlassSwitchControlView extends StatelessWidget ), )); }, - child: DeviceControlsContainer( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Container( - width: 60, - height: 60, - decoration: const BoxDecoration( - shape: BoxShape.circle, - color: ColorsManager.whiteColors, - ), - margin: const EdgeInsets.symmetric(horizontal: 4), - padding: const EdgeInsets.all(12), - child: ClipOval( - child: SvgPicture.asset( - Assets.scheduling, - fit: BoxFit.fill, - ), - ), - ), - const Spacer(), - Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - 'Wall Light', - textAlign: TextAlign.center, - style: context.textTheme.titleMedium!.copyWith( - fontWeight: FontWeight.w200, - fontSize: 12, - color: ColorsManager.blackColor, - ), - ), - Text( - 'Scheduling', - textAlign: TextAlign.center, - style: context.textTheme.titleMedium!.copyWith( - fontWeight: FontWeight.w400, - color: ColorsManager.blackColor, - ), - ), - ], - ), - ], - ), - ), + mainText: 'Wall Light', + subtitle: 'Scheduling', + iconPath: Assets.scheduling, ), - GestureDetector( + ScheduleControlButton( onTap: () { showDialog( context: context, @@ -177,111 +130,25 @@ class ThreeGangGlassSwitchControlView extends StatelessWidget ), )); }, - child: DeviceControlsContainer( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Container( - width: 60, - height: 60, - decoration: const BoxDecoration( - shape: BoxShape.circle, - color: ColorsManager.whiteColors, - ), - margin: const EdgeInsets.symmetric(horizontal: 4), - padding: const EdgeInsets.all(12), - child: ClipOval( - child: SvgPicture.asset( - Assets.scheduling, - fit: BoxFit.fill, - ), - ), - ), - const Spacer(), - Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - 'Ceiling Light', - textAlign: TextAlign.center, - style: context.textTheme.titleMedium!.copyWith( - fontWeight: FontWeight.w200, - fontSize: 12, - color: ColorsManager.blackColor, - ), - ), - Text( - 'Scheduling', - textAlign: TextAlign.center, - style: context.textTheme.titleMedium!.copyWith( - fontWeight: FontWeight.w400, - color: ColorsManager.blackColor, - ), - ), - ], - ), - ], - ), - ), + mainText: 'Ceiling Light', + subtitle: 'Scheduling', + iconPath: Assets.scheduling, ), - GestureDetector( + ScheduleControlButton( onTap: () { showDialog( context: context, builder: (ctx) => BlocProvider.value( value: BlocProvider.of(context), child: BuildScheduleView( - category: 'switch_1', + category: 'switch_3', deviceUuid: deviceId, ), )); }, - child: DeviceControlsContainer( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Container( - width: 60, - height: 60, - decoration: const BoxDecoration( - shape: BoxShape.circle, - color: ColorsManager.whiteColors, - ), - margin: const EdgeInsets.symmetric(horizontal: 4), - padding: const EdgeInsets.all(12), - child: ClipOval( - child: SvgPicture.asset( - Assets.scheduling, - fit: BoxFit.fill, - ), - ), - ), - const Spacer(), - Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - 'SpotLight', - textAlign: TextAlign.center, - style: context.textTheme.titleMedium!.copyWith( - fontWeight: FontWeight.w200, - fontSize: 12, - color: ColorsManager.blackColor, - ), - ), - Text( - 'Scheduling', - textAlign: TextAlign.center, - style: context.textTheme.titleMedium!.copyWith( - fontWeight: FontWeight.w400, - color: ColorsManager.blackColor, - ), - ), - ], - ), - ], - ), - ), + mainText: 'SpotLight', + subtitle: 'Scheduling', + iconPath: Assets.scheduling, ), ToggleWidget( value: false, diff --git a/lib/pages/device_managment/three_gang_switch/view/living_room_device_control.dart b/lib/pages/device_managment/three_gang_switch/view/living_room_device_control.dart index 57c4d397..66784bd5 100644 --- a/lib/pages/device_managment/three_gang_switch/view/living_room_device_control.dart +++ b/lib/pages/device_managment/three_gang_switch/view/living_room_device_control.dart @@ -1,15 +1,12 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:flutter_svg/svg.dart'; import 'package:syncrow_web/pages/device_managment/schedule_device/schedule_widgets/schedual_view.dart'; -import 'package:syncrow_web/pages/device_managment/shared/device_controls_container.dart'; +import 'package:syncrow_web/pages/device_managment/schedule_device/schedule_widgets/schedule_control_button.dart'; import 'package:syncrow_web/pages/device_managment/three_gang_switch/bloc/living_room_bloc.dart'; import 'package:syncrow_web/pages/device_managment/three_gang_switch/factories/living_room_bloc_factory.dart'; import 'package:syncrow_web/pages/device_managment/three_gang_switch/models/living_room_model.dart'; import 'package:syncrow_web/pages/device_managment/shared/toggle_widget.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 LivingRoomDeviceControlsView extends StatelessWidget @@ -96,7 +93,7 @@ class LivingRoomDeviceControlsView extends StatelessWidget ); }, ), - GestureDetector( + ScheduleControlButton( onTap: () { showDialog( context: context, @@ -108,52 +105,11 @@ class LivingRoomDeviceControlsView extends StatelessWidget ), )); }, - child: DeviceControlsContainer( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Container( - width: 60, - height: 60, - decoration: const BoxDecoration( - shape: BoxShape.circle, - color: ColorsManager.whiteColors, - ), - margin: const EdgeInsets.symmetric(horizontal: 4), - padding: const EdgeInsets.all(12), - child: ClipOval( - child: SvgPicture.asset( - Assets.scheduling, - fit: BoxFit.fill, - ), - ), - ), - const Spacer(), - Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - 'Wall Light', - style: context.textTheme.titleMedium!.copyWith( - fontWeight: FontWeight.w200, - fontSize: 12, - color: ColorsManager.blackColor, - ), - ), - Text( - 'Scheduling', - style: context.textTheme.titleMedium!.copyWith( - fontWeight: FontWeight.w400, - color: ColorsManager.blackColor, - ), - ), - ], - ), - ], - ), - ), + mainText: 'Wall Light', + subtitle: 'Scheduling', + iconPath: Assets.scheduling, ), - GestureDetector( + ScheduleControlButton( onTap: () { showDialog( context: context, @@ -165,53 +121,11 @@ class LivingRoomDeviceControlsView extends StatelessWidget ), )); }, - child: DeviceControlsContainer( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Container( - width: 60, - height: 60, - decoration: const BoxDecoration( - shape: BoxShape.circle, - color: ColorsManager.whiteColors, - ), - margin: const EdgeInsets.symmetric(horizontal: 4), - padding: const EdgeInsets.all(12), - child: ClipOval( - child: SvgPicture.asset( - Assets.scheduling, - fit: BoxFit.fill, - ), - ), - ), - const Spacer(), - Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - 'Ceiling Light', - style: context.textTheme.titleMedium!.copyWith( - fontWeight: FontWeight.w200, - fontSize: 12, - color: ColorsManager.blackColor, - ), - ), - Text( - 'Scheduling', - textAlign: TextAlign.center, - style: context.textTheme.titleMedium!.copyWith( - fontWeight: FontWeight.w400, - color: ColorsManager.blackColor, - ), - ), - ], - ), - ], - ), - ), + mainText: 'Ceiling Light', + subtitle: 'Scheduling', + iconPath: Assets.scheduling, ), - GestureDetector( + ScheduleControlButton( onTap: () { showDialog( context: context, @@ -223,51 +137,9 @@ class LivingRoomDeviceControlsView extends StatelessWidget ), )); }, - child: DeviceControlsContainer( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Container( - width: 60, - height: 60, - decoration: const BoxDecoration( - shape: BoxShape.circle, - color: ColorsManager.whiteColors, - ), - margin: const EdgeInsets.symmetric(horizontal: 4), - padding: const EdgeInsets.all(12), - child: ClipOval( - child: SvgPicture.asset( - Assets.scheduling, - fit: BoxFit.fill, - ), - ), - ), - const Spacer(), - Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - 'Spotlight', - style: context.textTheme.titleMedium!.copyWith( - fontWeight: FontWeight.w200, - fontSize: 12, - color: ColorsManager.blackColor, - ), - ), - Text( - 'Scheduling', - textAlign: TextAlign.center, - style: context.textTheme.titleMedium!.copyWith( - fontWeight: FontWeight.w400, - color: ColorsManager.blackColor, - ), - ), - ], - ), - ], - ), - ), + mainText: 'Spotlight', + subtitle: 'Scheduling', + iconPath: Assets.scheduling, ), ], ); diff --git a/lib/pages/device_managment/two_g_glass_switch/view/two_gang_glass_switch_control_view.dart b/lib/pages/device_managment/two_g_glass_switch/view/two_gang_glass_switch_control_view.dart index 3c6d7551..34b30dd3 100644 --- a/lib/pages/device_managment/two_g_glass_switch/view/two_gang_glass_switch_control_view.dart +++ b/lib/pages/device_managment/two_g_glass_switch/view/two_gang_glass_switch_control_view.dart @@ -1,15 +1,12 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:flutter_svg/svg.dart'; import 'package:syncrow_web/pages/device_managment/schedule_device/schedule_widgets/schedual_view.dart'; -import 'package:syncrow_web/pages/device_managment/shared/device_controls_container.dart'; +import 'package:syncrow_web/pages/device_managment/schedule_device/schedule_widgets/schedule_control_button.dart'; import 'package:syncrow_web/pages/device_managment/shared/toggle_widget.dart'; import 'package:syncrow_web/pages/device_managment/two_g_glass_switch/bloc/two_gang_glass_switch_bloc.dart'; import 'package:syncrow_web/pages/device_managment/two_g_glass_switch/factories/two_gang_glass_switch_bloc_factory.dart'; import 'package:syncrow_web/pages/device_managment/two_g_glass_switch/models/two_gang_glass_status_model.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 TwoGangGlassSwitchControlView extends StatelessWidget @@ -98,7 +95,7 @@ class TwoGangGlassSwitchControlView extends StatelessWidget onChange: (value) {}, showToggle: false, ), - GestureDetector( + ScheduleControlButton( onTap: () { showDialog( context: context, @@ -110,54 +107,11 @@ class TwoGangGlassSwitchControlView extends StatelessWidget ), )); }, - child: DeviceControlsContainer( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Container( - width: 60, - height: 60, - decoration: const BoxDecoration( - shape: BoxShape.circle, - color: ColorsManager.whiteColors, - ), - margin: const EdgeInsets.symmetric(horizontal: 4), - padding: const EdgeInsets.all(12), - child: ClipOval( - child: SvgPicture.asset( - Assets.scheduling, - fit: BoxFit.fill, - ), - ), - ), - const Spacer(), - Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - 'Wall Light', - textAlign: TextAlign.center, - style: context.textTheme.titleMedium!.copyWith( - fontWeight: FontWeight.w200, - fontSize: 12, - color: ColorsManager.blackColor, - ), - ), - Text( - 'Scheduling', - textAlign: TextAlign.center, - style: context.textTheme.titleMedium!.copyWith( - fontWeight: FontWeight.w400, - color: ColorsManager.blackColor, - ), - ), - ], - ), - ], - ), - ), + mainText: 'Wall Light', + subtitle: 'Scheduling', + iconPath: Assets.scheduling, ), - GestureDetector( + ScheduleControlButton( onTap: () { showDialog( context: context, @@ -169,53 +123,10 @@ class TwoGangGlassSwitchControlView extends StatelessWidget ), )); }, - child: DeviceControlsContainer( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Container( - width: 60, - height: 60, - decoration: const BoxDecoration( - shape: BoxShape.circle, - color: ColorsManager.whiteColors, - ), - margin: const EdgeInsets.symmetric(horizontal: 4), - padding: const EdgeInsets.all(12), - child: ClipOval( - child: SvgPicture.asset( - Assets.scheduling, - fit: BoxFit.fill, - ), - ), - ), - const Spacer(), - Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - 'Ceiling Light', - textAlign: TextAlign.center, - style: context.textTheme.titleMedium!.copyWith( - fontWeight: FontWeight.w200, - fontSize: 12, - color: ColorsManager.blackColor, - ), - ), - Text( - 'Scheduling', - textAlign: TextAlign.center, - style: context.textTheme.titleMedium!.copyWith( - fontWeight: FontWeight.w400, - color: ColorsManager.blackColor, - ), - ), - ], - ), - ], - ), - ), - ) + mainText: 'Ceiling Light', + subtitle: 'Scheduling', + iconPath: Assets.scheduling, + ), ], ); } diff --git a/lib/pages/device_managment/two_gang_switch/view/wall_light_batch_control.dart b/lib/pages/device_managment/two_gang_switch/view/wall_light_batch_control.dart index e8346cb2..849412f2 100644 --- a/lib/pages/device_managment/two_gang_switch/view/wall_light_batch_control.dart +++ b/lib/pages/device_managment/two_gang_switch/view/wall_light_batch_control.dart @@ -1,6 +1,8 @@ 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/schedule_device/schedule_widgets/schedual_view.dart'; +import 'package:syncrow_web/pages/device_managment/schedule_device/schedule_widgets/schedule_control_button.dart'; import 'package:syncrow_web/pages/device_managment/shared/batch_control/factory_reset.dart'; import 'package:syncrow_web/pages/device_managment/shared/toggle_widget.dart'; import 'package:syncrow_web/pages/device_managment/two_gang_switch/bloc/two_gang_switch_bloc.dart'; @@ -8,9 +10,11 @@ import 'package:syncrow_web/pages/device_managment/two_gang_switch/bloc/two_gang import 'package:syncrow_web/pages/device_managment/two_gang_switch/bloc/two_gang_switch_state.dart'; import 'package:syncrow_web/pages/device_managment/two_gang_switch/factories/two_gang_switch_bloc_factory.dart'; import 'package:syncrow_web/pages/device_managment/two_gang_switch/models/two_gang_status_model.dart'; +import 'package:syncrow_web/utils/constants/assets.dart'; import 'package:syncrow_web/utils/helpers/responsice_layout_helper/responsive_layout_helper.dart'; -class TwoGangBatchControlView extends StatelessWidget with HelperResponsiveLayout { +class TwoGangBatchControlView extends StatelessWidget + with HelperResponsiveLayout { const TwoGangBatchControlView({super.key, required this.deviceIds}); final List deviceIds; @@ -18,15 +22,17 @@ class TwoGangBatchControlView extends StatelessWidget with HelperResponsiveLayou @override Widget build(BuildContext context) { return BlocProvider( - create: (context) => TwoGangSwitchBlocFactory.create(deviceId: deviceIds.first) - ..add(TwoGangSwitchFetchBatchEvent(deviceIds)), + create: (context) => + TwoGangSwitchBlocFactory.create(deviceId: deviceIds.first) + ..add(TwoGangSwitchFetchBatchEvent(deviceIds)), child: BlocBuilder( builder: (context, state) { if (state is TwoGangSwitchLoading) { return const Center(child: CircularProgressIndicator()); } else if (state is TwoGangSwitchStatusLoaded) { return _buildStatusControls(context, state.status); - } else if (state is TwoGangSwitchError || state is TwoGangSwitchControlError) { + } else if (state is TwoGangSwitchError || + state is TwoGangSwitchControlError) { return const Center(child: Text('Error fetching status')); } else { return const Center(child: CircularProgressIndicator()); @@ -82,6 +88,39 @@ class TwoGangBatchControlView extends StatelessWidget with HelperResponsiveLayou )); }, ), + ScheduleControlButton( + onTap: () { + showDialog( + context: context, + builder: (ctx) => BlocProvider.value( + value: BlocProvider.of(context), + child: BuildScheduleView( + category: 'switch_1', + deviceUuid: deviceIds.first, + ), + )); + }, + mainText: 'Wall Light', + subtitle: 'Scheduling', + iconPath: Assets.scheduling, + ), + + ScheduleControlButton( + onTap: () { + showDialog( + context: context, + builder: (ctx) => BlocProvider.value( + value: BlocProvider.of(context), + child: BuildScheduleView( + category: 'switch_2', + deviceUuid: deviceIds.first, + ), + )); + }, + mainText: 'Ceiling Light', + subtitle: 'Scheduling', + iconPath: Assets.scheduling, + ), // FirmwareUpdateWidget( // deviceId: deviceIds.first, // version: 12, diff --git a/lib/pages/device_managment/two_gang_switch/view/wall_light_device_control.dart b/lib/pages/device_managment/two_gang_switch/view/wall_light_device_control.dart index d1f75564..ac3fe579 100644 --- a/lib/pages/device_managment/two_gang_switch/view/wall_light_device_control.dart +++ b/lib/pages/device_managment/two_gang_switch/view/wall_light_device_control.dart @@ -1,17 +1,14 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:flutter_svg/svg.dart'; import 'package:syncrow_web/pages/device_managment/schedule_device/schedule_widgets/schedual_view.dart'; -import 'package:syncrow_web/pages/device_managment/shared/device_controls_container.dart'; +import 'package:syncrow_web/pages/device_managment/schedule_device/schedule_widgets/schedule_control_button.dart'; import 'package:syncrow_web/pages/device_managment/shared/toggle_widget.dart'; import 'package:syncrow_web/pages/device_managment/two_gang_switch/bloc/two_gang_switch_bloc.dart'; import 'package:syncrow_web/pages/device_managment/two_gang_switch/bloc/two_gang_switch_event.dart'; import 'package:syncrow_web/pages/device_managment/two_gang_switch/bloc/two_gang_switch_state.dart'; import 'package:syncrow_web/pages/device_managment/two_gang_switch/factories/two_gang_switch_bloc_factory.dart'; import 'package:syncrow_web/pages/device_managment/two_gang_switch/models/two_gang_status_model.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 TwoGangDeviceControlView extends StatelessWidget @@ -96,7 +93,7 @@ class TwoGangDeviceControlView extends StatelessWidget SizedBox( width: 200, height: 150, - child: GestureDetector( + child: ScheduleControlButton( onTap: () { showDialog( context: context, @@ -109,58 +106,16 @@ class TwoGangDeviceControlView extends StatelessWidget ), )); }, - child: DeviceControlsContainer( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Container( - width: 60, - height: 60, - decoration: const BoxDecoration( - shape: BoxShape.circle, - color: ColorsManager.whiteColors, - ), - margin: const EdgeInsets.symmetric(horizontal: 4), - padding: const EdgeInsets.all(12), - child: ClipOval( - child: SvgPicture.asset( - Assets.scheduling, - fit: BoxFit.fill, - ), - ), - ), - const Spacer(), - Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - 'Wall Light', - textAlign: TextAlign.center, - style: context.textTheme.titleMedium!.copyWith( - fontWeight: FontWeight.w200, - color: ColorsManager.blackColor, - fontSize: 12), - ), - Text( - 'Scheduling', - textAlign: TextAlign.center, - style: context.textTheme.titleMedium!.copyWith( - fontWeight: FontWeight.w400, - color: ColorsManager.blackColor, - ), - ), - ], - ), - ], - ), - ), + mainText: 'Wall Light', + subtitle: 'Scheduling', + iconPath: Assets.scheduling, ), ), const SizedBox(width: 10), SizedBox( width: 200, height: 150, - child: GestureDetector( + child: ScheduleControlButton( onTap: () { showDialog( context: context, @@ -173,51 +128,9 @@ class TwoGangDeviceControlView extends StatelessWidget ), )); }, - child: DeviceControlsContainer( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Container( - width: 60, - height: 60, - decoration: const BoxDecoration( - shape: BoxShape.circle, - color: ColorsManager.whiteColors, - ), - margin: const EdgeInsets.symmetric(horizontal: 4), - padding: const EdgeInsets.all(12), - child: ClipOval( - child: SvgPicture.asset( - Assets.scheduling, - fit: BoxFit.fill, - ), - ), - ), - const Spacer(), - Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - 'Ceiling Light', - textAlign: TextAlign.center, - style: context.textTheme.titleMedium!.copyWith( - fontWeight: FontWeight.w200, - color: ColorsManager.blackColor, - fontSize: 12), - ), - Text( - 'Scheduling', - textAlign: TextAlign.center, - style: context.textTheme.titleMedium!.copyWith( - fontWeight: FontWeight.w400, - color: ColorsManager.blackColor, - ), - ), - ], - ), - ], - ), - ), + mainText: 'Ceiling Light', + subtitle: 'Scheduling', + iconPath: Assets.scheduling, ), ), ], diff --git a/lib/pages/device_managment/water_heater/view/water_heater_device_control.dart b/lib/pages/device_managment/water_heater/view/water_heater_device_control.dart index a847e315..16eff86a 100644 --- a/lib/pages/device_managment/water_heater/view/water_heater_device_control.dart +++ b/lib/pages/device_managment/water_heater/view/water_heater_device_control.dart @@ -2,6 +2,7 @@ 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/schedule_device/schedule_widgets/schedule_control_button.dart'; import 'package:syncrow_web/pages/device_managment/shared/device_controls_container.dart'; import 'package:syncrow_web/pages/device_managment/shared/toggle_widget.dart'; import 'package:syncrow_web/pages/device_managment/water_heater/bloc/water_heater_bloc.dart'; @@ -74,7 +75,7 @@ class WaterHeaterDeviceControlView extends StatelessWidget )); }, ), - GestureDetector( + ScheduleControlButton( onTap: () { showDialog( context: context, @@ -86,39 +87,10 @@ class WaterHeaterDeviceControlView extends StatelessWidget ), )); }, - child: DeviceControlsContainer( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Container( - width: 60, - height: 60, - decoration: const BoxDecoration( - shape: BoxShape.circle, - color: ColorsManager.whiteColors, - ), - margin: const EdgeInsets.symmetric(horizontal: 4), - padding: const EdgeInsets.all(12), - child: ClipOval( - child: SvgPicture.asset( - Assets.scheduling, - fit: BoxFit.fill, - ), - ), - ), - const Spacer(), - Text( - 'Scheduling', - textAlign: TextAlign.center, - style: context.textTheme.titleMedium!.copyWith( - fontWeight: FontWeight.w400, - color: ColorsManager.blackColor, - ), - ), - ], - ), - ), - ) + mainText: '', + subtitle: 'Scheduling', + iconPath: Assets.scheduling, + ), ], ); }