diff --git a/lib/pages/device_managment/water_heater/bloc/water_heater_bloc.dart b/lib/pages/device_managment/water_heater/bloc/water_heater_bloc.dart index 2e84c011..daca7632 100644 --- a/lib/pages/device_managment/water_heater/bloc/water_heater_bloc.dart +++ b/lib/pages/device_managment/water_heater/bloc/water_heater_bloc.dart @@ -5,6 +5,7 @@ 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/devices_mang_api.dart'; @@ -487,10 +488,8 @@ class WaterHeaterBloc extends Bloc { emit(ScheduleLoadingState()); try { - // List schedules = await DevicesManagementApi() - // .getDeviceSchedules(deviceStatus.uuid, event.category); - - List schedules = const []; + List schedules = await DevicesManagementApi() + .getDeviceSchedules(deviceStatus.uuid, event.category); emit(WaterHeaterDeviceStatusLoaded( deviceStatus, @@ -513,7 +512,7 @@ class WaterHeaterBloc extends Bloc { if (state is WaterHeaterDeviceStatusLoaded) { final currentState = state as WaterHeaterDeviceStatusLoaded; - ScheduleModel newSchedule = ScheduleModel( + ScheduleEntry newSchedule = ScheduleEntry( category: event.category, time: formatTimeOfDayToISO(event.time), function: Status(code: 'switch_1', value: event.functionOn), @@ -526,10 +525,7 @@ class WaterHeaterBloc extends Bloc { .addScheduleRecord(newSchedule, currentState.status.uuid); if (success) { - final updatedSchedules = - List.from(currentState.schedules)..add(newSchedule); - - emit(currentState.copyWith(schedules: updatedSchedules)); + add(GetSchedulesEvent(category: 'switch_1', uuid: deviceStatus.uuid)); } else { emit(currentState); //emit(const WaterHeaterFailedState(error: 'Failed to add schedule.')); diff --git a/lib/pages/device_managment/water_heater/bloc/water_heater_event.dart b/lib/pages/device_managment/water_heater/bloc/water_heater_event.dart index e2e718ae..9291e7c3 100644 --- a/lib/pages/device_managment/water_heater/bloc/water_heater_event.dart +++ b/lib/pages/device_managment/water_heater/bloc/water_heater_event.dart @@ -83,22 +83,21 @@ final class DeleteScheduleEvent extends WaterHeaterEvent { final class UpdateScheduleEntryEvent extends WaterHeaterEvent { final bool functionOn; - final String category; +// final String category; final String deviceId; final int index; final String scheduleId; const UpdateScheduleEntryEvent({ required this.functionOn, - required this.category, + // required this.category, required this.deviceId, required this.scheduleId, required this.index, }); @override - List get props => - [category, functionOn, deviceId, scheduleId, index]; + List get props => [functionOn, deviceId, scheduleId]; } class GetSchedulesEvent extends WaterHeaterEvent { 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 c6cc51df..97340521 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 @@ -143,16 +143,10 @@ class ScheduleDialogHelper { onPressed: () { if (state.selectedTime != null) { if (state.isEditing && index != null) { - bloc.add(UpdateScheduleEntryEvent( - index: index, - deviceId: state.status.uuid, - category: 'kg', - functionOn: state.functionOn, - scheduleId: state.schedules[index].scheduleId, - )); + return; } else { bloc.add(AddScheduleEvent( - category: 'kg', + category: 'switch_1', time: state.selectedTime!, selectedDays: state.selectedDays, functionOn: state.functionOn, @@ -177,8 +171,15 @@ class ScheduleDialogHelper { } static TimeOfDay _convertStringToTimeOfDay(String timeString) { - final DateTime dateTime = DateTime.parse(timeString); - return TimeOfDay(hour: dateTime.hour, minute: dateTime.minute); + 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) { @@ -233,9 +234,9 @@ class ScheduleDialogHelper { value: true, groupValue: isOn, onChanged: (bool? value) { - context - .read() - .add(const UpdateFunctionOnEvent(true)); + // context + // .read() + // .add(const UpdateFunctionOnEvent(true)); }, ), const Text('On'), @@ -244,9 +245,9 @@ class ScheduleDialogHelper { value: false, groupValue: isOn, onChanged: (bool? value) { - context - .read() - .add(const UpdateFunctionOnEvent(false)); + // context + // .read() + // .add(const UpdateFunctionOnEvent(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 8b79f563..ca14bf51 100644 --- a/lib/pages/device_managment/water_heater/models/schedule_entry.dart +++ b/lib/pages/device_managment/water_heater/models/schedule_entry.dart @@ -1,19 +1,80 @@ -// import 'package:flutter/material.dart'; +import 'dart:convert'; -// class ScheduleEntry { -// final List selectedDays; -// final TimeOfDay time; -// final bool functionOn; -// final String category; +import 'package:flutter/foundation.dart'; -// ScheduleEntry({ -// required this.selectedDays, -// required this.time, -// required this.functionOn, -// required this.category, -// }); +import 'package:syncrow_web/pages/device_managment/all_devices/models/device_status.dart'; -// @override -// String toString() => -// 'ScheduleEntry(selectedDays: $selectedDays, time: $time, functionOn: $functionOn)'; -// } \ No newline at end of file +class ScheduleEntry { + final String category; + final String time; + final Status function; + final List days; + + ScheduleEntry({ + required this.category, + required this.time, + required this.function, + required this.days, + }); + + @override + String toString() { + return 'ScheduleEntry(category: $category, time: $time, function: $function, days: $days)'; + } + + ScheduleEntry copyWith({ + String? category, + String? time, + Status? function, + List? days, + }) { + return ScheduleEntry( + category: category ?? this.category, + time: time ?? this.time, + function: function ?? this.function, + days: days ?? this.days, + ); + } + + Map toMap() { + return { + 'category': category, + 'time': time, + 'function': function.toMap(), + 'days': days, + }; + } + + factory ScheduleEntry.fromMap(Map map) { + return ScheduleEntry( + category: map['category'] ?? '', + time: map['time'] ?? '', + function: Status.fromMap(map['function']), + days: List.from(map['days']), + ); + } + + String toJson() => json.encode(toMap()); + + factory ScheduleEntry.fromJson(String source) => + ScheduleEntry.fromMap(json.decode(source)); + + @override + bool operator ==(Object other) { + if (identical(this, other)) return true; + + return other is ScheduleEntry && + other.category == category && + other.time == time && + other.function == function && + listEquals(other.days, days); + } + + @override + int get hashCode { + return category.hashCode ^ + time.hashCode ^ + function.hashCode ^ + days.hashCode; + } +} diff --git a/lib/pages/device_managment/water_heater/models/schedule_model.dart b/lib/pages/device_managment/water_heater/models/schedule_model.dart index 7e9410be..3d05a3e0 100644 --- a/lib/pages/device_managment/water_heater/models/schedule_model.dart +++ b/lib/pages/device_managment/water_heater/models/schedule_model.dart @@ -8,26 +8,31 @@ class ScheduleModel { final String category; final String time; final Status function; - final List days; - final TimeOfDay? timeOfDay; + final List days; final List? selectedDays; + final String timezoneId; + final bool enable; ScheduleModel({ + required this.scheduleId, required this.category, required this.time, required this.function, required this.days, - this.timeOfDay, this.selectedDays, - this.scheduleId = '', + required this.timezoneId, + required this.enable, }); Map toMap() { return { + 'scheduleId': scheduleId, 'category': category, 'time': time, 'function': function.toMap(), 'days': days, + 'timezoneId': timezoneId, + 'enable': enable, }; } @@ -37,11 +42,11 @@ class ScheduleModel { category: map['category'] ?? '', time: map['time'] ?? '', function: Status.fromMap(map['function']), - days: List.from(map['days']), - timeOfDay: - parseTimeOfDay(map['time']), - selectedDays: - parseSelectedDays(map['days']), + days: List.from(map['days'].map((e) => e.toString())), + timezoneId: map['timezoneId'] ?? '', + enable: map['enable'] ?? false, + selectedDays: parseSelectedDays( + List.from(map['days'].map((e) => e.toString()))), ); } @@ -51,22 +56,24 @@ class ScheduleModel { ScheduleModel.fromMap(json.decode(source)); ScheduleModel copyWith({ + String? scheduleId, String? category, String? time, Status? function, List? days, - TimeOfDay? timeOfDay, List? selectedDays, - String? scheduleId, + String? timezoneId, + bool? enable, }) { return ScheduleModel( + scheduleId: scheduleId ?? this.scheduleId, category: category ?? this.category, time: time ?? this.time, function: function ?? this.function, days: days ?? this.days, - timeOfDay: timeOfDay ?? this.timeOfDay, selectedDays: selectedDays ?? this.selectedDays, - scheduleId: scheduleId ?? this.scheduleId, + timezoneId: timezoneId ?? this.timezoneId, + enable: enable ?? this.enable, ); } @@ -97,7 +104,7 @@ class ScheduleModel { @override String toString() { - return 'ScheduleModel(category: $category, time: $time, function: $function, days: $days, timeOfDay: $timeOfDay, selectedDays: $selectedDays)'; + return 'ScheduleModel(category: $category, time: $time, function: $function, days: $days, selectedDays: $selectedDays)'; } @override @@ -109,7 +116,7 @@ class ScheduleModel { other.time == time && other.function == function && listEquals(other.days, days) && - timeOfDay == other.timeOfDay && + // timeOfDay == other.timeOfDay && listEquals(other.selectedDays, selectedDays); } @@ -119,7 +126,7 @@ class ScheduleModel { time.hashCode ^ function.hashCode ^ days.hashCode ^ - timeOfDay.hashCode ^ + // timeOfDay.hashCode ^ selectedDays.hashCode; } } diff --git a/lib/pages/device_managment/water_heater/widgets/schedual_view.dart b/lib/pages/device_managment/water_heater/widgets/schedual_view.dart index 09745eb3..739ae218 100644 --- a/lib/pages/device_managment/water_heater/widgets/schedual_view.dart +++ b/lib/pages/device_managment/water_heater/widgets/schedual_view.dart @@ -53,11 +53,10 @@ class _BuildScheduleViewState extends State { state: state, onAddSchedule: () => ScheduleDialogHelper.showAddScheduleDialog( - context, - schedule: null, - index: null, - isEdit: false - ), + context, + schedule: null, + index: null, + isEdit: false), ), if (state.scheduleMode == ScheduleModes.countdown || state.scheduleMode == ScheduleModes.inching) @@ -86,7 +85,9 @@ class _BuildScheduleViewState extends State { ); } if (state is WaterHeaterLoadingState) { - return const Center(child: CircularProgressIndicator()); + return const SizedBox( + height: 200, + child: Center(child: CircularProgressIndicator())); } return const SizedBox(); }, 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 index 7afd96de..bb9ddc8f 100644 --- a/lib/pages/device_managment/water_heater/widgets/schedule_mode_selector.dart +++ b/lib/pages/device_managment/water_heater/widgets/schedule_mode_selector.dart @@ -72,7 +72,7 @@ class ScheduleModeSelector extends StatelessWidget { if (value == ScheduleModes.schedule) { context.read().add( GetSchedulesEvent( - category: 'kg', + category: 'switch_1', uuid: state.status.uuid, ), ); 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 index 760b86c7..ee0805d6 100644 --- a/lib/pages/device_managment/water_heater/widgets/schedule_row_widget.dart +++ b/lib/pages/device_managment/water_heater/widgets/schedule_row_widget.dart @@ -1,76 +1,76 @@ -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'; +// 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; +// 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, - }); +// 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.function.value - ? 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))), - Center(child: Text(schedule.function.value ? '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), - ), - ), - ], - ), - ), - ], - ), - ], - ); - } +// @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(', '); - } -} +// 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(', '); +// } +// } diff --git a/lib/pages/device_managment/water_heater/widgets/schedule_table.dart b/lib/pages/device_managment/water_heater/widgets/schedule_table.dart index b997c7e0..19ec97a7 100644 --- a/lib/pages/device_managment/water_heater/widgets/schedule_table.dart +++ b/lib/pages/device_managment/water_heater/widgets/schedule_table.dart @@ -10,7 +10,7 @@ import 'package:syncrow_web/utils/format_date_time.dart'; import '../helper/add_schedule_dialog_helper.dart'; -class ScheduleTableWidget extends StatelessWidget { +class ScheduleTableWidget extends StatefulWidget { final WaterHeaterDeviceStatusLoaded state; const ScheduleTableWidget({ @@ -18,6 +18,26 @@ class ScheduleTableWidget extends StatelessWidget { required this.state, }); + @override + State createState() => _ScheduleTableWidgetState(); +} + +class _ScheduleTableWidgetState extends State { + late Map _enabledStates; + + @override + void initState() { + super.initState(); + _initializeEnabledStates(); + } + + void _initializeEnabledStates() { + _enabledStates = { + for (int i = 0; i < widget.state.schedules.length; i++) + i: widget.state.schedules[i].enable + }; + } + @override Widget build(BuildContext context) { return Column( @@ -107,14 +127,17 @@ class ScheduleTableWidget extends StatelessWidget { Widget _buildTableBody( WaterHeaterDeviceStatusLoaded state, BuildContext context) { - return SingleChildScrollView( - child: Table( - 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), - ], + return SizedBox( + height: 200, + child: SingleChildScrollView( + child: Table( + 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), + ], + ), ), ); } @@ -134,19 +157,31 @@ class ScheduleTableWidget extends StatelessWidget { ); } - TableRow _buildScheduleRow( - ScheduleModel schedule, int index, BuildContext context) { + TableRow _buildScheduleRow(ScheduleModel schedule, int index, + BuildContext context, WaterHeaterDeviceStatusLoaded state) { return TableRow( children: [ Center( - child: schedule.function.value - ? const Icon(Icons.radio_button_checked, - color: ColorsManager.blueColor) - : const Icon(Icons.radio_button_unchecked)), + child: Radio( + value: true, + groupValue: _enabledStates[index], + onChanged: (bool? value) { + setState(() { + _enabledStates[index] = value!; + }); + context.read().add(UpdateScheduleEntryEvent( + index: index, + functionOn: value ?? false, + scheduleId: schedule.scheduleId, + deviceId: state.status.uuid, + )); + }, + ), + ), Center( child: Text(_getSelectedDays( ScheduleModel.parseSelectedDays(schedule.days)))), - Center(child: Text(formatIsoStringToTime(schedule.time))), + Center(child: Text(formatIsoStringToTime(schedule.time, context))), Center(child: Text(schedule.function.value ? 'On' : 'Off')), Center( child: Wrap( diff --git a/lib/services/devices_mang_api.dart b/lib/services/devices_mang_api.dart index 5551bfe6..b22eedbb 100644 --- a/lib/services/devices_mang_api.dart +++ b/lib/services/devices_mang_api.dart @@ -3,6 +3,7 @@ import 'package:syncrow_web/pages/device_managment/all_devices/models/device_rep import 'package:syncrow_web/pages/device_managment/all_devices/models/device_status.dart'; import 'package:syncrow_web/pages/device_managment/all_devices/models/devices_model.dart'; import 'package:syncrow_web/pages/device_managment/all_devices/models/factory_reset_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/pages/visitor_password/model/device_model.dart'; import 'package:syncrow_web/services/api/http_service.dart'; @@ -179,7 +180,7 @@ class DevicesManagementApi { } Future addScheduleRecord( - ScheduleModel sendSchedule, String uuid) async { + ScheduleEntry sendSchedule, String uuid) async { try { final response = await HTTPService().post( path: ApiEndpoints.scheduleByDeviceId.replaceAll('{deviceUuid}', uuid), @@ -200,14 +201,14 @@ class DevicesManagementApi { String uuid, String category) async { try { final response = await HTTPService().get( - path: ApiEndpoints.scheduleByDeviceId + path: ApiEndpoints.getScheduleByDeviceId .replaceAll('{deviceUuid}', uuid) .replaceAll('{category}', category), showServerMessage: true, expectedResponseModel: (json) { List schedules = []; - for (var schedule in json['schedules']) { - schedules.add(ScheduleModel.fromJson(schedule)); + for (var schedule in json) { + schedules.add(ScheduleModel.fromMap(schedule)); } return schedules; }, @@ -225,7 +226,7 @@ class DevicesManagementApi { required String scheduleId}) async { try { final response = await HTTPService().put( - path: ApiEndpoints.scheduleByDeviceId + path: ApiEndpoints.updateScheduleByDeviceId .replaceAll('{deviceUuid}', uuid) .replaceAll('{scheduleUuid}', scheduleId), body: { @@ -246,7 +247,7 @@ class DevicesManagementApi { Future deleteScheduleRecord(String uuid, String scheduleId) async { try { final response = await HTTPService().delete( - path: ApiEndpoints.scheduleByDeviceId + path: ApiEndpoints.deleteScheduleByDeviceId .replaceAll('{deviceUuid}', uuid) .replaceAll('{scheduleUuid}', scheduleId), showServerMessage: true, diff --git a/lib/utils/format_date_time.dart b/lib/utils/format_date_time.dart index 470acb97..f26f4b36 100644 --- a/lib/utils/format_date_time.dart +++ b/lib/utils/format_date_time.dart @@ -25,7 +25,14 @@ String formatTimeOfDayToISO(TimeOfDay time, {DateTime? currentDate}) { return dateTime.toUtc().toIso8601String(); } -String formatIsoStringToTime(String isoString) { - final dateTime = DateTime.parse(isoString); - return DateFormat('hh:mm a').format(dateTime); +String formatIsoStringToTime(String isoString, BuildContext context) { + try { + final parts = isoString.split(':'); + final hour = int.parse(parts[0]); + final minute = int.parse(parts[1]); + final timeOfDay = TimeOfDay(hour: hour, minute: minute); + return timeOfDay.format(context); + } catch (e) { + return isoString; + } }