diff --git a/lib/pages/device_managment/all_devices/helper/route_controls_based_code.dart b/lib/pages/device_managment/all_devices/helper/route_controls_based_code.dart index eca980c6..3515ccf7 100644 --- a/lib/pages/device_managment/all_devices/helper/route_controls_based_code.dart +++ b/lib/pages/device_managment/all_devices/helper/route_controls_based_code.dart @@ -9,6 +9,7 @@ import 'package:syncrow_web/pages/device_managment/curtain/view/curtain_batch_st import 'package:syncrow_web/pages/device_managment/curtain/view/curtain_status_view.dart'; import 'package:syncrow_web/pages/device_managment/door_lock/view/door_lock_batch_control_view.dart'; import 'package:syncrow_web/pages/device_managment/door_lock/view/door_lock_control_view.dart'; +import 'package:syncrow_web/pages/device_managment/garage_door/view/garage_door_control_view.dart'; import 'package:syncrow_web/pages/device_managment/gateway/view/gateway_batch_control.dart'; import 'package:syncrow_web/pages/device_managment/gateway/view/gateway_view.dart'; import 'package:syncrow_web/pages/device_managment/main_door_sensor/view/main_door_control_view.dart'; @@ -82,6 +83,10 @@ mixin RouteControlsBasedCode { ); case 'DS': return MainDoorSensorControlView(device: device); + case 'GD': + return GarageDoorControlView( + deviceId: device.uuid!, + ); default: return const SizedBox(); } diff --git a/lib/pages/device_managment/garage_door/bloc/garage_door_bloc.dart b/lib/pages/device_managment/garage_door/bloc/garage_door_bloc.dart new file mode 100644 index 00000000..da93a137 --- /dev/null +++ b/lib/pages/device_managment/garage_door/bloc/garage_door_bloc.dart @@ -0,0 +1,254 @@ +import 'dart:async'; + +import 'package:bloc/bloc.dart'; +import 'package:syncrow_web/pages/device_managment/all_devices/models/device_reports.dart'; +import 'package:syncrow_web/pages/device_managment/all_devices/models/device_status.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/models/garage_door_model.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/services/devices_mang_api.dart'; +import 'package:syncrow_web/utils/format_date_time.dart'; + +class GarageDoorBloc extends Bloc { + final String deviceId; + late GarageDoorStatusModel deviceStatus; + Timer? _timer; + + GarageDoorBloc({required this.deviceId}) : super(GarageDoorInitialState()) { + on(_fetchGarageDoorStatus); + on(_toggleGarageDoor); + on(_addSchedule); + on(_updateSchedule); + on(_deleteSchedule); + on(_fetchSchedules); + on(_increaseDelay); + on(_decreaseDelay); + on(_fetchRecords); + on(_handleUpdate); + on(_updateSelectedTime); + on(_updateSelectedDay); + on(_updateFunctionOn); + on(_initializeAddSchedule); + } + + void _fetchGarageDoorStatus(GarageDoorInitialEvent event, Emitter emit) async { + emit(GarageDoorLoadingState()); + try { + var response = await DevicesManagementApi().getDeviceStatus(event.deviceId); + deviceStatus = GarageDoorStatusModel.fromJson(deviceId, response.status); + emit(GarageDoorLoadedState(status: deviceStatus)); + // _listenToChanges(); + } catch (e) { + emit(GarageDoorErrorState(message: e.toString())); + } + } + + void _toggleGarageDoor(ToggleGarageDoorEvent event, Emitter emit) async { + emit(GarageDoorLoadingNewState(garageDoorModel: deviceStatus)); + deviceStatus = deviceStatus.copyWith(isOpen: event.isOpen); + emit(GarageDoorLoadedState(status: deviceStatus)); + await _runDeBouncer( + deviceId: event.deviceId, + code: event.code, + value: event.isOpen, + emit: emit, + isBatch: false, + ); + } + + Future _addSchedule(AddGarageDoorScheduleEvent event, Emitter emit) async { + emit(GarageDoorLoadingState()); + try { + ScheduleEntry newSchedule = ScheduleEntry( + category: event.category, + time: formatTimeOfDayToISO(event.time), + function: Status(code: 'switch_1', value: event.functionOn), + days: ScheduleModel.convertSelectedDaysToStrings(event.selectedDays), + ); + + bool success = await DevicesManagementApi().addScheduleRecord(newSchedule, deviceId); + + if (success) { + add(FetchGarageDoorSchedulesEvent(deviceId: deviceId, category: event.category)); + } else { + emit(GarageDoorErrorState(message: 'Failed to add schedule.')); + } + } catch (e) { + emit(GarageDoorErrorState(message: e.toString())); + } + } + + Future _updateSchedule(UpdateGarageDoorScheduleEvent event, Emitter emit) async { + emit(GarageDoorLoadingState()); + try { + final updatedSchedules = deviceStatus.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: deviceStatus.uuid, + scheduleId: event.scheduleId, + ); + + if (success) { + deviceStatus = deviceStatus.copyWith(schedules: updatedSchedules); + emit(GarageDoorLoadedState(status: deviceStatus)); + } else { + emit(GarageDoorErrorState(message: 'Failed to update schedule.')); + } + } catch (e) { + emit(GarageDoorErrorState(message: e.toString())); + } + } + + Future _deleteSchedule(DeleteGarageDoorScheduleEvent event, Emitter emit) async { + emit(GarageDoorLoadingState()); + try { + bool success = await DevicesManagementApi().deleteScheduleRecord(deviceStatus.uuid, event.scheduleId); + + if (success) { + final updatedSchedules = + deviceStatus.schedules.where((schedule) => schedule.scheduleId != event.scheduleId).toList(); + deviceStatus = deviceStatus.copyWith(schedules: updatedSchedules); + emit(GarageDoorLoadedState(status: deviceStatus)); + } else { + emit(GarageDoorErrorState(message: 'Failed to delete schedule.')); + } + } catch (e) { + emit(GarageDoorErrorState(message: e.toString())); + } + } + + Future _fetchSchedules(FetchGarageDoorSchedulesEvent event, Emitter emit) async { + emit(GarageDoorLoadingState()); + try { + List schedules = + await DevicesManagementApi().getDeviceSchedules(deviceStatus.uuid, event.category); + + deviceStatus = deviceStatus.copyWith(schedules: schedules); + emit(GarageDoorLoadedState(status: deviceStatus)); + } catch (e) { + emit(GarageDoorErrorState(message: 'Failed to fetch schedules.')); + } + } + + void _increaseDelay(IncreaseGarageDoorDelayEvent event, Emitter emit) async { + emit(GarageDoorLoadingNewState(garageDoorModel: deviceStatus)); + try { + deviceStatus = deviceStatus.copyWith(delay: deviceStatus.delay + Duration(minutes: 10)); + emit(GarageDoorLoadedState(status: deviceStatus)); + } catch (e) { + emit(GarageDoorErrorState(message: e.toString())); + } + } + + void _decreaseDelay(DecreaseGarageDoorDelayEvent event, Emitter emit) async { + emit(GarageDoorLoadingNewState(garageDoorModel: deviceStatus)); + try { + if (deviceStatus.delay.inMinutes > 10) { + deviceStatus = deviceStatus.copyWith(delay: deviceStatus.delay - Duration(minutes: 10)); + } + emit(GarageDoorLoadedState(status: deviceStatus)); + } catch (e) { + emit(GarageDoorErrorState(message: e.toString())); + } + } + + Future _updateSelectedTime(UpdateSelectedTimeEvent event, Emitter emit) async { + final currentState = state; + if (currentState is GarageDoorLoadedState) { + emit(currentState.copyWith(selectedTime: event.selectedTime)); + } + } + + Future _updateSelectedDay(UpdateSelectedDayEvent event, Emitter emit) async { + final currentState = state; + if (currentState is GarageDoorLoadedState) { + List updatedDays = List.from(currentState.selectedDays); + updatedDays[event.dayIndex] = event.isSelected; + emit(currentState.copyWith(selectedDays: updatedDays)); + } + } + + Future _updateFunctionOn(UpdateFunctionOnEvent event, Emitter emit) async { + final currentState = state; + if (currentState is GarageDoorLoadedState) { + emit(currentState.copyWith(functionOn: event.functionOn)); + } + } + + Future _initializeAddSchedule(InitializeAddScheduleEvent event, Emitter emit) async { + final currentState = state; + if (currentState is GarageDoorLoadedState) { + emit(currentState.copyWith( + selectedTime: event.selectedTime, + selectedDays: event.selectedDays, + functionOn: event.functionOn, + isEditing: event.isEditing, + )); + } + } + + Future _fetchRecords(FetchGarageDoorRecordsEvent event, Emitter emit) async { + emit(GarageDoorReportsLoadingState()); + try { + final DeviceReport records = await DevicesManagementApi.getDeviceReports(event.deviceId, 'code_for_records'); + emit(GarageDoorReportsState(deviceReport: records)); + } catch (e) { + emit(GarageDoorReportsFailedState(error: e.toString())); + } + } + + void _handleUpdate(GarageDoorUpdatedEvent event, Emitter emit) { + emit(GarageDoorLoadedState(status: deviceStatus)); + } + + Future _runDeBouncer({ + required dynamic deviceId, + required String code, + required dynamic value, + required Emitter emit, + required bool isBatch, + }) async { + late String id; + if (deviceId is List) { + id = deviceId.first; + } else { + id = deviceId; + } + + if (_timer != null) { + _timer!.cancel(); + } + _timer = Timer(const Duration(seconds: 1), () async { + try { + late bool response; + if (isBatch) { + response = await DevicesManagementApi().deviceBatchControl(deviceId, code, value); + } else { + response = await DevicesManagementApi().deviceControl(deviceId, Status(code: code, value: value)); + } + + if (!response) { + add(GarageDoorInitialEvent(id)); + } else if (response == true && code == 'scene') { + emit(GarageDoorLoadingState()); + await Future.delayed(const Duration(seconds: 1)); + add(GarageDoorInitialEvent(id)); + } + } catch (_) { + await Future.delayed(const Duration(milliseconds: 500)); + add(GarageDoorInitialEvent(id)); + } + }); + } +} diff --git a/lib/pages/device_managment/garage_door/bloc/garage_door_event.dart b/lib/pages/device_managment/garage_door/bloc/garage_door_event.dart new file mode 100644 index 00000000..754ae140 --- /dev/null +++ b/lib/pages/device_managment/garage_door/bloc/garage_door_event.dart @@ -0,0 +1,165 @@ +// lib/pages/device_managment/garage_door/bloc/event.dart + +import 'package:equatable/equatable.dart'; +import 'package:flutter/material.dart'; + +abstract class GarageDoorEvent extends Equatable { + const GarageDoorEvent(); + + @override + List get props => []; +} + +class GarageDoorInitialEvent extends GarageDoorEvent { + final String deviceId; + + const GarageDoorInitialEvent(this.deviceId); + + @override + List get props => [deviceId]; +} + +class ToggleGarageDoorEvent extends GarageDoorEvent { + final String deviceId; + final bool isOpen; + final String code; + + const ToggleGarageDoorEvent({required this.deviceId, required this.isOpen, required this.code}); + + @override + List get props => [deviceId, isOpen]; +} + +class AddGarageDoorScheduleEvent extends GarageDoorEvent { + final String category; + final TimeOfDay time; + final bool functionOn; + final List selectedDays; + + const AddGarageDoorScheduleEvent({ + required this.category, + required this.time, + required this.functionOn, + required this.selectedDays, + }); +} + +class UpdateGarageDoorScheduleEvent extends GarageDoorEvent { + final String deviceId; + final String scheduleId; + final bool enable; + final bool functionOn; + final int index; + + const UpdateGarageDoorScheduleEvent({ + required this.deviceId, + required this.scheduleId, + required this.enable, + required this.functionOn, + required this.index, + }); +} + +class DeleteGarageDoorScheduleEvent extends GarageDoorEvent { + final String deviceId; + final String scheduleId; + final int index; + + const DeleteGarageDoorScheduleEvent({ + required this.deviceId, + required this.scheduleId, + required this.index, + }); +} + +class FetchGarageDoorSchedulesEvent extends GarageDoorEvent { + final String deviceId; + final String category; + + const FetchGarageDoorSchedulesEvent({ + required this.deviceId, + required this.category, + }); +} + +class IncreaseGarageDoorDelayEvent extends GarageDoorEvent { + final String deviceId; + + const IncreaseGarageDoorDelayEvent({required this.deviceId}); + + @override + List get props => [deviceId]; +} + +class DecreaseGarageDoorDelayEvent extends GarageDoorEvent { + final String deviceId; + + const DecreaseGarageDoorDelayEvent({required this.deviceId}); + + @override + List get props => [deviceId]; +} + +class FetchGarageDoorRecordsEvent extends GarageDoorEvent { + final String deviceId; + + const FetchGarageDoorRecordsEvent({required this.deviceId}); + + @override + List get props => [deviceId]; +} + +class GarageDoorUpdatedEvent extends GarageDoorEvent {} + +class UpdateSelectedTimeEvent extends GarageDoorEvent { + final TimeOfDay? selectedTime; + + const UpdateSelectedTimeEvent(this.selectedTime); + + @override + List get props => [selectedTime]; +} + +class UpdateSelectedDayEvent extends GarageDoorEvent { + final int dayIndex; + final bool isSelected; + + const UpdateSelectedDayEvent(this.dayIndex, this.isSelected); + + @override + List get props => [dayIndex, isSelected]; +} + +class UpdateFunctionOnEvent extends GarageDoorEvent { + final bool functionOn; + + const UpdateFunctionOnEvent({required this.functionOn}); + + @override + List get props => [functionOn]; +} + +class InitializeAddScheduleEvent extends GarageDoorEvent { + final TimeOfDay? selectedTime; + final List selectedDays; + final bool functionOn; + final bool isEditing; + final int? index; + + const InitializeAddScheduleEvent({ + required this.selectedTime, + required this.selectedDays, + required this.functionOn, + required this.isEditing, + this.index, + }); + + @override + List get props => [ + selectedTime, + selectedDays, + functionOn, + isEditing, + index, + ]; +} diff --git a/lib/pages/device_managment/garage_door/bloc/garage_door_state.dart b/lib/pages/device_managment/garage_door/bloc/garage_door_state.dart new file mode 100644 index 00000000..24b07618 --- /dev/null +++ b/lib/pages/device_managment/garage_door/bloc/garage_door_state.dart @@ -0,0 +1,129 @@ +// lib/pages/device_managment/garage_door/bloc/state.dart + +import 'package:equatable/equatable.dart'; +import 'package:flutter/material.dart'; +import 'package:syncrow_web/pages/device_managment/all_devices/models/device_reports.dart'; +import 'package:syncrow_web/pages/device_managment/garage_door/models/garage_door_model.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'; + +abstract class GarageDoorState extends Equatable { + const GarageDoorState(); + + @override + List get props => []; +} + +class GarageDoorInitialState extends GarageDoorState {} + +class GarageDoorLoadingState extends GarageDoorState {} + +class GarageDoorLoadedState extends GarageDoorState { + final GarageDoorStatusModel status; + final List? schedules; + final Duration? delay; + final DeviceReport? records; + final List selectedDays; + final TimeOfDay? selectedTime; + final bool functionOn; + final bool isEditing; + final ScheduleModes? scheduleMode; + + const GarageDoorLoadedState({ + required this.status, + this.schedules, + this.delay, + this.records, + this.selectedDays = const [false, false, false, false, false, false, false], + this.selectedTime, + this.functionOn = false, + this.isEditing = false, + this.scheduleMode = ScheduleModes.schedule, + }); + + @override + List get props => [ + status, + schedules, + delay, + records, + selectedDays, + selectedTime, + functionOn, + isEditing, + scheduleMode, + ]; + + GarageDoorLoadedState copyWith({ + GarageDoorStatusModel? status, + List? schedules, + Duration? delay, + DeviceReport? records, + List? selectedDays, + TimeOfDay? selectedTime, + bool? functionOn, + bool? isEditing, + ScheduleModes? scheduleMode, + }) { + return GarageDoorLoadedState( + status: status ?? this.status, + schedules: schedules ?? this.schedules, + delay: delay ?? this.delay, + records: records ?? this.records, + selectedDays: selectedDays ?? this.selectedDays, + selectedTime: selectedTime, + functionOn: functionOn ?? this.functionOn, + isEditing: isEditing ?? this.isEditing, + scheduleMode: scheduleMode ?? this.scheduleMode, + ); + } +} + +class GarageDoorErrorState extends GarageDoorState { + final String message; + + const GarageDoorErrorState({required this.message}); + + @override + List get props => [message]; +} + +class GarageDoorLoadingNewState extends GarageDoorState { + final GarageDoorStatusModel garageDoorModel; + + const GarageDoorLoadingNewState({required this.garageDoorModel}); + + @override + List get props => [garageDoorModel]; +} + +class GarageDoorReportsLoadingState extends GarageDoorState {} + +class GarageDoorReportsFailedState extends GarageDoorState { + final String error; + + const GarageDoorReportsFailedState({required this.error}); + + @override + List get props => [error]; +} + +class GarageDoorReportsState extends GarageDoorState { + final DeviceReport deviceReport; + + const GarageDoorReportsState({required this.deviceReport}); + + @override + List get props => [deviceReport]; +} + +class ShowGarageDoorDescriptionState extends GarageDoorState { + final String description; + + const ShowGarageDoorDescriptionState({required this.description}); + + @override + List get props => [description]; +} + +class ScheduleGarageLoadingState extends GarageDoorState {} 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 new file mode 100644 index 00000000..e939ad9e --- /dev/null +++ b/lib/pages/device_managment/garage_door/helper/garage_door_helper.dart @@ -0,0 +1,252 @@ +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/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/water_heater/models/schedule_model.dart'; +import 'package:syncrow_web/utils/color_manager.dart'; +import 'package:syncrow_web/utils/extension/build_context_x.dart'; + +class ScheduleGarageDoorDialogHelper { + static void showAddGarageDoorScheduleDialog(BuildContext context, + {ScheduleModel? schedule, int? index, bool? isEdit}) { + final bloc = context.read(); + + 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); + + bloc.add(InitializeAddScheduleEvent( + selectedTime: time, + selectedDays: selectedDays, + functionOn: schedule.function.value, + isEditing: true, + index: index, + )); + } + + showDialog( + context: context, + builder: (ctx) { + return BlocProvider.value( + value: bloc, + child: BlocBuilder( + builder: (context, state) { + if (state is GarageDoorLoadedState) { + return AlertDialog( + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(20), + ), + content: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + const SizedBox(), + Text( + 'Scheduling', + style: context.textTheme.titleLarge!.copyWith( + color: ColorsManager.dialogBlueTitle, + fontWeight: FontWeight.bold, + ), + ), + const SizedBox(), + ], + ), + const SizedBox(height: 24), + SizedBox( + width: 150, + height: 40, + child: DefaultButton( + padding: 8, + backgroundColor: ColorsManager.boxColor, + borderRadius: 15, + onPressed: isEdit == true + ? null + : () 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), + ], + ), + actions: [ + SizedBox( + width: 200, + child: DefaultButton( + height: 40, + onPressed: () { + Navigator.pop(context); + }, + backgroundColor: ColorsManager.boxColor, + child: Text( + 'Cancel', + style: context.textTheme.bodyMedium, + ), + ), + ), + SizedBox( + width: 200, + child: DefaultButton( + height: 40, + onPressed: () { + if (state.selectedTime != null) { + if (state.isEditing && index != null) { + return; + } else { + bloc.add(AddGarageDoorScheduleEvent( + category: 'switch_1', + time: state.selectedTime!, + selectedDays: state.selectedDays, + functionOn: state.functionOn, + )); + } + Navigator.pop(context); + } + }, + backgroundColor: ColorsManager.primaryColor, + child: const Text('Save'), + ), + ), + ], + ); + } + return const SizedBox(); + }, + ), + ); + }, + ); + } + + 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 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; + } + + static Widget _buildDayCheckboxes(BuildContext context, List selectedDays, {bool? isEdit}) { + final dayLabels = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat']; + + return Row( + children: List.generate(7, (index) { + return Row( + children: [ + Checkbox( + value: selectedDays[index], + onChanged: isEdit == true + ? null + : (bool? value) { + context.read().add(UpdateSelectedDayEvent(index, value!)); + }, + ), + Text(dayLabels[index]), + ], + ); + }), + ); + } + + static Widget _buildFunctionSwitch(BuildContext context, bool isOn, bool? isEdit) { + return Row( + children: [ + Text( + 'Function:', + style: context.textTheme.bodySmall!.copyWith(color: ColorsManager.grayColor), + ), + const SizedBox(width: 10), + Radio( + value: true, + groupValue: isOn, + onChanged: (bool? value) { + if (isEdit == true) { + return; + } else { + context.read().add(const UpdateFunctionOnEvent(functionOn: true)); + } + }, + ), + const Text('On'), + const SizedBox(width: 10), + Radio( + value: false, + groupValue: isOn, + onChanged: (bool? value) { + if (isEdit == true) { + return; + } else { + context.read().add(const UpdateFunctionOnEvent(functionOn: false)); + } + }, + ), + const Text('Off'), + ], + ); + } +} diff --git a/lib/pages/device_managment/garage_door/models/garage_door_model.dart b/lib/pages/device_managment/garage_door/models/garage_door_model.dart new file mode 100644 index 00000000..9e8d75fe --- /dev/null +++ b/lib/pages/device_managment/garage_door/models/garage_door_model.dart @@ -0,0 +1,123 @@ +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 GarageDoorStatusModel { + final String uuid; + final bool switch1; + final int countdown1; + final bool doorContactState; + final int trTimeCon; + final int countdownAlarm; + final String doorControl1; + final bool voiceControl1; + final String doorState1; + final bool isOpen; + final Duration delay; + final List schedules; // Add schedules field + + GarageDoorStatusModel({ + required this.uuid, + required this.switch1, + required this.countdown1, + required this.doorContactState, + required this.trTimeCon, + required this.countdownAlarm, + required this.doorControl1, + required this.voiceControl1, + required this.doorState1, + required this.isOpen, + required this.delay, + required this.schedules, // Initialize schedules + }); + + factory GarageDoorStatusModel.fromJson(String id, List jsonList) { + late bool switch1; + late int countdown1; + late bool doorContactState; + late int trTimeCon; + late int countdownAlarm; + late String doorControl1; + late bool voiceControl1; + late String doorState1; + List schedules = []; // Initialize schedules + + for (var status in jsonList) { + switch (status.code) { + case 'switch_1': + switch1 = status.value ?? false; + break; + case 'countdown_1': + countdown1 = status.value ?? 0; + break; + case 'doorcontact_state': + doorContactState = status.value ?? false; + break; + case 'tr_timecon': + trTimeCon = status.value ?? 0; + break; + case 'countdown_alarm': + countdownAlarm = status.value ?? 0; + break; + case 'door_control_1': + doorControl1 = status.value ?? 'closed'; + break; + case 'voice_control_1': + voiceControl1 = status.value ?? false; + break; + case 'door_state_1': + doorState1 = status.value ?? 'closed'; + break; + } + } + + return GarageDoorStatusModel( + uuid: id, + switch1: switch1, + countdown1: countdown1, + doorContactState: doorContactState, + trTimeCon: trTimeCon, + countdownAlarm: countdownAlarm, + doorControl1: doorControl1, + voiceControl1: voiceControl1, + doorState1: doorState1, + isOpen: doorState1 == 'open' ? true : false, + delay: Duration(seconds: countdown1), + schedules: schedules, // Assign schedules + ); + } + + GarageDoorStatusModel copyWith({ + String? uuid, + bool? switch1, + int? countdown1, + bool? doorContactState, + int? trTimeCon, + int? countdownAlarm, + String? doorControl1, + bool? voiceControl1, + String? doorState1, + bool? isOpen, + Duration? delay, + List? schedules, // Add schedules to copyWith + }) { + return GarageDoorStatusModel( + uuid: uuid ?? this.uuid, + switch1: switch1 ?? this.switch1, + countdown1: countdown1 ?? this.countdown1, + doorContactState: doorContactState ?? this.doorContactState, + trTimeCon: trTimeCon ?? this.trTimeCon, + countdownAlarm: countdownAlarm ?? this.countdownAlarm, + doorControl1: doorControl1 ?? this.doorControl1, + voiceControl1: voiceControl1 ?? this.voiceControl1, + doorState1: doorState1 ?? this.doorState1, + isOpen: isOpen ?? this.isOpen, + delay: delay ?? this.delay, + schedules: schedules ?? this.schedules, // Copy schedules + ); + } + + @override + String toString() { + return 'GarageDoorStatusModel(uuid: $uuid, switch1: $switch1, countdown1: $countdown1, doorContactState: $doorContactState, trTimeCon: $trTimeCon, countdownAlarm: $countdownAlarm, doorControl1: $doorControl1, voiceControl1: $voiceControl1, doorState1: $doorState1, isOpen: $isOpen, delay: $delay, schedules: $schedules)'; + } +} diff --git a/lib/pages/device_managment/garage_door/view/garage_door_batch_control_view.dart b/lib/pages/device_managment/garage_door/view/garage_door_batch_control_view.dart new file mode 100644 index 00000000..e69de29b 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 new file mode 100644 index 00000000..8e6a62f0 --- /dev/null +++ b/lib/pages/device_managment/garage_door/view/garage_door_control_view.dart @@ -0,0 +1,177 @@ +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/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/models/garage_door_model.dart'; +import 'package:syncrow_web/pages/device_managment/garage_door/widgets/schedule_garage_view.dart'; +import 'package:syncrow_web/pages/device_managment/shared/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'; + +import '../../main_door_sensor/view/main_door_control_view.dart'; + +class GarageDoorControlView extends StatelessWidget with HelperResponsiveLayout { + final String deviceId; + + const GarageDoorControlView({required this.deviceId, super.key}); + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar(title: const Text('Garage Door Control')), + body: BlocProvider( + create: (context) => GarageDoorBloc(deviceId: deviceId)..add(GarageDoorInitialEvent(deviceId)), + child: BlocBuilder( + builder: (context, state) { + if (state is GarageDoorLoadingState || state is GarageDoorLoadingNewState) { + return const Center(child: CircularProgressIndicator()); + } else if (state is GarageDoorLoadedState) { + return _buildControlView(context, state.status); + } else if (state is GarageDoorErrorState) { + return Center(child: Text('Error: ${state.message}')); + } + return const Center(child: Text('Unknown state')); + }, + ), + ), + ); + } + + Widget _buildControlView(BuildContext context, GarageDoorStatusModel status) { + final isExtraLarge = isExtraLargeScreenSize(context); + final isLarge = isLargeScreenSize(context); + final isMedium = isMediumScreenSize(context); + + return GridView( + shrinkWrap: true, + physics: const NeverScrollableScrollPhysics(), + gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( + crossAxisCount: isLarge || isExtraLarge + ? 3 + : isMedium + ? 2 + : 1, + childAspectRatio: 1.5, + crossAxisSpacing: 12, + mainAxisSpacing: 12, + ), + children: [ + IconNameStatusContainer( + isFullIcon: true, + name: status.doorContactState ? 'Open' : 'Close', + icon: Assets.openCloseDoor, + onTap: () { + context.read().add( + ToggleGarageDoorEvent(deviceId: status.uuid, isOpen: !status.isOpen, code: 'switch_1'), + ); + }, + status: status.doorContactState, + textColor: status.doorContactState ? ColorsManager.red : ColorsManager.blackColor, + paddingAmount: 8, + ), + GestureDetector( + onTap: () { + showDialog( + context: context, + builder: (ctx) => BlocProvider.value( + value: BlocProvider.of(context), + child: BuildGarageDoorScheduleView(status: status), + )); + }, + child: DeviceControlsContainer( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + mainAxisSize: MainAxisSize.min, + children: [ + Container( + width: 60, + height: 60, + decoration: const BoxDecoration( + shape: BoxShape.circle, + color: ColorsManager.whiteColors, + ), + padding: const EdgeInsets.all(12), + child: ClipOval( + child: SvgPicture.asset( + Assets.scheduling, + fit: BoxFit.fill, + ), + ), + ), + const SizedBox(height: 8), + Text( + 'Scheduling', + textAlign: TextAlign.center, + style: context.textTheme.titleMedium!.copyWith( + fontWeight: FontWeight.w400, + color: ColorsManager.blackColor, + ), + ), + ], + ), + ), + ), + ToggleWidget( + label: '', + labelWidget: Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + IconButton( + onPressed: () { + context.read().add(DecreaseGarageDoorDelayEvent(deviceId: deviceId)); + }, + icon: const Icon( + Icons.remove, + size: 28, + color: ColorsManager.greyColor, + ), + ), + Text( + '${status.delay.inHours.toString().padLeft(2, '0')}', + style: context.textTheme.titleLarge!.copyWith( + color: ColorsManager.dialogBlueTitle, + fontWeight: FontWeight.bold, + ), + ), + Text( + 'h', + style: context.textTheme.bodySmall!.copyWith(color: ColorsManager.blackColor), + ), + Text( + '${(status.delay.inMinutes % 60).toString().padLeft(2, '0')}', + style: context.textTheme.titleLarge!.copyWith( + color: ColorsManager.dialogBlueTitle, + fontWeight: FontWeight.bold, + ), + ), + Text( + 'm', + style: context.textTheme.bodySmall!.copyWith(color: ColorsManager.blackColor), + ), + IconButton( + onPressed: () { + context.read().add(IncreaseGarageDoorDelayEvent(deviceId: deviceId)); + }, + icon: const Icon( + Icons.add, + size: 28, + color: ColorsManager.greyColor, + ), + ), + ], + ), + value: false, + code: 'switch_1', + deviceId: status.uuid, + icon: Assets.acSchedule, + onChange: (value) {}, + ), + ], + ); + } +} diff --git a/lib/pages/device_managment/garage_door/widgets/schedule__garage_table.dart b/lib/pages/device_managment/garage_door/widgets/schedule__garage_table.dart new file mode 100644 index 00000000..4bd54aa8 --- /dev/null +++ b/lib/pages/device_managment/garage_door/widgets/schedule__garage_table.dart @@ -0,0 +1,212 @@ +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/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/helper/garage_door_helper.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'; + +class ScheduleGarageTableWidget extends StatelessWidget { + final GarageDoorLoadedState state; + + const ScheduleGarageTableWidget({ + super.key, + required this.state, + }); + + @override + Widget build(BuildContext context) { + return Column( + children: [ + Table( + border: TableBorder.all( + color: ColorsManager.graysColor, + borderRadius: const BorderRadius.only(topLeft: Radius.circular(20), topRight: Radius.circular(20)), + ), + children: [ + TableRow( + decoration: const BoxDecoration( + color: ColorsManager.boxColor, + borderRadius: BorderRadius.only( + topLeft: Radius.circular(20), + topRight: Radius.circular(20), + ), + ), + children: [ + _buildTableHeader('Active'), + _buildTableHeader('Days'), + _buildTableHeader('Time'), + _buildTableHeader('Function'), + _buildTableHeader('Action'), + ], + ), + ], + ), + BlocBuilder( + builder: (context, state) { + if (state is ScheduleGarageLoadingState) { + return const SizedBox(height: 200, child: Center(child: CircularProgressIndicator())); + } + if (state is GarageDoorLoadedState && state.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)), + ), + child: _buildTableBody(state, context)); + } + return const SizedBox( + height: 200, + ); + }, + ), + ], + ); + } + + Widget _buildEmptyState(BuildContext context) { + return Container( + height: 200, + decoration: BoxDecoration( + border: Border.all(color: ColorsManager.graysColor), + borderRadius: const BorderRadius.only(bottomLeft: Radius.circular(20), bottomRight: Radius.circular(20)), + ), + child: Center( + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + SvgPicture.asset(Assets.emptyRecords, width: 40, height: 40), + const SizedBox(height: 8), + Padding( + padding: const EdgeInsets.all(8.0), + child: Text( + 'No schedules added yet', + style: context.textTheme.bodySmall!.copyWith( + fontSize: 13, + color: ColorsManager.grayColor, + ), + ), + ), + ], + ), + ), + ); + } + + Widget _buildTableBody(GarageDoorLoadedState state, BuildContext context) { + return SizedBox( + height: 200, + child: SingleChildScrollView( + child: Table( + border: TableBorder.all(color: ColorsManager.graysColor), + defaultVerticalAlignment: TableCellVerticalAlignment.middle, + children: [ + if (state.schedules != null) + for (int i = 0; i < state.schedules!.length; i++) + _buildScheduleRow(state.schedules![i], i, context, state), + ], + ), + ), + ); + } + + Widget _buildTableHeader(String label) { + return TableCell( + child: Padding( + padding: const EdgeInsets.all(12), + child: Text( + label, + style: const TextStyle( + fontSize: 13, + color: ColorsManager.grayColor, + ), + ), + ), + ); + } + + TableRow _buildScheduleRow(ScheduleModel schedule, int index, BuildContext context, GarageDoorLoadedState state) { + return TableRow( + children: [ + Center( + child: GestureDetector( + onTap: () { + context.read().add(UpdateGarageDoorScheduleEvent( + index: index, + enable: !schedule.enable, + scheduleId: schedule.scheduleId, + deviceId: state.status.uuid, + functionOn: schedule.function.value, + )); + }, + 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, + ), + ), + ), + ), + Center(child: Text(_getSelectedDays(ScheduleModel.parseSelectedDays(schedule.days)))), + Center(child: Text(formatIsoStringToTime(schedule.time, context))), + Center(child: Text(schedule.function.value ? 'On' : 'Off')), + Center( + child: Wrap( + runAlignment: WrapAlignment.center, + children: [ + TextButton( + style: TextButton.styleFrom(padding: EdgeInsets.zero), + onPressed: () { + ScheduleGarageDoorDialogHelper.showAddGarageDoorScheduleDialog(context, + schedule: schedule, index: index, isEdit: true); + }, + child: Text( + 'Edit', + style: context.textTheme.bodySmall!.copyWith(color: ColorsManager.blueColor), + ), + ), + TextButton( + style: TextButton.styleFrom(padding: EdgeInsets.zero), + onPressed: () { + context.read().add(DeleteGarageDoorScheduleEvent( + index: index, + scheduleId: schedule.scheduleId, + deviceId: state.status.uuid, + )); + }, + child: Text( + 'Delete', + style: context.textTheme.bodySmall!.copyWith(color: ColorsManager.blueColor), + ), + ), + ], + ), + ), + ], + ); + } + + 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(', '); + } +} diff --git a/lib/pages/device_managment/garage_door/widgets/schedule_garage_header.dart b/lib/pages/device_managment/garage_door/widgets/schedule_garage_header.dart new file mode 100644 index 00000000..cf42e6a3 --- /dev/null +++ b/lib/pages/device_managment/garage_door/widgets/schedule_garage_header.dart @@ -0,0 +1,46 @@ +import 'package:flutter/material.dart'; +import 'package:syncrow_web/utils/color_manager.dart'; + +class ScheduleGarageHeader extends StatelessWidget { + const ScheduleGarageHeader({super.key}); + + @override + Widget build(BuildContext context) { + return Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + const SizedBox(), + Text( + 'Scheduling', + style: TextStyle( + fontWeight: FontWeight.bold, + fontSize: 22, + color: ColorsManager.dialogBlueTitle, + ), + ), + Container( + width: 25, + decoration: BoxDecoration( + color: Colors.transparent, + shape: BoxShape.circle, + border: Border.all( + color: Colors.grey, + width: 1.0, + ), + ), + child: IconButton( + padding: const EdgeInsets.all(1), + icon: const Icon( + Icons.close, + color: Colors.grey, + size: 18, + ), + onPressed: () { + Navigator.of(context).pop(); + }, + ), + ), + ], + ); + } +} diff --git a/lib/pages/device_managment/garage_door/widgets/schedule_garage_managment_ui.dart b/lib/pages/device_managment/garage_door/widgets/schedule_garage_managment_ui.dart new file mode 100644 index 00000000..e5819e89 --- /dev/null +++ b/lib/pages/device_managment/garage_door/widgets/schedule_garage_managment_ui.dart @@ -0,0 +1,50 @@ +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/utils/color_manager.dart'; +import 'package:syncrow_web/utils/extension/build_context_x.dart'; + +class ScheduleGarageManagementUI extends StatelessWidget { + final GarageDoorLoadedState state; + final Function onAddSchedule; + + const ScheduleGarageManagementUI({ + super.key, + required this.state, + required this.onAddSchedule, + }); + + @override + Widget build(BuildContext context) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + SizedBox( + width: 170, + height: 40, + child: DefaultButton( + borderColor: ColorsManager.boxColor, + padding: 2, + backgroundColor: ColorsManager.graysColor, + borderRadius: 15, + onPressed: () => onAddSchedule(), + child: Row( + children: [ + const Icon(Icons.add, color: ColorsManager.primaryColor), + Text( + ' Add new schedule', + style: context.textTheme.bodySmall!.copyWith( + color: ColorsManager.blackColor, + ), + ), + ], + ), + ), + ), + const SizedBox(height: 20), + ScheduleGarageTableWidget(state: state), + ], + ); + } +} diff --git a/lib/pages/device_managment/garage_door/widgets/schedule_garage_mode_buttons.dart b/lib/pages/device_managment/garage_door/widgets/schedule_garage_mode_buttons.dart new file mode 100644 index 00000000..f1307d5f --- /dev/null +++ b/lib/pages/device_managment/garage_door/widgets/schedule_garage_mode_buttons.dart @@ -0,0 +1,45 @@ +import 'package:flutter/material.dart'; +import 'package:syncrow_web/pages/common/buttons/default_button.dart'; +import 'package:syncrow_web/utils/color_manager.dart'; +import 'package:syncrow_web/utils/extension/build_context_x.dart'; + +class ScheduleModeButtons extends StatelessWidget { + final VoidCallback onSave; + + const ScheduleModeButtons({ + super.key, + required this.onSave, + }); + + @override + Widget build(BuildContext context) { + return Row( + mainAxisAlignment: MainAxisAlignment.center, + mainAxisSize: MainAxisSize.max, + children: [ + Expanded( + child: DefaultButton( + height: 40, + onPressed: () { + Navigator.pop(context); + }, + backgroundColor: ColorsManager.boxColor, + child: Text( + 'Cancel', + style: context.textTheme.bodyMedium, + ), + ), + ), + const SizedBox(width: 20), + Expanded( + child: DefaultButton( + height: 40, + onPressed: onSave, + backgroundColor: ColorsManager.primaryColor, + child: const Text('Save'), + ), + ), + ], + ); + } +} diff --git a/lib/pages/device_managment/garage_door/widgets/schedule_garage_mode_selector.dart b/lib/pages/device_managment/garage_door/widgets/schedule_garage_mode_selector.dart new file mode 100644 index 00000000..7b6e4690 --- /dev/null +++ b/lib/pages/device_managment/garage_door/widgets/schedule_garage_mode_selector.dart @@ -0,0 +1,68 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.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/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 ScheduleGarageDoorModeSelector extends StatelessWidget { + final GarageDoorLoadedState state; + + const ScheduleGarageDoorModeSelector({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, 'Schedule', ScheduleModes.schedule, state), + ], + ), + ], + ); + } + + Widget _buildRadioTile(BuildContext context, String label, ScheduleModes mode, GarageDoorLoadedState 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.schedule) { + context.read().add( + FetchGarageDoorSchedulesEvent( + category: 'switch_1', + deviceId: state.status.uuid, + ), + ); + } + } + }, + ), + ), + ); + } +} diff --git a/lib/pages/device_managment/garage_door/widgets/schedule_garage_view.dart b/lib/pages/device_managment/garage_door/widgets/schedule_garage_view.dart new file mode 100644 index 00000000..be11203f --- /dev/null +++ b/lib/pages/device_managment/garage_door/widgets/schedule_garage_view.dart @@ -0,0 +1,84 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.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/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_selector.dart'; + +class BuildGarageDoorScheduleView extends StatefulWidget { + const BuildGarageDoorScheduleView({super.key, required this.status}); + + final GarageDoorStatusModel 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 GarageDoorLoadedState) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const ScheduleGarageHeader(), + const SizedBox(height: 20), + ScheduleGarageDoorModeSelector(state: state), + const SizedBox(height: 20), + ScheduleGarageManagementUI( + state: state, + onAddSchedule: () { + ScheduleGarageDoorDialogHelper.showAddGarageDoorScheduleDialog(context, + schedule: null, index: null, isEdit: false); + }, + ), + ], + ); + } + if (state is GarageDoorLoadingState) { + return const SizedBox( + height: 200, + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + ScheduleGarageHeader(), + SizedBox( + height: 20, + ), + Center(child: CircularProgressIndicator()), + ], + )); + } + return const SizedBox( + height: 200, + child: ScheduleGarageHeader(), + ); + }, + ), + ), + ), + ), + ), + ); + } +} diff --git a/lib/pages/device_managment/water_heater/widgets/schedule_row_widget.dart b/lib/pages/device_managment/water_heater/widgets/schedule_row_widget.dart deleted file mode 100644 index ee0805d6..00000000 --- a/lib/pages/device_managment/water_heater/widgets/schedule_row_widget.dart +++ /dev/null @@ -1,76 +0,0 @@ -// import 'package:flutter/material.dart'; -// import 'package:syncrow_web/pages/device_managment/water_heater/models/schedule_model.dart'; -// import 'package:syncrow_web/utils/format_date_time.dart'; -// import 'package:syncrow_web/utils/color_manager.dart'; - -// class ScheduleRowWidget extends StatelessWidget { -// final ScheduleModel schedule; -// final int index; -// final Function onEdit; -// final Function onDelete; - -// const ScheduleRowWidget({ -// super.key, -// required this.schedule, -// required this.index, -// required this.onEdit, -// required this.onDelete, -// }); - -// @override -// Widget build(BuildContext context) { -// return Table( -// border: TableBorder.all(color: ColorsManager.graysColor), -// defaultVerticalAlignment: TableCellVerticalAlignment.middle, -// children: [ -// TableRow( -// children: [ -// Center( -// child: schedule.enable -// ? const Icon(Icons.radio_button_checked, -// color: ColorsManager.blueColor) -// : const Icon(Icons.radio_button_unchecked), -// ), -// Center(child: Text(_getSelectedDays(schedule.selectedDays ?? []))), -// Center(child: Text(formatIsoStringToTime(schedule.time, context))), -// Center(child: Text(schedule.enable ? 'On' : 'Off')), -// Center( -// child: Wrap( -// runAlignment: WrapAlignment.center, -// children: [ -// TextButton( -// style: TextButton.styleFrom(padding: EdgeInsets.zero), -// onPressed: () => onEdit(), -// child: const Text( -// 'Edit', -// style: TextStyle(color: ColorsManager.blueColor), -// ), -// ), -// TextButton( -// style: TextButton.styleFrom(padding: EdgeInsets.zero), -// onPressed: () => onDelete(), -// child: const Text( -// 'Delete', -// style: TextStyle(color: ColorsManager.blueColor), -// ), -// ), -// ], -// ), -// ), -// ], -// ), -// ], -// ); -// } - -// String _getSelectedDays(List selectedDays) { -// final days = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']; -// List selectedDaysStr = []; -// for (int i = 0; i < selectedDays.length; i++) { -// if (selectedDays[i]) { -// selectedDaysStr.add(days[i]); -// } -// } -// return selectedDaysStr.join(', '); -// } -// }