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}'; }