mirror of
https://github.com/SyncrowIOT/web.git
synced 2026-10-23 10:47:56 +00:00
connecting apis and changes the table design to match figma
This commit is contained in:
@@ -5,8 +5,11 @@ import 'package:bloc/bloc.dart';
|
||||
import 'package:equatable/equatable.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/all_devices/models/device_status.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/water_heater/models/schedule_entry.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/water_heater/models/send_schedule.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/water_heater/models/water_heater_status_model.dart';
|
||||
import 'package:syncrow_web/services/devices_mang_api.dart';
|
||||
import 'package:syncrow_web/utils/format_date_time.dart';
|
||||
|
||||
part 'water_heater_event.dart';
|
||||
part 'water_heater_state.dart';
|
||||
@@ -21,11 +24,63 @@ class WaterHeaterBloc extends Bloc<WaterHeaterEvent, WaterHeaterState> {
|
||||
on<AddScheduleEvent>(_onAddSchedule);
|
||||
on<DeleteScheduleEvent>(_onDeleteSchedule);
|
||||
on<UpdateScheduleEntryEvent>(_onUpdateSchedule);
|
||||
on<InitializeAddScheduleEvent>(_initializeAddSchedule);
|
||||
on<UpdateSelectedTimeEvent>(_updateSelectedTime);
|
||||
on<UpdateSelectedDayEvent>(_updateSelectedDay);
|
||||
on<UpdateFunctionOnEvent>(_updateFunctionOn);
|
||||
}
|
||||
|
||||
late WaterHeaterStatusModel deviceStatus;
|
||||
Timer? _countdownTimer;
|
||||
|
||||
FutureOr<void> _initializeAddSchedule(
|
||||
InitializeAddScheduleEvent event,
|
||||
Emitter<WaterHeaterState> emit,
|
||||
) {
|
||||
final currentState = state as WaterHeaterDeviceStatusLoaded;
|
||||
if (event.isEditing) {
|
||||
emit(currentState.copyWith(
|
||||
selectedTime: event.selectedTime,
|
||||
selectedDays: event.selectedDays ?? List.filled(7, false),
|
||||
functionOn: event.functionOn ?? false,
|
||||
isEditing: event.isEditing,
|
||||
));
|
||||
} else {
|
||||
emit(currentState.copyWith(
|
||||
selectedTime: null,
|
||||
selectedDays: List.filled(7, false),
|
||||
functionOn: false,
|
||||
isEditing: false,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
FutureOr<void> _updateSelectedTime(
|
||||
UpdateSelectedTimeEvent event,
|
||||
Emitter<WaterHeaterState> emit,
|
||||
) {
|
||||
final currentState = state as WaterHeaterDeviceStatusLoaded;
|
||||
emit(currentState.copyWith(selectedTime: event.selectedTime));
|
||||
}
|
||||
|
||||
FutureOr<void> _updateSelectedDay(
|
||||
UpdateSelectedDayEvent event,
|
||||
Emitter<WaterHeaterState> emit,
|
||||
) {
|
||||
final currentState = state as WaterHeaterDeviceStatusLoaded;
|
||||
final updatedDays = List<bool>.from(currentState.selectedDays);
|
||||
updatedDays[event.index] = event.value;
|
||||
emit(currentState.copyWith(selectedDays: updatedDays));
|
||||
}
|
||||
|
||||
FutureOr<void> _updateFunctionOn(
|
||||
UpdateFunctionOnEvent event,
|
||||
Emitter<WaterHeaterState> emit,
|
||||
) {
|
||||
final currentState = state as WaterHeaterDeviceStatusLoaded;
|
||||
emit(currentState.copyWith(functionOn: event.isOn));
|
||||
}
|
||||
|
||||
FutureOr<void> _updateScheduleEvent(
|
||||
UpdateScheduleEvent event,
|
||||
Emitter<WaterHeaterState> emit,
|
||||
@@ -301,21 +356,50 @@ class WaterHeaterBloc extends Bloc<WaterHeaterEvent, WaterHeaterState> {
|
||||
FutureOr<void> _onAddSchedule(
|
||||
AddScheduleEvent event,
|
||||
Emitter<WaterHeaterState> emit,
|
||||
) {
|
||||
) async {
|
||||
if (state is WaterHeaterDeviceStatusLoaded) {
|
||||
final currentState = state as WaterHeaterDeviceStatusLoaded;
|
||||
final newSchedule = ScheduleEntry(
|
||||
selectedDays: event.selectedDays,
|
||||
time: event.time,
|
||||
functionOn: event.functionOn,
|
||||
);
|
||||
final updatedSchedules = List<ScheduleEntry>.from(currentState.schedules)
|
||||
..add(newSchedule);
|
||||
|
||||
emit(currentState.copyWith(schedules: updatedSchedules));
|
||||
SendSchedule sendSchedule = SendSchedule(
|
||||
category: event.category,
|
||||
time: formatTimeOfDayToISO(event.time),
|
||||
function: Status(code: 'switch_1', value: event.functionOn),
|
||||
days: _getSelectedDaysString(event.selectedDays),
|
||||
);
|
||||
|
||||
bool success = await DevicesManagementApi()
|
||||
.addScheduleRecord(sendSchedule, currentState.status.uuid);
|
||||
|
||||
if (success) {
|
||||
final newSchedule = ScheduleEntry(
|
||||
selectedDays: event.selectedDays,
|
||||
time: event.time,
|
||||
functionOn: event.functionOn,
|
||||
category: event.category,
|
||||
);
|
||||
|
||||
final updatedSchedules =
|
||||
List<ScheduleEntry>.from(currentState.schedules)..add(newSchedule);
|
||||
|
||||
emit(currentState.copyWith(schedules: updatedSchedules));
|
||||
} else {
|
||||
emit(const WaterHeaterFailedState(
|
||||
error: 'Failed to add schedule. Please try again.'));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
List<String> _getSelectedDaysString(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;
|
||||
}
|
||||
|
||||
FutureOr<void> _onDeleteSchedule(
|
||||
DeleteScheduleEvent event,
|
||||
Emitter<WaterHeaterState> emit,
|
||||
@@ -332,17 +416,42 @@ class WaterHeaterBloc extends Bloc<WaterHeaterEvent, WaterHeaterState> {
|
||||
FutureOr<void> _onUpdateSchedule(
|
||||
UpdateScheduleEntryEvent event,
|
||||
Emitter<WaterHeaterState> emit,
|
||||
) {
|
||||
) async {
|
||||
if (state is WaterHeaterDeviceStatusLoaded) {
|
||||
final currentState = state as WaterHeaterDeviceStatusLoaded;
|
||||
final updatedSchedules = List<ScheduleEntry>.from(currentState.schedules);
|
||||
updatedSchedules[event.index] = ScheduleEntry(
|
||||
selectedDays: event.selectedDays,
|
||||
time: event.time,
|
||||
functionOn: event.functionOn,
|
||||
|
||||
// Get the current schedule ID or UUID (assuming it's stored in the schedules)
|
||||
String scheduleId =
|
||||
""; // Retrieve the actual schedule ID based on your model
|
||||
|
||||
// Call the API to update the schedule
|
||||
bool success = await DevicesManagementApi().updateScheduleRecord(
|
||||
enable: event.functionOn,
|
||||
uuid: event.deviceId,
|
||||
scheduleId: scheduleId,
|
||||
);
|
||||
|
||||
emit(currentState.copyWith(schedules: updatedSchedules));
|
||||
if (success) {
|
||||
final updatedSchedules =
|
||||
List<ScheduleEntry>.from(currentState.schedules);
|
||||
|
||||
final updatedScheduleIndex = updatedSchedules.indexWhere((schedule) {
|
||||
return schedule.category == event.category;
|
||||
});
|
||||
|
||||
if (updatedScheduleIndex != -1) {
|
||||
updatedSchedules[updatedScheduleIndex] = ScheduleEntry(
|
||||
category: event.category,
|
||||
selectedDays: updatedSchedules[updatedScheduleIndex].selectedDays,
|
||||
time: updatedSchedules[updatedScheduleIndex].time,
|
||||
functionOn: event.functionOn,
|
||||
);
|
||||
emit(currentState.copyWith(schedules: updatedSchedules));
|
||||
}
|
||||
} else {
|
||||
emit(const WaterHeaterFailedState(
|
||||
error: 'Failed to update schedule. Please try again.'));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -67,15 +67,17 @@ final class AddScheduleEvent extends WaterHeaterEvent {
|
||||
final List<bool> selectedDays;
|
||||
final TimeOfDay time;
|
||||
final bool functionOn;
|
||||
final String category;
|
||||
|
||||
const AddScheduleEvent({
|
||||
required this.selectedDays,
|
||||
required this.time,
|
||||
required this.functionOn,
|
||||
required this.category,
|
||||
});
|
||||
|
||||
@override
|
||||
List<Object?> get props => [selectedDays, time, functionOn];
|
||||
List<Object?> get props => [selectedDays, time, functionOn, category];
|
||||
}
|
||||
|
||||
final class DeleteScheduleEvent extends WaterHeaterEvent {
|
||||
@@ -88,18 +90,60 @@ final class DeleteScheduleEvent extends WaterHeaterEvent {
|
||||
}
|
||||
|
||||
final class UpdateScheduleEntryEvent extends WaterHeaterEvent {
|
||||
final int index;
|
||||
final List<bool> selectedDays;
|
||||
final TimeOfDay time;
|
||||
final bool functionOn;
|
||||
final String category;
|
||||
final String deviceId;
|
||||
|
||||
const UpdateScheduleEntryEvent({
|
||||
required this.index,
|
||||
required this.selectedDays,
|
||||
required this.time,
|
||||
required this.functionOn,
|
||||
required this.category,
|
||||
required this.deviceId,
|
||||
});
|
||||
|
||||
@override
|
||||
List<Object?> get props => [index, selectedDays, time, functionOn];
|
||||
List<Object?> get props => [category, functionOn, deviceId];
|
||||
}
|
||||
|
||||
class InitializeAddScheduleEvent extends WaterHeaterEvent {
|
||||
final TimeOfDay? selectedTime;
|
||||
final List<bool>? selectedDays;
|
||||
final bool? functionOn;
|
||||
final bool isEditing;
|
||||
final int? index;
|
||||
|
||||
const InitializeAddScheduleEvent({
|
||||
this.selectedTime,
|
||||
this.selectedDays,
|
||||
this.functionOn,
|
||||
this.isEditing = false,
|
||||
this.index,
|
||||
});
|
||||
}
|
||||
|
||||
class UpdateSelectedTimeEvent extends WaterHeaterEvent {
|
||||
final TimeOfDay selectedTime;
|
||||
|
||||
const UpdateSelectedTimeEvent(this.selectedTime);
|
||||
|
||||
@override
|
||||
List<Object?> get props => [selectedTime];
|
||||
}
|
||||
|
||||
class UpdateSelectedDayEvent extends WaterHeaterEvent {
|
||||
final int index;
|
||||
final bool value;
|
||||
|
||||
const UpdateSelectedDayEvent(this.index, this.value);
|
||||
|
||||
@override
|
||||
List<Object?> get props => [index, value];
|
||||
}
|
||||
|
||||
class UpdateFunctionOnEvent extends WaterHeaterEvent {
|
||||
final bool isOn;
|
||||
|
||||
const UpdateFunctionOnEvent(this.isOn);
|
||||
|
||||
@override
|
||||
List<Object?> get props => [isOn];
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ final class WaterHeaterInitial extends WaterHeaterState {}
|
||||
|
||||
final class WaterHeaterLoadingState extends WaterHeaterState {}
|
||||
|
||||
final class WaterHeaterDeviceStatusLoaded extends WaterHeaterState {
|
||||
class WaterHeaterDeviceStatusLoaded extends WaterHeaterState {
|
||||
final WaterHeaterStatusModel status;
|
||||
final ScheduleModes? scheduleMode;
|
||||
final int? hours;
|
||||
@@ -21,6 +21,10 @@ final class WaterHeaterDeviceStatusLoaded extends WaterHeaterState {
|
||||
final bool? isActive;
|
||||
final Duration? countdownRemaining;
|
||||
final List<ScheduleEntry> schedules;
|
||||
final List<bool> selectedDays;
|
||||
final TimeOfDay? selectedTime;
|
||||
final bool functionOn;
|
||||
final bool isEditing;
|
||||
|
||||
const WaterHeaterDeviceStatusLoaded(
|
||||
this.status, {
|
||||
@@ -30,6 +34,10 @@ final class WaterHeaterDeviceStatusLoaded extends WaterHeaterState {
|
||||
this.isActive,
|
||||
this.countdownRemaining,
|
||||
this.schedules = const [],
|
||||
this.selectedDays = const [false, false, false, false, false, false, false],
|
||||
this.selectedTime,
|
||||
this.functionOn = false,
|
||||
this.isEditing = false,
|
||||
});
|
||||
|
||||
@override
|
||||
@@ -41,6 +49,10 @@ final class WaterHeaterDeviceStatusLoaded extends WaterHeaterState {
|
||||
isActive,
|
||||
countdownRemaining,
|
||||
schedules,
|
||||
selectedDays,
|
||||
selectedTime,
|
||||
functionOn,
|
||||
isEditing
|
||||
];
|
||||
|
||||
WaterHeaterDeviceStatusLoaded copyWith({
|
||||
@@ -51,6 +63,10 @@ final class WaterHeaterDeviceStatusLoaded extends WaterHeaterState {
|
||||
bool? isActive,
|
||||
Duration? countdownRemaining,
|
||||
List<ScheduleEntry>? schedules,
|
||||
List<bool>? selectedDays,
|
||||
TimeOfDay? selectedTime,
|
||||
bool? functionOn,
|
||||
bool? isEditing,
|
||||
}) {
|
||||
return WaterHeaterDeviceStatusLoaded(
|
||||
status ?? this.status,
|
||||
@@ -60,26 +76,14 @@ final class WaterHeaterDeviceStatusLoaded extends WaterHeaterState {
|
||||
isActive: isActive ?? this.isActive,
|
||||
countdownRemaining: countdownRemaining ?? this.countdownRemaining,
|
||||
schedules: schedules ?? this.schedules,
|
||||
selectedDays: selectedDays ?? this.selectedDays,
|
||||
selectedTime: selectedTime ?? this.selectedTime,
|
||||
functionOn: functionOn ?? this.functionOn,
|
||||
isEditing: isEditing ?? this.isEditing,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class ScheduleEntry {
|
||||
final List<bool> selectedDays;
|
||||
final TimeOfDay time;
|
||||
final bool functionOn;
|
||||
|
||||
ScheduleEntry({
|
||||
required this.selectedDays,
|
||||
required this.time,
|
||||
required this.functionOn,
|
||||
});
|
||||
|
||||
@override
|
||||
String toString() =>
|
||||
'ScheduleEntry(selectedDays: $selectedDays, time: $time, functionOn: $functionOn)';
|
||||
}
|
||||
|
||||
final class WaterHeaterFailedState extends WaterHeaterState {
|
||||
final String error;
|
||||
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class ScheduleEntry {
|
||||
final List<bool> selectedDays;
|
||||
final TimeOfDay time;
|
||||
final bool functionOn;
|
||||
final String category;
|
||||
|
||||
ScheduleEntry({
|
||||
required this.selectedDays,
|
||||
required this.time,
|
||||
required this.functionOn,
|
||||
required this.category,
|
||||
});
|
||||
|
||||
@override
|
||||
String toString() =>
|
||||
'ScheduleEntry(selectedDays: $selectedDays, time: $time, functionOn: $functionOn)';
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/all_devices/models/device_status.dart';
|
||||
|
||||
/*
|
||||
{
|
||||
"category": "kg",
|
||||
"time": "2024-09-22T10:31:54Z",
|
||||
"function": {
|
||||
"code": "switch_1",
|
||||
"value": true
|
||||
},
|
||||
"days": [
|
||||
"Sun"
|
||||
]
|
||||
}
|
||||
*/
|
||||
|
||||
class SendSchedule {
|
||||
final String category;
|
||||
final String time;
|
||||
final Status function;
|
||||
final List<String> days;
|
||||
|
||||
SendSchedule({
|
||||
required this.category,
|
||||
required this.time,
|
||||
required this.function,
|
||||
required this.days,
|
||||
});
|
||||
|
||||
SendSchedule copyWith({
|
||||
String? category,
|
||||
String? time,
|
||||
Status? function,
|
||||
List<String>? days,
|
||||
}) {
|
||||
return SendSchedule(
|
||||
category: category ?? this.category,
|
||||
time: time ?? this.time,
|
||||
function: function ?? this.function,
|
||||
days: days ?? this.days,
|
||||
);
|
||||
}
|
||||
|
||||
Map<String, dynamic> toMap() {
|
||||
return {
|
||||
'category': category,
|
||||
'time': time,
|
||||
'function': function.toMap(),
|
||||
'days': days,
|
||||
};
|
||||
}
|
||||
|
||||
factory SendSchedule.fromMap(Map<String, dynamic> map) {
|
||||
return SendSchedule(
|
||||
category: map['category'] ?? '',
|
||||
time: map['time'] ?? '',
|
||||
function: Status.fromMap(map['function']),
|
||||
days: List<String>.from(map['days']),
|
||||
);
|
||||
}
|
||||
|
||||
String toJson() => json.encode(toMap());
|
||||
|
||||
factory SendSchedule.fromJson(String source) =>
|
||||
SendSchedule.fromMap(json.decode(source));
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'SendSchedule(category: $category, time: $time, function: $function, days: $days)';
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
if (identical(this, other)) return true;
|
||||
|
||||
return other is SendSchedule &&
|
||||
other.category == category &&
|
||||
other.time == time &&
|
||||
other.function == function &&
|
||||
listEquals(other.days, days);
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode {
|
||||
return category.hashCode ^
|
||||
time.hashCode ^
|
||||
function.hashCode ^
|
||||
days.hashCode;
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,12 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:flutter_svg/flutter_svg.dart';
|
||||
import 'package:syncrow_web/pages/common/buttons/default_button.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/water_heater/bloc/water_heater_bloc.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/water_heater/models/schedule_entry.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/constants/assets.dart';
|
||||
import 'package:syncrow_web/utils/extension/build_context_x.dart';
|
||||
|
||||
class BuildScheduleView extends StatefulWidget {
|
||||
@@ -171,7 +174,8 @@ class _BuildScheduleViewState extends State<BuildScheduleView> {
|
||||
padding: 2,
|
||||
backgroundColor: ColorsManager.graysColor,
|
||||
borderRadius: 15,
|
||||
onPressed: () => _showAddScheduleDialog(context),
|
||||
onPressed: () =>
|
||||
_showAddScheduleDialog(context, schedule: null, index: null),
|
||||
child: Row(
|
||||
children: [
|
||||
const Icon(Icons.add, color: ColorsManager.primaryColor),
|
||||
@@ -192,52 +196,92 @@ class _BuildScheduleViewState extends State<BuildScheduleView> {
|
||||
}
|
||||
|
||||
Widget _buildScheduleTable(WaterHeaterDeviceStatusLoaded state) {
|
||||
return Table(
|
||||
border: TableBorder.all(color: Colors.grey),
|
||||
return Column(
|
||||
children: [
|
||||
TableRow(
|
||||
Table(
|
||||
border: TableBorder.all(
|
||||
color: ColorsManager.graysColor,
|
||||
borderRadius: const BorderRadius.only(
|
||||
topLeft: Radius.circular(20), topRight: Radius.circular(20)),
|
||||
),
|
||||
children: [
|
||||
_buildTableHeader('Active'),
|
||||
_buildTableHeader('Days'),
|
||||
_buildTableHeader('Time'),
|
||||
_buildTableHeader('Function'),
|
||||
_buildTableHeader('Action'),
|
||||
],
|
||||
),
|
||||
if (state.schedules.isEmpty)
|
||||
TableRow(
|
||||
children: [
|
||||
Center(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: Text(
|
||||
'No schedules added yet',
|
||||
style: context.textTheme.bodySmall!.copyWith(
|
||||
fontSize: 13,
|
||||
color: ColorsManager.grayColor,
|
||||
),
|
||||
),
|
||||
TableRow(
|
||||
decoration: const BoxDecoration(
|
||||
color: ColorsManager.boxColor,
|
||||
borderRadius: BorderRadius.only(
|
||||
topLeft: Radius.circular(20),
|
||||
topRight: Radius.circular(20),
|
||||
),
|
||||
),
|
||||
const SizedBox(),
|
||||
const SizedBox(),
|
||||
const SizedBox(),
|
||||
const SizedBox(),
|
||||
],
|
||||
children: [
|
||||
_buildTableHeader('Active'),
|
||||
_buildTableHeader('Days'),
|
||||
_buildTableHeader('Time'),
|
||||
_buildTableHeader('Function'),
|
||||
_buildTableHeader('Action'),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
Container(
|
||||
height: 200,
|
||||
decoration: BoxDecoration(
|
||||
border: Border.all(color: ColorsManager.graysColor),
|
||||
borderRadius: const BorderRadius.only(
|
||||
bottomLeft: Radius.circular(20),
|
||||
bottomRight: Radius.circular(20)),
|
||||
),
|
||||
for (int i = 0; i < state.schedules.length; i++)
|
||||
_buildScheduleRow(state.schedules[i], i, context),
|
||||
child: state.schedules.isEmpty
|
||||
? _buildEmptyState(context)
|
||||
: _buildTableBody(state),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildEmptyState(BuildContext context) {
|
||||
return 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(WaterHeaterDeviceStatusLoaded state) {
|
||||
return SingleChildScrollView(
|
||||
child: Table(
|
||||
border: TableBorder.all(color: ColorsManager.graysColor),
|
||||
defaultVerticalAlignment: TableCellVerticalAlignment.middle,
|
||||
children: [
|
||||
for (int i = 0; i < state.schedules.length; i++)
|
||||
_buildScheduleRow(state.schedules[i], i, context),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
TableRow _buildScheduleRow(
|
||||
ScheduleEntry schedule, int index, BuildContext context) {
|
||||
return TableRow(
|
||||
children: [
|
||||
Center(
|
||||
child: schedule.functionOn
|
||||
? const Icon(Icons.radio_button_checked)
|
||||
? const Icon(Icons.radio_button_checked,
|
||||
color: ColorsManager.blueColor)
|
||||
: const Icon(Icons.radio_button_unchecked)),
|
||||
Center(child: Text(_getSelectedDays(schedule.selectedDays))),
|
||||
Center(child: Text(schedule.time.format(context))),
|
||||
@@ -247,9 +291,10 @@ class _BuildScheduleViewState extends State<BuildScheduleView> {
|
||||
runAlignment: WrapAlignment.center,
|
||||
children: [
|
||||
TextButton(
|
||||
style: TextButton.styleFrom(padding: EdgeInsets.zero),
|
||||
onPressed: () {
|
||||
_showAddScheduleDialog(context);
|
||||
// _showEditScheduleDialog(context, schedule, index);
|
||||
_showAddScheduleDialog(context,
|
||||
schedule: schedule, index: index);
|
||||
},
|
||||
child: Text(
|
||||
'Edit',
|
||||
@@ -258,6 +303,7 @@ class _BuildScheduleViewState extends State<BuildScheduleView> {
|
||||
),
|
||||
),
|
||||
TextButton(
|
||||
style: TextButton.styleFrom(padding: EdgeInsets.zero),
|
||||
onPressed: () {
|
||||
context
|
||||
.read<WaterHeaterBloc>()
|
||||
@@ -287,245 +333,165 @@ class _BuildScheduleViewState extends State<BuildScheduleView> {
|
||||
return selectedDaysStr.join(', ');
|
||||
}
|
||||
|
||||
void _showEditScheduleDialog(
|
||||
BuildContext context, ScheduleEntry schedule, int index) {
|
||||
List<bool> selectedDays = List.from(schedule.selectedDays);
|
||||
TimeOfDay selectedTime = schedule.time;
|
||||
bool isOn = schedule.functionOn;
|
||||
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (ctx) {
|
||||
return StatefulBuilder(
|
||||
builder: (context, setState) {
|
||||
return AlertDialog(
|
||||
title: const Text('Edit Schedule'),
|
||||
content: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
ElevatedButton(
|
||||
onPressed: () async {
|
||||
TimeOfDay? time = await showTimePicker(
|
||||
context: context,
|
||||
initialTime: selectedTime,
|
||||
);
|
||||
if (time != null) {
|
||||
setState(() {
|
||||
selectedTime = time;
|
||||
});
|
||||
}
|
||||
},
|
||||
child: Text(selectedTime.format(context)),
|
||||
),
|
||||
],
|
||||
),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () {
|
||||
Navigator.pop(context);
|
||||
},
|
||||
child: const Text('Cancel'),
|
||||
),
|
||||
TextButton(
|
||||
onPressed: () {
|
||||
context.read<WaterHeaterBloc>().add(
|
||||
UpdateScheduleEntryEvent(
|
||||
index: index,
|
||||
selectedDays: selectedDays,
|
||||
time: selectedTime,
|
||||
functionOn: isOn,
|
||||
),
|
||||
);
|
||||
Navigator.pop(context);
|
||||
},
|
||||
child: const Text('Save'),
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildTableHeader(String label) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: Text(
|
||||
label,
|
||||
style: const TextStyle(fontWeight: FontWeight.bold),
|
||||
return TableCell(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(12),
|
||||
child: Text(
|
||||
label,
|
||||
style: context.textTheme.bodySmall!.copyWith(
|
||||
fontSize: 13,
|
||||
color: ColorsManager.grayColor,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void _showAddScheduleDialog(
|
||||
BuildContext context) {
|
||||
void _showAddScheduleDialog(BuildContext context,
|
||||
{ScheduleEntry? schedule, int? index}) {
|
||||
final bloc = context.read<WaterHeaterBloc>();
|
||||
|
||||
if (schedule != null) {
|
||||
bloc.add(InitializeAddScheduleEvent(
|
||||
selectedTime: schedule.time,
|
||||
selectedDays: schedule.selectedDays,
|
||||
functionOn: schedule.functionOn,
|
||||
isEditing: true,
|
||||
index: index,
|
||||
));
|
||||
} else {
|
||||
bloc.add(
|
||||
const InitializeAddScheduleEvent(
|
||||
selectedDays: [false, false, false, false, false, false, false],
|
||||
functionOn: false,
|
||||
isEditing: false,
|
||||
index: null,
|
||||
selectedTime: null,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (ctx) {
|
||||
List<bool> selectedDays = [
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false
|
||||
];
|
||||
|
||||
TimeOfDay? selectedTime;
|
||||
bool isOn = false;
|
||||
return BlocProvider.value(
|
||||
value: BlocProvider.of<WaterHeaterBloc>(context),
|
||||
child: StatefulBuilder(
|
||||
builder: (ctx, setState) {
|
||||
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,
|
||||
value: bloc,
|
||||
child: BlocBuilder<WaterHeaterBloc, WaterHeaterState>(
|
||||
builder: (context, state) {
|
||||
if (state is WaterHeaterDeviceStatusLoaded) {
|
||||
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: () async {
|
||||
TimeOfDay? time = await showTimePicker(
|
||||
context: context,
|
||||
initialTime:
|
||||
state.selectedTime ?? TimeOfDay.now(),
|
||||
);
|
||||
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(),
|
||||
],
|
||||
),
|
||||
const SizedBox(
|
||||
height: 24,
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
_buildDayCheckboxes(context, state.selectedDays),
|
||||
const SizedBox(height: 16),
|
||||
_buildFunctionSwitch(context, state.functionOn),
|
||||
],
|
||||
),
|
||||
actions: [
|
||||
SizedBox(
|
||||
width: 200,
|
||||
child: DefaultButton(
|
||||
height: 40,
|
||||
onPressed: () {
|
||||
Navigator.pop(context);
|
||||
},
|
||||
backgroundColor: ColorsManager.boxColor,
|
||||
child: Text(
|
||||
'Cancel',
|
||||
style: context.textTheme.bodyMedium,
|
||||
),
|
||||
),
|
||||
),
|
||||
SizedBox(
|
||||
width: 150,
|
||||
height: 40,
|
||||
width: 200,
|
||||
child: DefaultButton(
|
||||
padding: 8,
|
||||
backgroundColor: ColorsManager.boxColor,
|
||||
borderRadius: 15,
|
||||
onPressed: () async {
|
||||
TimeOfDay? time = await showTimePicker(
|
||||
context: context,
|
||||
initialTime: TimeOfDay.now(),
|
||||
);
|
||||
if (time != null) {
|
||||
setState(() {
|
||||
selectedTime = time;
|
||||
});
|
||||
height: 40,
|
||||
onPressed: () {
|
||||
if (state.selectedTime != null) {
|
||||
if (state.isEditing && index != null) {
|
||||
bloc.add(UpdateScheduleEntryEvent(
|
||||
deviceId: state.status.uuid,
|
||||
category: 'kg',
|
||||
functionOn: state.functionOn,
|
||||
));
|
||||
} else {
|
||||
bloc.add(AddScheduleEvent(
|
||||
category: 'kg',
|
||||
time: state.selectedTime!,
|
||||
selectedDays: state.selectedDays,
|
||||
functionOn: state.functionOn,
|
||||
));
|
||||
}
|
||||
Navigator.pop(context);
|
||||
}
|
||||
},
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(
|
||||
selectedTime == null
|
||||
? 'Time'
|
||||
: selectedTime!.format(context),
|
||||
style: context.textTheme.bodySmall!.copyWith(
|
||||
color: ColorsManager.grayColor,
|
||||
),
|
||||
),
|
||||
const Icon(
|
||||
Icons.access_time,
|
||||
color: ColorsManager.grayColor,
|
||||
size: 18,
|
||||
),
|
||||
],
|
||||
),
|
||||
backgroundColor: ColorsManager.primaryColor,
|
||||
child: const Text('Save'),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
Row(
|
||||
children: [
|
||||
_buildDayCheckbox(setState, 'Mon.', 0, selectedDays),
|
||||
_buildDayCheckbox(setState, 'Tue.', 1, selectedDays),
|
||||
_buildDayCheckbox(setState, 'Wed.', 2, selectedDays),
|
||||
_buildDayCheckbox(setState, 'Thu.', 3, selectedDays),
|
||||
_buildDayCheckbox(setState, 'Fri.', 4, selectedDays),
|
||||
_buildDayCheckbox(setState, 'Sat.', 5, selectedDays),
|
||||
_buildDayCheckbox(setState, 'Sun.', 6, selectedDays),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
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) {
|
||||
setState(() {
|
||||
isOn = value!;
|
||||
});
|
||||
},
|
||||
),
|
||||
const Text('On'),
|
||||
const SizedBox(
|
||||
width: 10,
|
||||
),
|
||||
Radio<bool>(
|
||||
value: false,
|
||||
groupValue: isOn,
|
||||
onChanged: (bool? value) {
|
||||
setState(() {
|
||||
isOn = value!;
|
||||
});
|
||||
},
|
||||
),
|
||||
const Text('Off'),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
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 (selectedTime != null) {
|
||||
context.read<WaterHeaterBloc>().add(AddScheduleEvent(
|
||||
time: selectedTime!,
|
||||
selectedDays: selectedDays,
|
||||
functionOn: isOn,
|
||||
));
|
||||
Navigator.pop(context);
|
||||
}
|
||||
},
|
||||
backgroundColor: ColorsManager.primaryColor,
|
||||
child: const Text('Save'),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
);
|
||||
}
|
||||
return const SizedBox();
|
||||
},
|
||||
),
|
||||
);
|
||||
@@ -533,19 +499,57 @@ class _BuildScheduleViewState extends State<BuildScheduleView> {
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildDayCheckbox(void Function(void Function()) setState, String day,
|
||||
int index, List<bool> selectedDays) {
|
||||
Widget _buildDayCheckboxes(BuildContext context, List<bool> selectedDays) {
|
||||
return Row(
|
||||
children: List.generate(7, (index) {
|
||||
final dayLabels = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'];
|
||||
return Row(
|
||||
children: [
|
||||
Checkbox(
|
||||
value: selectedDays[index],
|
||||
onChanged: (bool? value) {
|
||||
context
|
||||
.read<WaterHeaterBloc>()
|
||||
.add(UpdateSelectedDayEvent(index, value!));
|
||||
},
|
||||
),
|
||||
Text(dayLabels[index]),
|
||||
],
|
||||
);
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildFunctionSwitch(BuildContext context, bool isOn) {
|
||||
return Row(
|
||||
children: [
|
||||
Checkbox(
|
||||
value: selectedDays[index],
|
||||
Text(
|
||||
'Function:',
|
||||
style: context.textTheme.bodySmall!
|
||||
.copyWith(color: ColorsManager.grayColor),
|
||||
),
|
||||
const SizedBox(width: 10),
|
||||
Radio<bool>(
|
||||
value: true,
|
||||
groupValue: isOn,
|
||||
onChanged: (bool? value) {
|
||||
setState(() {
|
||||
selectedDays[index] = value!;
|
||||
});
|
||||
context
|
||||
.read<WaterHeaterBloc>()
|
||||
.add(const UpdateFunctionOnEvent(true));
|
||||
},
|
||||
),
|
||||
Text(day),
|
||||
const Text('On'),
|
||||
const SizedBox(width: 10),
|
||||
Radio<bool>(
|
||||
value: false,
|
||||
groupValue: isOn,
|
||||
onChanged: (bool? value) {
|
||||
context
|
||||
.read<WaterHeaterBloc>()
|
||||
.add(const UpdateFunctionOnEvent(false));
|
||||
},
|
||||
),
|
||||
const Text('Off'),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ 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/all_devices/models/device_status.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/all_devices/models/devices_model.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/water_heater/models/send_schedule.dart';
|
||||
import 'package:syncrow_web/pages/visitor_password/model/device_model.dart';
|
||||
import 'package:syncrow_web/services/api/http_service.dart';
|
||||
import 'package:syncrow_web/utils/constants/api_const.dart';
|
||||
@@ -79,7 +80,7 @@ class DevicesManagementApi {
|
||||
body: body,
|
||||
showServerMessage: true,
|
||||
expectedResponseModel: (json) {
|
||||
return (json['successResults'] as List).isNotEmpty ;
|
||||
return (json['successResults'] as List).isNotEmpty;
|
||||
},
|
||||
);
|
||||
|
||||
@@ -175,4 +176,61 @@ class DevicesManagementApi {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Future<bool> addScheduleRecord(SendSchedule sendSchedule, String uuid) async {
|
||||
try {
|
||||
final response = await HTTPService().post(
|
||||
path: ApiEndpoints.scheduleByDeviceId.replaceAll('{deviceUuid}', uuid),
|
||||
body: sendSchedule.toMap(),
|
||||
showServerMessage: true,
|
||||
expectedResponseModel: (json) {
|
||||
return json['success'] ?? false;
|
||||
},
|
||||
);
|
||||
return response;
|
||||
} catch (e) {
|
||||
debugPrint('Error fetching $e');
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
Future<bool> updateScheduleRecord(
|
||||
{required bool enable,
|
||||
required String uuid,
|
||||
required String scheduleId}) async {
|
||||
try {
|
||||
final response = await HTTPService().put(
|
||||
path: ApiEndpoints.scheduleByDeviceId
|
||||
.replaceAll('{deviceUuid}', uuid)
|
||||
.replaceAll('{scheduleUuid}', scheduleId),
|
||||
body: {
|
||||
'scheduleId': scheduleId,
|
||||
'enable': enable,
|
||||
},
|
||||
expectedResponseModel: (json) {
|
||||
return json['success'] ?? false;
|
||||
},
|
||||
);
|
||||
return response;
|
||||
} catch (e) {
|
||||
debugPrint('Error fetching $e');
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
Future<bool> deleteScheduleRecord(String uuid) async {
|
||||
try {
|
||||
final response = await HTTPService().delete(
|
||||
path: ApiEndpoints.scheduleByDeviceId.replaceAll('{deviceUuid}', uuid),
|
||||
showServerMessage: true,
|
||||
expectedResponseModel: (json) {
|
||||
return json['success'] ?? false;
|
||||
},
|
||||
);
|
||||
return response;
|
||||
} catch (e) {
|
||||
debugPrint('Error fetching $e');
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,4 +38,10 @@ abstract class ApiEndpoints {
|
||||
static const String getDeviceLogs = '/device/report-logs/{uuid}?code={code}';
|
||||
static const String getDeviceLogsByDate =
|
||||
'/device/report-logs/{uuid}?code={code}&startTime={startTime}&endTime={endTime}';
|
||||
|
||||
static const String scheduleByDeviceId = '/schedule/{deviceUuid}';
|
||||
static const String deleteScheduleByDeviceId =
|
||||
'/schedule/{deviceUuid}/{scheduleUuid}';
|
||||
static const String updateScheduleByDeviceId =
|
||||
'/schedule/enable/{deviceUuid}';
|
||||
}
|
||||
|
||||
@@ -154,4 +154,7 @@ class Assets {
|
||||
static const String mainDoorReports = 'assets/icons/main_door_reports.svg';
|
||||
//assets/icons/main_door.svg
|
||||
static const String mainDoor = 'assets/icons/main_door.svg';
|
||||
|
||||
//assets/icons/empty_records.svg
|
||||
static const String emptyRecords = 'assets/icons/empty_records.svg';
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
|
||||
String formatDateTime(DateTime? dateTime) {
|
||||
@@ -9,3 +10,17 @@ String formatDateTime(DateTime? dateTime) {
|
||||
|
||||
return '${dateFormatter.format(dateTime)} ${timeFormatter.format(dateTime)}';
|
||||
}
|
||||
|
||||
String formatTimeOfDayToISO(TimeOfDay time, {DateTime? currentDate}) {
|
||||
final now = currentDate ?? DateTime.now();
|
||||
|
||||
final dateTime = DateTime(
|
||||
now.year,
|
||||
now.month,
|
||||
now.day,
|
||||
time.hour,
|
||||
time.minute,
|
||||
);
|
||||
|
||||
return dateTime.toUtc().toIso8601String();
|
||||
}
|
||||
Reference in New Issue
Block a user