push garage partial garage door

This commit is contained in:
ashrafzarkanisala
2024-10-06 15:09:36 +03:00
parent b9bc2ec186
commit 1e2e2bf4e6
15 changed files with 1610 additions and 76 deletions

View File

@ -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();
}

View File

@ -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<GarageDoorEvent, GarageDoorState> {
final String deviceId;
late GarageDoorStatusModel deviceStatus;
Timer? _timer;
GarageDoorBloc({required this.deviceId}) : super(GarageDoorInitialState()) {
on<GarageDoorInitialEvent>(_fetchGarageDoorStatus);
on<ToggleGarageDoorEvent>(_toggleGarageDoor);
on<AddGarageDoorScheduleEvent>(_addSchedule);
on<UpdateGarageDoorScheduleEvent>(_updateSchedule);
on<DeleteGarageDoorScheduleEvent>(_deleteSchedule);
on<FetchGarageDoorSchedulesEvent>(_fetchSchedules);
on<IncreaseGarageDoorDelayEvent>(_increaseDelay);
on<DecreaseGarageDoorDelayEvent>(_decreaseDelay);
on<FetchGarageDoorRecordsEvent>(_fetchRecords);
on<GarageDoorUpdatedEvent>(_handleUpdate);
on<UpdateSelectedTimeEvent>(_updateSelectedTime);
on<UpdateSelectedDayEvent>(_updateSelectedDay);
on<UpdateFunctionOnEvent>(_updateFunctionOn);
on<InitializeAddScheduleEvent>(_initializeAddSchedule);
}
void _fetchGarageDoorStatus(GarageDoorInitialEvent event, Emitter<GarageDoorState> 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<GarageDoorState> 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<void> _addSchedule(AddGarageDoorScheduleEvent event, Emitter<GarageDoorState> 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<void> _updateSchedule(UpdateGarageDoorScheduleEvent event, Emitter<GarageDoorState> 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<void> _deleteSchedule(DeleteGarageDoorScheduleEvent event, Emitter<GarageDoorState> 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<void> _fetchSchedules(FetchGarageDoorSchedulesEvent event, Emitter<GarageDoorState> emit) async {
emit(GarageDoorLoadingState());
try {
List<ScheduleModel> 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<GarageDoorState> 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<GarageDoorState> 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<void> _updateSelectedTime(UpdateSelectedTimeEvent event, Emitter<GarageDoorState> emit) async {
final currentState = state;
if (currentState is GarageDoorLoadedState) {
emit(currentState.copyWith(selectedTime: event.selectedTime));
}
}
Future<void> _updateSelectedDay(UpdateSelectedDayEvent event, Emitter<GarageDoorState> emit) async {
final currentState = state;
if (currentState is GarageDoorLoadedState) {
List<bool> updatedDays = List.from(currentState.selectedDays);
updatedDays[event.dayIndex] = event.isSelected;
emit(currentState.copyWith(selectedDays: updatedDays));
}
}
Future<void> _updateFunctionOn(UpdateFunctionOnEvent event, Emitter<GarageDoorState> emit) async {
final currentState = state;
if (currentState is GarageDoorLoadedState) {
emit(currentState.copyWith(functionOn: event.functionOn));
}
}
Future<void> _initializeAddSchedule(InitializeAddScheduleEvent event, Emitter<GarageDoorState> 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<void> _fetchRecords(FetchGarageDoorRecordsEvent event, Emitter<GarageDoorState> 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<GarageDoorState> emit) {
emit(GarageDoorLoadedState(status: deviceStatus));
}
Future<void> _runDeBouncer({
required dynamic deviceId,
required String code,
required dynamic value,
required Emitter<GarageDoorState> 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));
}
});
}
}

View File

@ -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<Object?> get props => [];
}
class GarageDoorInitialEvent extends GarageDoorEvent {
final String deviceId;
const GarageDoorInitialEvent(this.deviceId);
@override
List<Object?> 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<Object?> get props => [deviceId, isOpen];
}
class AddGarageDoorScheduleEvent extends GarageDoorEvent {
final String category;
final TimeOfDay time;
final bool functionOn;
final List<bool> 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<Object?> get props => [deviceId];
}
class DecreaseGarageDoorDelayEvent extends GarageDoorEvent {
final String deviceId;
const DecreaseGarageDoorDelayEvent({required this.deviceId});
@override
List<Object?> get props => [deviceId];
}
class FetchGarageDoorRecordsEvent extends GarageDoorEvent {
final String deviceId;
const FetchGarageDoorRecordsEvent({required this.deviceId});
@override
List<Object?> get props => [deviceId];
}
class GarageDoorUpdatedEvent extends GarageDoorEvent {}
class UpdateSelectedTimeEvent extends GarageDoorEvent {
final TimeOfDay? selectedTime;
const UpdateSelectedTimeEvent(this.selectedTime);
@override
List<Object?> get props => [selectedTime];
}
class UpdateSelectedDayEvent extends GarageDoorEvent {
final int dayIndex;
final bool isSelected;
const UpdateSelectedDayEvent(this.dayIndex, this.isSelected);
@override
List<Object?> get props => [dayIndex, isSelected];
}
class UpdateFunctionOnEvent extends GarageDoorEvent {
final bool functionOn;
const UpdateFunctionOnEvent({required this.functionOn});
@override
List<Object?> get props => [functionOn];
}
class InitializeAddScheduleEvent extends GarageDoorEvent {
final TimeOfDay? selectedTime;
final List<bool> 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<Object?> get props => [
selectedTime,
selectedDays,
functionOn,
isEditing,
index,
];
}

View File

@ -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<Object?> get props => [];
}
class GarageDoorInitialState extends GarageDoorState {}
class GarageDoorLoadingState extends GarageDoorState {}
class GarageDoorLoadedState extends GarageDoorState {
final GarageDoorStatusModel status;
final List<ScheduleModel>? schedules;
final Duration? delay;
final DeviceReport? records;
final List<bool> 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<Object?> get props => [
status,
schedules,
delay,
records,
selectedDays,
selectedTime,
functionOn,
isEditing,
scheduleMode,
];
GarageDoorLoadedState copyWith({
GarageDoorStatusModel? status,
List<ScheduleModel>? schedules,
Duration? delay,
DeviceReport? records,
List<bool>? 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<Object?> get props => [message];
}
class GarageDoorLoadingNewState extends GarageDoorState {
final GarageDoorStatusModel garageDoorModel;
const GarageDoorLoadingNewState({required this.garageDoorModel});
@override
List<Object?> get props => [garageDoorModel];
}
class GarageDoorReportsLoadingState extends GarageDoorState {}
class GarageDoorReportsFailedState extends GarageDoorState {
final String error;
const GarageDoorReportsFailedState({required this.error});
@override
List<Object?> get props => [error];
}
class GarageDoorReportsState extends GarageDoorState {
final DeviceReport deviceReport;
const GarageDoorReportsState({required this.deviceReport});
@override
List<Object?> get props => [deviceReport];
}
class ShowGarageDoorDescriptionState extends GarageDoorState {
final String description;
const ShowGarageDoorDescriptionState({required this.description});
@override
List<Object?> get props => [description];
}
class ScheduleGarageLoadingState extends GarageDoorState {}

View File

@ -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<GarageDoorBloc>();
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<GarageDoorBloc, GarageDoorState>(
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<bool> _convertDaysStringToBooleans(List<String> selectedDays) {
final daysOfWeek = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'];
List<bool> 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<bool> 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<GarageDoorBloc>().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<bool>(
value: true,
groupValue: isOn,
onChanged: (bool? value) {
if (isEdit == true) {
return;
} else {
context.read<GarageDoorBloc>().add(const UpdateFunctionOnEvent(functionOn: true));
}
},
),
const Text('On'),
const SizedBox(width: 10),
Radio<bool>(
value: false,
groupValue: isOn,
onChanged: (bool? value) {
if (isEdit == true) {
return;
} else {
context.read<GarageDoorBloc>().add(const UpdateFunctionOnEvent(functionOn: false));
}
},
),
const Text('Off'),
],
);
}
}

View File

@ -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<ScheduleModel> 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<Status> 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<ScheduleModel> 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<ScheduleModel>? 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)';
}
}

