connecting apis and changes the table design to match figma

This commit is contained in:
ashrafzarkanisala
2024-09-22 14:17:52 +03:00
parent 66f45721e5
commit f99744338c
11 changed files with 674 additions and 304 deletions
@@ -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'),
],
);
}
+59 -1
View File
@@ -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;
}
}
}
+6
View File
@@ -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}';
}
+3
View File
@@ -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';
}
+15
View File
@@ -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();
}