mirror of
https://github.com/SyncrowIOT/web.git
synced 2025-07-10 15:17:31 +00:00
push garage partial garage door
This commit is contained in:
@ -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/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_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/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_batch_control.dart';
|
||||||
import 'package:syncrow_web/pages/device_managment/gateway/view/gateway_view.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';
|
import 'package:syncrow_web/pages/device_managment/main_door_sensor/view/main_door_control_view.dart';
|
||||||
@ -82,6 +83,10 @@ mixin RouteControlsBasedCode {
|
|||||||
);
|
);
|
||||||
case 'DS':
|
case 'DS':
|
||||||
return MainDoorSensorControlView(device: device);
|
return MainDoorSensorControlView(device: device);
|
||||||
|
case 'GD':
|
||||||
|
return GarageDoorControlView(
|
||||||
|
deviceId: device.uuid!,
|
||||||
|
);
|
||||||
default:
|
default:
|
||||||
return const SizedBox();
|
return const SizedBox();
|
||||||
}
|
}
|
||||||
|
@ -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));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
@ -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,
|
||||||
|
];
|
||||||
|
}
|
@ -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 {}
|
@ -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'),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -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)';
|
||||||
|
}
|
||||||
|
}
|
@ -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) {},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -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(', ');
|
||||||
|
}
|
||||||
|
}
|
@ -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();
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -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),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -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'),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -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,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -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(),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -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(', ');
|
|
||||||
// }
|
|
||||||
// }
|
|
Reference in New Issue
Block a user