View File

@ -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<GarageDoorBloc, GarageDoorState>(
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<GarageDoorBloc>().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<GarageDoorBloc>(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<GarageDoorBloc>().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<GarageDoorBloc>().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) {},
),
],
);
}
}

View File

@ -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<GarageDoorBloc, GarageDoorState>(
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<GarageDoorBloc>().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<GarageDoorBloc>().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<bool> selectedDays) {
final days = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'];
List<String> selectedDaysStr = [];
for (int i = 0; i < selectedDays.length; i++) {
if (selectedDays[i]) {
selectedDaysStr.add(days[i]);
}
}
return selectedDaysStr.join(', ');
}
}

View File

@ -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();
},
),
),
],
);
}
}

View File

@ -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),
],
);
}
}

View File

@ -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'),
),
),
],
);
}
}

View File

@ -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<ScheduleModes>(
value: mode,
groupValue: state.scheduleMode,
onChanged: (ScheduleModes? value) {
if (value != null) {
if (value == ScheduleModes.schedule) {
context.read<GarageDoorBloc>().add(
FetchGarageDoorSchedulesEvent(
category: 'switch_1',
deviceId: state.status.uuid,
),
);
}
}
},
),
),
);
}
}

View File

@ -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<BuildGarageDoorScheduleView> createState() => _BuildScheduleViewState();
}
class _BuildScheduleViewState extends State<BuildGarageDoorScheduleView> {
@override
Widget build(BuildContext context) {
final bloc = BlocProvider.of<GarageDoorBloc>(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<GarageDoorBloc, GarageDoorState>(
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(),
);
},
),
),
),
),
),
);
}
}

View File

@ -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<bool> selectedDays) {
// final days = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'];
// List<String> selectedDaysStr = [];
// for (int i = 0; i < selectedDays.length; i++) {
// if (selectedDays[i]) {
// selectedDaysStr.add(days[i]);
// }
// }
// return selectedDaysStr.join(', ');
// }
// }