mirror of
https://github.com/SyncrowIOT/web.git
synced 2025-07-10 15:17:31 +00:00
push updated code after merge
This commit is contained in:
125
lib/pages/routiens/bloc/effective_period/effect_period_bloc.dart
Normal file
125
lib/pages/routiens/bloc/effective_period/effect_period_bloc.dart
Normal file
@ -0,0 +1,125 @@
|
|||||||
|
import 'dart:async';
|
||||||
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
|
import 'package:syncrow_web/pages/routiens/bloc/effective_period/effect_period_event.dart';
|
||||||
|
import 'package:syncrow_web/pages/routiens/bloc/effective_period/effect_period_state.dart';
|
||||||
|
import 'package:syncrow_web/utils/constants/app_enum.dart';
|
||||||
|
|
||||||
|
class EffectPeriodBloc extends Bloc<EffectPeriodEvent, EffectPeriodState> {
|
||||||
|
final daysMap = {
|
||||||
|
'Sun': 'S',
|
||||||
|
'Mon': 'M',
|
||||||
|
'Tue': 'T',
|
||||||
|
'Wed': 'W',
|
||||||
|
'Thu': 'T',
|
||||||
|
'Fri': 'F',
|
||||||
|
'Sat': 'S',
|
||||||
|
};
|
||||||
|
|
||||||
|
EffectPeriodBloc() : super(EffectPeriodState.initial()) {
|
||||||
|
on<SetPeriod>(_onSetPeriod);
|
||||||
|
on<ToggleDay>(_onToggleDay);
|
||||||
|
on<SetCustomTime>(_onSetCustomTime);
|
||||||
|
on<ResetEffectivePeriod>(_onResetEffectivePeriod);
|
||||||
|
on<ResetDays>(_onResetDays);
|
||||||
|
on<SetDays>(_setAllDays);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _onSetPeriod(SetPeriod event, Emitter<EffectPeriodState> emit) {
|
||||||
|
String startTime = '';
|
||||||
|
String endTime = '';
|
||||||
|
|
||||||
|
switch (event.period) {
|
||||||
|
case EnumEffectivePeriodOptions.allDay:
|
||||||
|
startTime = '00:00';
|
||||||
|
endTime = '23:59';
|
||||||
|
break;
|
||||||
|
case EnumEffectivePeriodOptions.daytime:
|
||||||
|
startTime = '06:00';
|
||||||
|
endTime = '18:00';
|
||||||
|
break;
|
||||||
|
case EnumEffectivePeriodOptions.night:
|
||||||
|
startTime = '18:00';
|
||||||
|
endTime = '06:00';
|
||||||
|
break;
|
||||||
|
case EnumEffectivePeriodOptions.custom:
|
||||||
|
startTime = state.customStartTime ?? '00:00';
|
||||||
|
endTime = state.customEndTime ?? '23:59';
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// BlocProvider.of<CreateSceneBloc>(NavigationService.navigatorKey.currentContext!).add(
|
||||||
|
// EffectiveTimePeriodEvent(
|
||||||
|
// EffectiveTime(start: startTime, end: endTime, loops: state.selectedDaysBinary)));
|
||||||
|
|
||||||
|
emit(state.copyWith(
|
||||||
|
selectedPeriod: event.period, customStartTime: startTime, customEndTime: endTime));
|
||||||
|
}
|
||||||
|
|
||||||
|
void _onToggleDay(ToggleDay event, Emitter<EffectPeriodState> emit) {
|
||||||
|
final daysList = state.selectedDaysBinary.split('');
|
||||||
|
final dayIndex = getDayIndex(event.day);
|
||||||
|
if (daysList[dayIndex] == '1') {
|
||||||
|
daysList[dayIndex] = '0';
|
||||||
|
} else {
|
||||||
|
daysList[dayIndex] = '1';
|
||||||
|
}
|
||||||
|
final newDaysBinary = daysList.join();
|
||||||
|
emit(state.copyWith(selectedDaysBinary: newDaysBinary));
|
||||||
|
|
||||||
|
// BlocProvider.of<CreateSceneBloc>(NavigationService.navigatorKey.currentContext!).add(
|
||||||
|
// EffectiveTimePeriodEvent(EffectiveTime(
|
||||||
|
// start: state.customStartTime ?? '00:00',
|
||||||
|
// end: state.customEndTime ?? '23:59',
|
||||||
|
// loops: newDaysBinary)));
|
||||||
|
}
|
||||||
|
|
||||||
|
void _onSetCustomTime(SetCustomTime event, Emitter<EffectPeriodState> emit) {
|
||||||
|
String startTime = event.startTime;
|
||||||
|
String endTime = event.endTime;
|
||||||
|
EnumEffectivePeriodOptions period;
|
||||||
|
|
||||||
|
// Determine the period based on start and end times
|
||||||
|
if (startTime == '00:00' && endTime == '23:59') {
|
||||||
|
period = EnumEffectivePeriodOptions.allDay;
|
||||||
|
} else if (startTime == '06:00' && endTime == '18:00') {
|
||||||
|
period = EnumEffectivePeriodOptions.daytime;
|
||||||
|
} else if (startTime == '18:00' && endTime == '06:00') {
|
||||||
|
period = EnumEffectivePeriodOptions.night;
|
||||||
|
} else {
|
||||||
|
period = EnumEffectivePeriodOptions.custom;
|
||||||
|
}
|
||||||
|
|
||||||
|
emit(
|
||||||
|
state.copyWith(customStartTime: startTime, customEndTime: endTime, selectedPeriod: period));
|
||||||
|
|
||||||
|
// BlocProvider.of<CreateSceneBloc>(NavigationService.navigatorKey.currentContext!).add(
|
||||||
|
// EffectiveTimePeriodEvent(
|
||||||
|
// EffectiveTime(start: startTime, end: endTime, loops: state.selectedDaysBinary)));
|
||||||
|
}
|
||||||
|
|
||||||
|
void _onResetEffectivePeriod(ResetEffectivePeriod event, Emitter<EffectPeriodState> emit) {
|
||||||
|
emit(state.copyWith(
|
||||||
|
selectedPeriod: EnumEffectivePeriodOptions.allDay,
|
||||||
|
customStartTime: '00:00',
|
||||||
|
customEndTime: '23:59',
|
||||||
|
selectedDaysBinary: '1111111'));
|
||||||
|
|
||||||
|
// BlocProvider.of<CreateSceneBloc>(NavigationService.navigatorKey.currentContext!).add(
|
||||||
|
// EffectiveTimePeriodEvent(EffectiveTime(start: '00:00', end: '23:59', loops: '1111111')));
|
||||||
|
}
|
||||||
|
|
||||||
|
void _onResetDays(ResetDays event, Emitter<EffectPeriodState> emit) {
|
||||||
|
emit(state.copyWith(selectedDaysBinary: '1111111'));
|
||||||
|
}
|
||||||
|
|
||||||
|
int getDayIndex(String day) {
|
||||||
|
const days = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'];
|
||||||
|
return days.indexOf(day);
|
||||||
|
}
|
||||||
|
|
||||||
|
FutureOr<void> _setAllDays(SetDays event, Emitter<EffectPeriodState> emit) {
|
||||||
|
emit(state.copyWith(selectedDaysBinary: event.daysBinary));
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,50 @@
|
|||||||
|
import 'package:equatable/equatable.dart';
|
||||||
|
import 'package:syncrow_web/utils/constants/app_enum.dart';
|
||||||
|
|
||||||
|
abstract class EffectPeriodEvent extends Equatable {
|
||||||
|
const EffectPeriodEvent();
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<Object> get props => [];
|
||||||
|
}
|
||||||
|
|
||||||
|
class SetPeriod extends EffectPeriodEvent {
|
||||||
|
final EnumEffectivePeriodOptions period;
|
||||||
|
|
||||||
|
const SetPeriod(this.period);
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<Object> get props => [period];
|
||||||
|
}
|
||||||
|
|
||||||
|
class ToggleDay extends EffectPeriodEvent {
|
||||||
|
final String day;
|
||||||
|
|
||||||
|
const ToggleDay(this.day);
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<Object> get props => [day];
|
||||||
|
}
|
||||||
|
|
||||||
|
class SetCustomTime extends EffectPeriodEvent {
|
||||||
|
final String startTime;
|
||||||
|
final String endTime;
|
||||||
|
|
||||||
|
const SetCustomTime(this.startTime, this.endTime);
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<Object> get props => [startTime, endTime];
|
||||||
|
}
|
||||||
|
|
||||||
|
class ResetEffectivePeriod extends EffectPeriodEvent {}
|
||||||
|
|
||||||
|
class ResetDays extends EffectPeriodEvent {
|
||||||
|
@override
|
||||||
|
List<Object> get props => [];
|
||||||
|
}
|
||||||
|
|
||||||
|
class SetDays extends EffectPeriodEvent {
|
||||||
|
final String daysBinary;
|
||||||
|
|
||||||
|
const SetDays(this.daysBinary);
|
||||||
|
}
|
@ -0,0 +1,54 @@
|
|||||||
|
import 'package:equatable/equatable.dart';
|
||||||
|
import 'package:syncrow_web/utils/constants/app_enum.dart';
|
||||||
|
|
||||||
|
class EffectPeriodState extends Equatable {
|
||||||
|
final EnumEffectivePeriodOptions selectedPeriod;
|
||||||
|
final String selectedDaysBinary;
|
||||||
|
final String? customStartTime;
|
||||||
|
final String? customEndTime;
|
||||||
|
|
||||||
|
const EffectPeriodState({
|
||||||
|
required this.selectedPeriod,
|
||||||
|
required this.selectedDaysBinary,
|
||||||
|
this.customStartTime,
|
||||||
|
this.customEndTime,
|
||||||
|
});
|
||||||
|
|
||||||
|
factory EffectPeriodState.initial() {
|
||||||
|
return const EffectPeriodState(
|
||||||
|
selectedPeriod: EnumEffectivePeriodOptions.allDay,
|
||||||
|
selectedDaysBinary: "1111111", // All days selected
|
||||||
|
customStartTime: "00:00",
|
||||||
|
customEndTime: "23:59",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
EffectPeriodState copyWith({
|
||||||
|
EnumEffectivePeriodOptions? selectedPeriod,
|
||||||
|
String? selectedDaysBinary,
|
||||||
|
String? customStartTime,
|
||||||
|
String? customEndTime,
|
||||||
|
}) {
|
||||||
|
return EffectPeriodState(
|
||||||
|
selectedPeriod: selectedPeriod ?? this.selectedPeriod,
|
||||||
|
selectedDaysBinary: selectedDaysBinary ?? this.selectedDaysBinary,
|
||||||
|
customStartTime: customStartTime ?? this.customStartTime,
|
||||||
|
customEndTime: customEndTime ?? this.customEndTime,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
EnumEffectivePeriodOptions getEffectivePeriod() {
|
||||||
|
if (customStartTime == '00:00' && customEndTime == '23:59') {
|
||||||
|
return EnumEffectivePeriodOptions.allDay;
|
||||||
|
} else if (customStartTime == '06:00' && customEndTime == '18:00') {
|
||||||
|
return EnumEffectivePeriodOptions.daytime;
|
||||||
|
} else if (customStartTime == '18:00' && customEndTime == '06:00') {
|
||||||
|
return EnumEffectivePeriodOptions.night;
|
||||||
|
} else {
|
||||||
|
return EnumEffectivePeriodOptions.custom;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<Object?> get props => [selectedPeriod, selectedDaysBinary, customStartTime, customEndTime];
|
||||||
|
}
|
53
lib/pages/routiens/bloc/setting_bloc/setting_bloc.dart
Normal file
53
lib/pages/routiens/bloc/setting_bloc/setting_bloc.dart
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
|
import 'package:syncrow_web/pages/routiens/bloc/setting_bloc/setting_event.dart';
|
||||||
|
import 'package:syncrow_web/pages/routiens/bloc/setting_bloc/setting_state.dart';
|
||||||
|
import 'package:syncrow_web/pages/routiens/models/icon_model.dart';
|
||||||
|
import 'package:syncrow_web/services/routines_api.dart';
|
||||||
|
|
||||||
|
class SettingBloc extends Bloc<SettingEvent, SettingState> {
|
||||||
|
bool isExpanded = false;
|
||||||
|
String selectedIcon = '';
|
||||||
|
List<IconModel> iconModelList = [];
|
||||||
|
|
||||||
|
SettingBloc() : super(const InitialState()) {
|
||||||
|
on<InitialEvent>(_initialSetting);
|
||||||
|
on<FetchIcons>(_fetchIcons);
|
||||||
|
on<SelectIcon>(_selectIcon);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _initialSetting(InitialEvent event, Emitter<SettingState> emit) async {
|
||||||
|
try {
|
||||||
|
emit(const LoadingState());
|
||||||
|
selectedIcon = event.selectedIcon;
|
||||||
|
emit(TabToRunSettingLoaded(
|
||||||
|
showInDevice: true, selectedIcon: event.selectedIcon, iconList: iconModelList));
|
||||||
|
} catch (e) {
|
||||||
|
emit(const FailedState(error: 'Something went wrong'));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void _fetchIcons(FetchIcons event, Emitter<SettingState> emit) async {
|
||||||
|
try {
|
||||||
|
isExpanded = event.expanded;
|
||||||
|
emit(const LoadingState());
|
||||||
|
if (isExpanded) {
|
||||||
|
iconModelList = await SceneApi.getIcon();
|
||||||
|
emit(TabToRunSettingLoaded(
|
||||||
|
showInDevice: true, selectedIcon: selectedIcon, iconList: iconModelList));
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
emit(const FailedState(error: 'Something went wrong'));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void _selectIcon(SelectIcon event, Emitter<SettingState> emit) async {
|
||||||
|
try {
|
||||||
|
emit(const LoadingState());
|
||||||
|
selectedIcon = event.iconId;
|
||||||
|
emit(TabToRunSettingLoaded(
|
||||||
|
showInDevice: true, selectedIcon: event.iconId, iconList: iconModelList));
|
||||||
|
} catch (e) {
|
||||||
|
emit(const FailedState(error: 'Something went wrong'));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
32
lib/pages/routiens/bloc/setting_bloc/setting_event.dart
Normal file
32
lib/pages/routiens/bloc/setting_bloc/setting_event.dart
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
import 'package:equatable/equatable.dart';
|
||||||
|
|
||||||
|
abstract class SettingEvent extends Equatable {
|
||||||
|
const SettingEvent();
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<Object> get props => [];
|
||||||
|
}
|
||||||
|
|
||||||
|
class InitialEvent extends SettingEvent {
|
||||||
|
final String selectedIcon;
|
||||||
|
const InitialEvent({required this.selectedIcon});
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<Object> get props => [selectedIcon];
|
||||||
|
}
|
||||||
|
|
||||||
|
class FetchIcons extends SettingEvent {
|
||||||
|
final bool expanded;
|
||||||
|
const FetchIcons({required this.expanded});
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<Object> get props => [expanded];
|
||||||
|
}
|
||||||
|
|
||||||
|
class SelectIcon extends SettingEvent {
|
||||||
|
final String iconId;
|
||||||
|
const SelectIcon({required this.iconId});
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<Object> get props => [iconId];
|
||||||
|
}
|
56
lib/pages/routiens/bloc/setting_bloc/setting_state.dart
Normal file
56
lib/pages/routiens/bloc/setting_bloc/setting_state.dart
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
import 'package:equatable/equatable.dart';
|
||||||
|
import 'package:syncrow_web/pages/routiens/models/icon_model.dart';
|
||||||
|
|
||||||
|
abstract class SettingState extends Equatable {
|
||||||
|
const SettingState();
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<Object> get props => [];
|
||||||
|
}
|
||||||
|
|
||||||
|
class LoadingState extends SettingState {
|
||||||
|
const LoadingState();
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<Object> get props => [];
|
||||||
|
}
|
||||||
|
|
||||||
|
class InitialState extends SettingState {
|
||||||
|
const InitialState();
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<Object> get props => [];
|
||||||
|
}
|
||||||
|
|
||||||
|
class IconLoadedState extends SettingState {
|
||||||
|
final List<String> status;
|
||||||
|
|
||||||
|
const IconLoadedState(this.status);
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<Object> get props => [status];
|
||||||
|
}
|
||||||
|
|
||||||
|
class TabToRunSettingLoaded extends SettingState {
|
||||||
|
final String selectedIcon;
|
||||||
|
final List<IconModel> iconList;
|
||||||
|
final bool showInDevice;
|
||||||
|
|
||||||
|
const TabToRunSettingLoaded({
|
||||||
|
required this.selectedIcon,
|
||||||
|
required this.iconList,
|
||||||
|
required this.showInDevice,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<Object> get props => [selectedIcon, iconList, showInDevice];
|
||||||
|
}
|
||||||
|
|
||||||
|
class FailedState extends SettingState {
|
||||||
|
final String error;
|
||||||
|
|
||||||
|
const FailedState({required this.error});
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<Object> get props => [error];
|
||||||
|
}
|
151
lib/pages/routiens/helper/effictive_period_helper.dart
Normal file
151
lib/pages/routiens/helper/effictive_period_helper.dart
Normal file
@ -0,0 +1,151 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
|
import 'package:syncrow_web/pages/routiens/bloc/effective_period/effect_period_bloc.dart';
|
||||||
|
import 'package:syncrow_web/pages/routiens/bloc/effective_period/effect_period_event.dart';
|
||||||
|
import 'package:syncrow_web/utils/color_manager.dart';
|
||||||
|
import 'package:syncrow_web/utils/constants/app_enum.dart';
|
||||||
|
import 'package:syncrow_web/utils/extension/build_context_x.dart';
|
||||||
|
import 'package:time_picker_spinner/time_picker_spinner.dart';
|
||||||
|
|
||||||
|
class EffectPeriodHelper {
|
||||||
|
static Future<List<String>?> showCustomTimePicker(BuildContext context) async {
|
||||||
|
String selectedStartTime = "00:00";
|
||||||
|
String selectedEndTime = "23:59";
|
||||||
|
PageController pageController = PageController(initialPage: 0);
|
||||||
|
|
||||||
|
DateTime startDateTime = DateTime(2022, 1, 1, 0, 0);
|
||||||
|
DateTime endDateTime = DateTime(2022, 1, 1, 23, 59);
|
||||||
|
|
||||||
|
context.customAlertDialog(
|
||||||
|
alertBody: SizedBox(
|
||||||
|
height: 250,
|
||||||
|
child: PageView(
|
||||||
|
controller: pageController,
|
||||||
|
physics: const NeverScrollableScrollPhysics(),
|
||||||
|
children: [
|
||||||
|
_buildTimePickerPage(
|
||||||
|
context: context,
|
||||||
|
pageController: pageController,
|
||||||
|
isStartTime: true,
|
||||||
|
time: startDateTime,
|
||||||
|
onTimeChange: (time) {
|
||||||
|
selectedStartTime =
|
||||||
|
"${time.hour.toString().padLeft(2, '0')}:${time.minute.toString().padLeft(2, '0')}";
|
||||||
|
},
|
||||||
|
),
|
||||||
|
_buildTimePickerPage(
|
||||||
|
context: context,
|
||||||
|
pageController: pageController,
|
||||||
|
isStartTime: false,
|
||||||
|
time: endDateTime,
|
||||||
|
onTimeChange: (time) {
|
||||||
|
selectedEndTime =
|
||||||
|
"${time.hour.toString().padLeft(2, '0')}:${time.minute.toString().padLeft(2, '0')}";
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
title: "Custom",
|
||||||
|
onConfirm: () {
|
||||||
|
context.read<EffectPeriodBloc>().add(
|
||||||
|
SetCustomTime(selectedStartTime, selectedEndTime),
|
||||||
|
);
|
||||||
|
context.read<EffectPeriodBloc>().add(
|
||||||
|
const SetPeriod(EnumEffectivePeriodOptions.custom),
|
||||||
|
);
|
||||||
|
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
},
|
||||||
|
);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
static Widget _buildTimePickerPage({
|
||||||
|
required BuildContext context,
|
||||||
|
required PageController pageController,
|
||||||
|
required bool isStartTime,
|
||||||
|
required DateTime time,
|
||||||
|
required Function(DateTime) onTimeChange,
|
||||||
|
}) {
|
||||||
|
return Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 16),
|
||||||
|
child: Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
if (!isStartTime)
|
||||||
|
TextButton(
|
||||||
|
onPressed: () {
|
||||||
|
pageController.previousPage(
|
||||||
|
duration: const Duration(milliseconds: 300),
|
||||||
|
curve: Curves.easeIn,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
child: const Text('Start'),
|
||||||
|
),
|
||||||
|
TextButton(
|
||||||
|
onPressed: () {},
|
||||||
|
child: Text(isStartTime ? "Start" : "End",
|
||||||
|
style: Theme.of(context).textTheme.bodyMedium!.copyWith(
|
||||||
|
color: ColorsManager.textPrimaryColor,
|
||||||
|
fontWeight: FontWeight.w400,
|
||||||
|
fontSize: 10)),
|
||||||
|
),
|
||||||
|
if (isStartTime)
|
||||||
|
TextButton(
|
||||||
|
onPressed: () {
|
||||||
|
pageController.nextPage(
|
||||||
|
duration: const Duration(milliseconds: 300),
|
||||||
|
curve: Curves.easeIn,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
child: Text('End',
|
||||||
|
style: Theme.of(context).textTheme.bodyMedium!.copyWith(
|
||||||
|
color: ColorsManager.textPrimaryColor,
|
||||||
|
fontWeight: FontWeight.w400,
|
||||||
|
fontSize: 10)),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
TimePickerSpinner(
|
||||||
|
is24HourMode: false,
|
||||||
|
normalTextStyle: const TextStyle(
|
||||||
|
fontSize: 24,
|
||||||
|
color: Colors.grey,
|
||||||
|
),
|
||||||
|
highlightedTextStyle: const TextStyle(
|
||||||
|
fontSize: 24,
|
||||||
|
color: ColorsManager.primaryColor,
|
||||||
|
),
|
||||||
|
spacing: 20,
|
||||||
|
itemHeight: 50,
|
||||||
|
isForce2Digits: true,
|
||||||
|
time: time,
|
||||||
|
onTimeChange: onTimeChange,
|
||||||
|
),
|
||||||
|
const SizedBox(height: 16),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
static String formatEnumValue(EnumEffectivePeriodOptions value) {
|
||||||
|
switch (value) {
|
||||||
|
case EnumEffectivePeriodOptions.allDay:
|
||||||
|
return "All Day";
|
||||||
|
case EnumEffectivePeriodOptions.daytime:
|
||||||
|
return "Daytime";
|
||||||
|
case EnumEffectivePeriodOptions.night:
|
||||||
|
return "Night";
|
||||||
|
case EnumEffectivePeriodOptions.custom:
|
||||||
|
return "Custom";
|
||||||
|
case EnumEffectivePeriodOptions.none:
|
||||||
|
return "None";
|
||||||
|
default:
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
384
lib/pages/routiens/helper/setting_helper.dart
Normal file
384
lib/pages/routiens/helper/setting_helper.dart
Normal file
@ -0,0 +1,384 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
|
import 'package:syncrow_web/pages/routiens/bloc/setting_bloc/setting_bloc.dart';
|
||||||
|
import 'package:syncrow_web/pages/routiens/bloc/setting_bloc/setting_event.dart';
|
||||||
|
import 'package:syncrow_web/pages/routiens/bloc/setting_bloc/setting_state.dart';
|
||||||
|
import 'package:syncrow_web/pages/routiens/models/icon_model.dart';
|
||||||
|
import 'package:syncrow_web/pages/routiens/view/effective_period_view.dart';
|
||||||
|
import 'package:syncrow_web/pages/routiens/widgets/dialog_header.dart';
|
||||||
|
import 'package:syncrow_web/utils/color_manager.dart';
|
||||||
|
import 'package:flutter/cupertino.dart';
|
||||||
|
|
||||||
|
class SettingHelper {
|
||||||
|
static Future<Map<String, dynamic>?> showSettingDialog(
|
||||||
|
{required BuildContext context, String? iconId, required bool isAutomation}) async {
|
||||||
|
return showDialog<Map<String, dynamic>?>(
|
||||||
|
context: context,
|
||||||
|
builder: (BuildContext context) {
|
||||||
|
return BlocProvider(
|
||||||
|
create: (_) => SettingBloc()..add(InitialEvent(selectedIcon: iconId ?? '')),
|
||||||
|
child: AlertDialog(
|
||||||
|
contentPadding: EdgeInsets.zero,
|
||||||
|
content: BlocBuilder<SettingBloc, SettingState>(
|
||||||
|
builder: (context, state) {
|
||||||
|
String selectedIcon = '';
|
||||||
|
List<IconModel> list = [];
|
||||||
|
if (state is TabToRunSettingLoaded) {
|
||||||
|
selectedIcon = state.selectedIcon;
|
||||||
|
list = state.iconList;
|
||||||
|
}
|
||||||
|
return Container(
|
||||||
|
width: context.read<SettingBloc>().isExpanded ? 800 : 400,
|
||||||
|
height: context.read<SettingBloc>().isExpanded && isAutomation ? 500 : 300,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: Colors.white,
|
||||||
|
borderRadius: BorderRadius.circular(20),
|
||||||
|
),
|
||||||
|
padding: const EdgeInsets.only(top: 20),
|
||||||
|
child: Column(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
const DialogHeader('Settings'),
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
SizedBox(
|
||||||
|
width: 400,
|
||||||
|
child: isAutomation
|
||||||
|
? Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
Container(
|
||||||
|
padding: const EdgeInsets.only(
|
||||||
|
top: 10, left: 10, right: 10, bottom: 10),
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
InkWell(
|
||||||
|
onTap: () {},
|
||||||
|
child: Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
'Validity',
|
||||||
|
style: Theme.of(context)
|
||||||
|
.textTheme
|
||||||
|
.bodyMedium!
|
||||||
|
.copyWith(
|
||||||
|
color: ColorsManager.textPrimaryColor,
|
||||||
|
fontWeight: FontWeight.w400,
|
||||||
|
fontSize: 14),
|
||||||
|
),
|
||||||
|
const Icon(
|
||||||
|
Icons.arrow_forward_ios_outlined,
|
||||||
|
color: ColorsManager.textGray,
|
||||||
|
size: 15,
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(
|
||||||
|
height: 5,
|
||||||
|
),
|
||||||
|
const Divider(
|
||||||
|
color: ColorsManager.graysColor,
|
||||||
|
),
|
||||||
|
const SizedBox(
|
||||||
|
height: 5,
|
||||||
|
),
|
||||||
|
InkWell(
|
||||||
|
onTap: () {
|
||||||
|
BlocProvider.of<SettingBloc>(context).add(
|
||||||
|
FetchIcons(
|
||||||
|
expanded: !context
|
||||||
|
.read<SettingBloc>()
|
||||||
|
.isExpanded));
|
||||||
|
},
|
||||||
|
child: Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
'Effective Period',
|
||||||
|
style: Theme.of(context)
|
||||||
|
.textTheme
|
||||||
|
.bodyMedium!
|
||||||
|
.copyWith(
|
||||||
|
color: ColorsManager.textPrimaryColor,
|
||||||
|
fontWeight: FontWeight.w400,
|
||||||
|
fontSize: 14),
|
||||||
|
),
|
||||||
|
const Icon(
|
||||||
|
Icons.arrow_forward_ios_outlined,
|
||||||
|
color: ColorsManager.textGray,
|
||||||
|
size: 15,
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(
|
||||||
|
height: 5,
|
||||||
|
),
|
||||||
|
const Divider(
|
||||||
|
color: ColorsManager.graysColor,
|
||||||
|
),
|
||||||
|
const SizedBox(
|
||||||
|
height: 5,
|
||||||
|
),
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
'Executed by',
|
||||||
|
style: Theme.of(context)
|
||||||
|
.textTheme
|
||||||
|
.bodyMedium!
|
||||||
|
.copyWith(
|
||||||
|
color: ColorsManager.textPrimaryColor,
|
||||||
|
fontWeight: FontWeight.w400,
|
||||||
|
fontSize: 14),
|
||||||
|
),
|
||||||
|
Text('Cloud',
|
||||||
|
style: Theme.of(context)
|
||||||
|
.textTheme
|
||||||
|
.bodyMedium!
|
||||||
|
.copyWith(
|
||||||
|
color: ColorsManager.textGray,
|
||||||
|
fontWeight: FontWeight.w400,
|
||||||
|
fontSize: 14)),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
)),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
: Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
Container(
|
||||||
|
padding: const EdgeInsets.only(
|
||||||
|
top: 10, left: 10, right: 10, bottom: 10),
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
InkWell(
|
||||||
|
onTap: () {
|
||||||
|
BlocProvider.of<SettingBloc>(context).add(
|
||||||
|
FetchIcons(
|
||||||
|
expanded: !context
|
||||||
|
.read<SettingBloc>()
|
||||||
|
.isExpanded));
|
||||||
|
},
|
||||||
|
child: Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
'Icons',
|
||||||
|
style: Theme.of(context)
|
||||||
|
.textTheme
|
||||||
|
.bodyMedium!
|
||||||
|
.copyWith(
|
||||||
|
color: ColorsManager.textPrimaryColor,
|
||||||
|
fontWeight: FontWeight.w400,
|
||||||
|
fontSize: 14),
|
||||||
|
),
|
||||||
|
const Icon(
|
||||||
|
Icons.arrow_forward_ios_outlined,
|
||||||
|
color: ColorsManager.textGray,
|
||||||
|
size: 15,
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(
|
||||||
|
height: 5,
|
||||||
|
),
|
||||||
|
const Divider(
|
||||||
|
color: ColorsManager.graysColor,
|
||||||
|
),
|
||||||
|
const SizedBox(
|
||||||
|
height: 5,
|
||||||
|
),
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
'Show on devices page',
|
||||||
|
style: Theme.of(context)
|
||||||
|
.textTheme
|
||||||
|
.bodyMedium!
|
||||||
|
.copyWith(
|
||||||
|
color: ColorsManager.textPrimaryColor,
|
||||||
|
fontWeight: FontWeight.w400,
|
||||||
|
fontSize: 14),
|
||||||
|
),
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.end,
|
||||||
|
children: [
|
||||||
|
Container(
|
||||||
|
height: 30,
|
||||||
|
width: 1,
|
||||||
|
color: ColorsManager.graysColor,
|
||||||
|
),
|
||||||
|
Transform.scale(
|
||||||
|
scale: .8,
|
||||||
|
child: CupertinoSwitch(
|
||||||
|
value: true,
|
||||||
|
onChanged: (value) {},
|
||||||
|
applyTheme: true,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
const SizedBox(
|
||||||
|
height: 5,
|
||||||
|
),
|
||||||
|
const Divider(
|
||||||
|
color: ColorsManager.graysColor,
|
||||||
|
),
|
||||||
|
const SizedBox(
|
||||||
|
height: 5,
|
||||||
|
),
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
'Executed by',
|
||||||
|
style: Theme.of(context)
|
||||||
|
.textTheme
|
||||||
|
.bodyMedium!
|
||||||
|
.copyWith(
|
||||||
|
color: ColorsManager.textPrimaryColor,
|
||||||
|
fontWeight: FontWeight.w400,
|
||||||
|
fontSize: 14),
|
||||||
|
),
|
||||||
|
Text('Cloud',
|
||||||
|
style: Theme.of(context)
|
||||||
|
.textTheme
|
||||||
|
.bodyMedium!
|
||||||
|
.copyWith(
|
||||||
|
color: ColorsManager.textGray,
|
||||||
|
fontWeight: FontWeight.w400,
|
||||||
|
fontSize: 14)),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
)),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
if (context.read<SettingBloc>().isExpanded && !isAutomation)
|
||||||
|
SizedBox(
|
||||||
|
width: 400,
|
||||||
|
height: 150,
|
||||||
|
child: state is LoadingState
|
||||||
|
? const Center(child: CircularProgressIndicator())
|
||||||
|
: GridView.builder(
|
||||||
|
gridDelegate:
|
||||||
|
const SliverGridDelegateWithFixedCrossAxisCount(
|
||||||
|
crossAxisCount: 6,
|
||||||
|
crossAxisSpacing: 12,
|
||||||
|
mainAxisSpacing: 12,
|
||||||
|
),
|
||||||
|
shrinkWrap: true,
|
||||||
|
itemCount: list.length,
|
||||||
|
itemBuilder: (context, index) {
|
||||||
|
final iconModel = list[index];
|
||||||
|
return SizedBox(
|
||||||
|
width: 35,
|
||||||
|
height: 35,
|
||||||
|
child: InkWell(
|
||||||
|
onTap: () {
|
||||||
|
BlocProvider.of<SettingBloc>(context)
|
||||||
|
.add(SelectIcon(
|
||||||
|
iconId: iconModel.uuid,
|
||||||
|
));
|
||||||
|
selectedIcon = iconModel.uuid;
|
||||||
|
},
|
||||||
|
child: SizedBox(
|
||||||
|
child: ClipOval(
|
||||||
|
child: Container(
|
||||||
|
padding: const EdgeInsets.all(1),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
border: Border.all(
|
||||||
|
color: selectedIcon == iconModel.uuid
|
||||||
|
? ColorsManager.primaryColorWithOpacity
|
||||||
|
: Colors.transparent,
|
||||||
|
width: 2,
|
||||||
|
),
|
||||||
|
shape: BoxShape.circle,
|
||||||
|
),
|
||||||
|
child: Image.memory(
|
||||||
|
iconModel.iconBytes,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
)),
|
||||||
|
if (context.read<SettingBloc>().isExpanded && isAutomation)
|
||||||
|
const SizedBox(height: 350, width: 400, child: EffectivePeriodView())
|
||||||
|
],
|
||||||
|
),
|
||||||
|
Container(
|
||||||
|
width: MediaQuery.sizeOf(context).width,
|
||||||
|
decoration: const BoxDecoration(
|
||||||
|
border: Border(
|
||||||
|
top: BorderSide(
|
||||||
|
color: ColorsManager.greyColor,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
child: Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
Expanded(
|
||||||
|
child: InkWell(
|
||||||
|
onTap: () {
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
},
|
||||||
|
child: Container(
|
||||||
|
alignment: AlignmentDirectional.center,
|
||||||
|
child: Text(
|
||||||
|
'Cancel',
|
||||||
|
style: Theme.of(context).textTheme.bodyMedium!.copyWith(
|
||||||
|
color: ColorsManager.textGray,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Container(width: 1, height: 50, color: ColorsManager.greyColor),
|
||||||
|
Expanded(
|
||||||
|
child: InkWell(
|
||||||
|
onTap: () {
|
||||||
|
Navigator.of(context).pop(selectedIcon);
|
||||||
|
},
|
||||||
|
child: Container(
|
||||||
|
alignment: AlignmentDirectional.center,
|
||||||
|
child: Text(
|
||||||
|
'Confirm',
|
||||||
|
style: Theme.of(context).textTheme.bodyMedium!.copyWith(
|
||||||
|
color: ColorsManager.primaryColorWithOpacity,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
39
lib/pages/routiens/models/icon_model.dart
Normal file
39
lib/pages/routiens/models/icon_model.dart
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
import 'dart:convert';
|
||||||
|
import 'dart:typed_data';
|
||||||
|
|
||||||
|
class IconModel {
|
||||||
|
final String uuid;
|
||||||
|
final DateTime createdAt;
|
||||||
|
final DateTime updatedAt;
|
||||||
|
final String iconBase64;
|
||||||
|
|
||||||
|
IconModel({
|
||||||
|
required this.uuid,
|
||||||
|
required this.createdAt,
|
||||||
|
required this.updatedAt,
|
||||||
|
required this.iconBase64,
|
||||||
|
});
|
||||||
|
|
||||||
|
// Method to decode the icon from Base64 and return as Uint8List
|
||||||
|
Uint8List get iconBytes => base64Decode(iconBase64);
|
||||||
|
|
||||||
|
// Factory constructor to create an instance from JSON
|
||||||
|
factory IconModel.fromJson(Map<String, dynamic> json) {
|
||||||
|
return IconModel(
|
||||||
|
uuid: json['uuid'] as String,
|
||||||
|
createdAt: DateTime.parse(json['createdAt'] as String),
|
||||||
|
updatedAt: DateTime.parse(json['updatedAt'] as String),
|
||||||
|
iconBase64: json['icon'] as String,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Method to convert an instance back to JSON
|
||||||
|
Map<String, dynamic> toJson() {
|
||||||
|
return {
|
||||||
|
'uuid': uuid,
|
||||||
|
'createdAt': createdAt.toIso8601String(),
|
||||||
|
'updatedAt': updatedAt.toIso8601String(),
|
||||||
|
'icon': iconBase64,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
50
lib/pages/routiens/view/effective_period_view.dart
Normal file
50
lib/pages/routiens/view/effective_period_view.dart
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
|
import 'package:syncrow_web/pages/routiens/bloc/effective_period/effect_period_bloc.dart';
|
||||||
|
import 'package:syncrow_web/pages/routiens/helper/effictive_period_helper.dart';
|
||||||
|
import 'package:syncrow_web/pages/routiens/widgets/period_option.dart';
|
||||||
|
import 'package:syncrow_web/pages/routiens/widgets/repeat_days.dart';
|
||||||
|
import 'package:syncrow_web/utils/color_manager.dart';
|
||||||
|
|
||||||
|
class EffectivePeriodView extends StatelessWidget {
|
||||||
|
const EffectivePeriodView({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return BlocProvider(
|
||||||
|
create: (_) => EffectPeriodBloc(),
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.all(16.0),
|
||||||
|
child: ListView(
|
||||||
|
children: [
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
const Spacer(),
|
||||||
|
Expanded(
|
||||||
|
child: Text(
|
||||||
|
'Effective Period',
|
||||||
|
style: Theme.of(context).textTheme.bodyMedium!.copyWith(
|
||||||
|
color: ColorsManager.textPrimaryColor,
|
||||||
|
fontWeight: FontWeight.w400,
|
||||||
|
fontSize: 14),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const Spacer(),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
const Divider(
|
||||||
|
color: ColorsManager.backgroundColor,
|
||||||
|
),
|
||||||
|
const PeriodOptions(
|
||||||
|
showCustomTimePicker: EffectPeriodHelper.showCustomTimePicker,
|
||||||
|
),
|
||||||
|
const SizedBox(height: 16),
|
||||||
|
const RepeatDays(),
|
||||||
|
const SizedBox(height: 24),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
97
lib/pages/routiens/widgets/period_option.dart
Normal file
97
lib/pages/routiens/widgets/period_option.dart
Normal file
@ -0,0 +1,97 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
|
import 'package:syncrow_web/pages/routiens/bloc/effective_period/effect_period_bloc.dart';
|
||||||
|
import 'package:syncrow_web/pages/routiens/bloc/effective_period/effect_period_event.dart';
|
||||||
|
import 'package:syncrow_web/pages/routiens/bloc/effective_period/effect_period_state.dart';
|
||||||
|
import 'package:syncrow_web/pages/routiens/helper/effictive_period_helper.dart';
|
||||||
|
import 'package:syncrow_web/utils/color_manager.dart';
|
||||||
|
import 'package:syncrow_web/utils/constants/app_enum.dart';
|
||||||
|
|
||||||
|
class PeriodOptions extends StatelessWidget {
|
||||||
|
final Future<List<String>?> Function(BuildContext) showCustomTimePicker;
|
||||||
|
|
||||||
|
const PeriodOptions({
|
||||||
|
required this.showCustomTimePicker,
|
||||||
|
super.key,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return BlocBuilder<EffectPeriodBloc, EffectPeriodState>(
|
||||||
|
builder: (context, state) {
|
||||||
|
return Column(
|
||||||
|
children: [
|
||||||
|
_buildRadioOption(context, EnumEffectivePeriodOptions.allDay, '24 Hours'),
|
||||||
|
_buildRadioOption(context, EnumEffectivePeriodOptions.daytime, 'Sunrise to Sunset'),
|
||||||
|
_buildRadioOption(context, EnumEffectivePeriodOptions.night, 'Sunset to Sunrise'),
|
||||||
|
ListTile(
|
||||||
|
contentPadding: EdgeInsets.zero,
|
||||||
|
onTap: () => showCustomTimePicker(context),
|
||||||
|
title: Text(
|
||||||
|
'Custom',
|
||||||
|
style: Theme.of(context).textTheme.bodyMedium!.copyWith(
|
||||||
|
color: ColorsManager.textPrimaryColor,
|
||||||
|
fontWeight: FontWeight.w400,
|
||||||
|
fontSize: 14),
|
||||||
|
),
|
||||||
|
subtitle: state.customStartTime != null && state.customEndTime != null
|
||||||
|
? Text(
|
||||||
|
'${"${state.customStartTime}"} - ${"${state.customEndTime}"}',
|
||||||
|
style: Theme.of(context).textTheme.bodyMedium!.copyWith(
|
||||||
|
color: ColorsManager.textPrimaryColor,
|
||||||
|
fontWeight: FontWeight.w400,
|
||||||
|
fontSize: 10),
|
||||||
|
)
|
||||||
|
: Text(
|
||||||
|
'00:00 - 23:59',
|
||||||
|
style: Theme.of(context).textTheme.bodyMedium!.copyWith(
|
||||||
|
color: ColorsManager.textPrimaryColor,
|
||||||
|
fontWeight: FontWeight.w400,
|
||||||
|
fontSize: 10),
|
||||||
|
),
|
||||||
|
trailing: Radio<EnumEffectivePeriodOptions>(
|
||||||
|
value: EnumEffectivePeriodOptions.custom,
|
||||||
|
groupValue: state.selectedPeriod,
|
||||||
|
onChanged: (value) async {
|
||||||
|
if (value != null) {
|
||||||
|
context.read<EffectPeriodBloc>().add(SetPeriod(value));
|
||||||
|
}
|
||||||
|
showCustomTimePicker(context);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildRadioOption(
|
||||||
|
BuildContext context, EnumEffectivePeriodOptions value, String subtitle) {
|
||||||
|
return BlocBuilder<EffectPeriodBloc, EffectPeriodState>(
|
||||||
|
builder: (context, state) {
|
||||||
|
return ListTile(
|
||||||
|
contentPadding: EdgeInsets.zero,
|
||||||
|
onTap: () {
|
||||||
|
context.read<EffectPeriodBloc>().add(SetPeriod(value));
|
||||||
|
},
|
||||||
|
title: Text(EffectPeriodHelper.formatEnumValue(value)),
|
||||||
|
subtitle: Text(
|
||||||
|
subtitle,
|
||||||
|
style: Theme.of(context).textTheme.bodyMedium!.copyWith(
|
||||||
|
color: ColorsManager.textPrimaryColor, fontWeight: FontWeight.w400, fontSize: 10),
|
||||||
|
),
|
||||||
|
trailing: Radio<EnumEffectivePeriodOptions>(
|
||||||
|
value: value,
|
||||||
|
groupValue: state.selectedPeriod,
|
||||||
|
onChanged: (value) {
|
||||||
|
if (value != null) {
|
||||||
|
context.read<EffectPeriodBloc>().add(SetPeriod(value));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
83
lib/pages/routiens/widgets/repeat_days.dart
Normal file
83
lib/pages/routiens/widgets/repeat_days.dart
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
|
import 'package:syncrow_web/pages/routiens/bloc/effective_period/effect_period_bloc.dart';
|
||||||
|
import 'package:syncrow_web/pages/routiens/bloc/effective_period/effect_period_event.dart';
|
||||||
|
import 'package:syncrow_web/pages/routiens/bloc/effective_period/effect_period_state.dart';
|
||||||
|
import 'package:syncrow_web/utils/color_manager.dart';
|
||||||
|
|
||||||
|
class RepeatDays extends StatelessWidget {
|
||||||
|
const RepeatDays({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final effectiveBloc = context.read<EffectPeriodBloc>();
|
||||||
|
return Row(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
Text('Repeat',
|
||||||
|
style: Theme.of(context).textTheme.bodyMedium!.copyWith(
|
||||||
|
color: ColorsManager.textPrimaryColor, fontWeight: FontWeight.w400, fontSize: 14)),
|
||||||
|
const SizedBox(width: 8),
|
||||||
|
BlocBuilder<EffectPeriodBloc, EffectPeriodState>(
|
||||||
|
builder: (context, state) {
|
||||||
|
return Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceAround,
|
||||||
|
children: effectiveBloc.daysMap.entries.map((entry) {
|
||||||
|
final day = entry.key;
|
||||||
|
final abbreviation = entry.value;
|
||||||
|
final dayIndex = effectiveBloc.getDayIndex(day);
|
||||||
|
final isSelected = state.selectedDaysBinary[dayIndex] == '1';
|
||||||
|
return Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 3.0),
|
||||||
|
child: GestureDetector(
|
||||||
|
onTap: () {
|
||||||
|
effectiveBloc.add(ToggleDay(day));
|
||||||
|
},
|
||||||
|
child: Container(
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
shape: BoxShape.circle,
|
||||||
|
border: Border.all(
|
||||||
|
color: isSelected ? Colors.grey : Colors.grey.shade300,
|
||||||
|
width: 1,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
child: CircleAvatar(
|
||||||
|
radius: 15,
|
||||||
|
backgroundColor: Colors.white,
|
||||||
|
child: Text(
|
||||||
|
abbreviation,
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 16,
|
||||||
|
color: isSelected ? Colors.grey : Colors.grey.shade300,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}).toList(),
|
||||||
|
),
|
||||||
|
const SizedBox(
|
||||||
|
height: 8,
|
||||||
|
),
|
||||||
|
if (state.selectedDaysBinary == '0000000')
|
||||||
|
Text(
|
||||||
|
'At least one day must be selected',
|
||||||
|
style: Theme.of(context).textTheme.bodyMedium!.copyWith(
|
||||||
|
color: ColorsManager.textPrimaryColor,
|
||||||
|
fontWeight: FontWeight.w400,
|
||||||
|
fontSize: 14),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -1,6 +1,7 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:syncrow_web/pages/common/buttons/default_button.dart';
|
import 'package:syncrow_web/pages/common/buttons/default_button.dart';
|
||||||
import 'package:syncrow_web/pages/common/text_field/custom_text_field.dart';
|
import 'package:syncrow_web/pages/common/text_field/custom_text_field.dart';
|
||||||
|
import 'package:syncrow_web/pages/routiens/helper/setting_helper.dart';
|
||||||
import 'package:syncrow_web/utils/color_manager.dart';
|
import 'package:syncrow_web/utils/color_manager.dart';
|
||||||
import 'package:syncrow_web/utils/style.dart';
|
import 'package:syncrow_web/utils/style.dart';
|
||||||
|
|
||||||
@ -26,8 +27,8 @@ class RoutineSearchAndButtons extends StatelessWidget {
|
|||||||
crossAxisAlignment: WrapCrossAlignment.end,
|
crossAxisAlignment: WrapCrossAlignment.end,
|
||||||
children: [
|
children: [
|
||||||
ConstrainedBox(
|
ConstrainedBox(
|
||||||
constraints:
|
constraints: BoxConstraints(
|
||||||
BoxConstraints(maxWidth: constraints.maxWidth > 700 ? 450 : constraints.maxWidth - 32),
|
maxWidth: constraints.maxWidth > 700 ? 450 : constraints.maxWidth - 32),
|
||||||
child: StatefulTextField(
|
child: StatefulTextField(
|
||||||
title: 'Routine Name',
|
title: 'Routine Name',
|
||||||
height: 40,
|
height: 40,
|
||||||
@ -46,7 +47,10 @@ class RoutineSearchAndButtons extends StatelessWidget {
|
|||||||
width: 200,
|
width: 200,
|
||||||
child: Center(
|
child: Center(
|
||||||
child: DefaultButton(
|
child: DefaultButton(
|
||||||
onPressed: () {},
|
onPressed: () async {
|
||||||
|
final result = await SettingHelper.showSettingDialog(
|
||||||
|
context: context, isAutomation: true);
|
||||||
|
},
|
||||||
borderRadius: 15,
|
borderRadius: 15,
|
||||||
elevation: 0,
|
elevation: 0,
|
||||||
borderColor: ColorsManager.greyColor,
|
borderColor: ColorsManager.greyColor,
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import 'package:syncrow_web/pages/routiens/models/icon_model.dart';
|
||||||
import 'package:syncrow_web/pages/routiens/models/routine_model.dart';
|
import 'package:syncrow_web/pages/routiens/models/routine_model.dart';
|
||||||
import 'package:syncrow_web/services/api/http_service.dart';
|
import 'package:syncrow_web/services/api/http_service.dart';
|
||||||
import 'package:syncrow_web/utils/constants/api_const.dart';
|
import 'package:syncrow_web/utils/constants/api_const.dart';
|
||||||
@ -41,6 +42,21 @@ class SceneApi {
|
|||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
|
|
||||||
|
static Future<List<IconModel>> getIcon() async {
|
||||||
|
final response = await _httpService.get(
|
||||||
|
path: ApiEndpoints.getIconScene,
|
||||||
|
showServerMessage: false,
|
||||||
|
expectedResponseModel: (json) {
|
||||||
|
List<IconModel> iconsList = [];
|
||||||
|
json.forEach((element) {
|
||||||
|
iconsList.add(IconModel.fromJson(element));
|
||||||
|
});
|
||||||
|
return iconsList;
|
||||||
|
},
|
||||||
|
);
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
|
||||||
//get scene by unit id
|
//get scene by unit id
|
||||||
|
|
||||||
static Future<List<ScenesModel>> getScenesByUnitId(String unitId) async {
|
static Future<List<ScenesModel>> getScenesByUnitId(String unitId) async {
|
||||||
|
@ -12,11 +12,13 @@ abstract class ApiEndpoints {
|
|||||||
static const String getDevices = '/visitor-password/devices';
|
static const String getDevices = '/visitor-password/devices';
|
||||||
|
|
||||||
static const String sendOnlineOneTime = '/visitor-password/temporary-password/online/one-time';
|
static const String sendOnlineOneTime = '/visitor-password/temporary-password/online/one-time';
|
||||||
static const String sendOnlineMultipleTime = '/visitor-password/temporary-password/online/multiple-time';
|
static const String sendOnlineMultipleTime =
|
||||||
|
'/visitor-password/temporary-password/online/multiple-time';
|
||||||
|
|
||||||
//offline Password
|
//offline Password
|
||||||
static const String sendOffLineOneTime = '/visitor-password/temporary-password/offline/one-time';
|
static const String sendOffLineOneTime = '/visitor-password/temporary-password/offline/one-time';
|
||||||
static const String sendOffLineMultipleTime = '/visitor-password/temporary-password/offline/multiple-time';
|
static const String sendOffLineMultipleTime =
|
||||||
|
'/visitor-password/temporary-password/offline/multiple-time';
|
||||||
|
|
||||||
static const String getUser = '/user/{userUuid}';
|
static const String getUser = '/user/{userUuid}';
|
||||||
|
|
||||||
@ -44,4 +46,5 @@ abstract class ApiEndpoints {
|
|||||||
static const String powerClamp = '/device/{powerClampUuid}/power-clamp/status';
|
static const String powerClamp = '/device/{powerClampUuid}/power-clamp/status';
|
||||||
static const String getSpaceScenes = '/scene/tap-to-run/{unitUuid}';
|
static const String getSpaceScenes = '/scene/tap-to-run/{unitUuid}';
|
||||||
static const String getSpaceAutomation = '/automation/{unitUuid}';
|
static const String getSpaceAutomation = '/automation/{unitUuid}';
|
||||||
|
static const String getIconScene = '/scene/icon';
|
||||||
}
|
}
|
||||||
|
@ -103,3 +103,11 @@ enum AcValuesEnums {
|
|||||||
Heating,
|
Heating,
|
||||||
Ventilation,
|
Ventilation,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum EnumEffectivePeriodOptions {
|
||||||
|
allDay,
|
||||||
|
daytime,
|
||||||
|
night,
|
||||||
|
custom,
|
||||||
|
none,
|
||||||
|
}
|
||||||
|
@ -573,6 +573,14 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.7.2"
|
version: "0.7.2"
|
||||||
|
time_picker_spinner:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: time_picker_spinner
|
||||||
|
sha256: "53d824801d108890d22756501e7ade9db48b53dac1ec41580499dd4ebd128e3c"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.0.0"
|
||||||
typed_data:
|
typed_data:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -50,6 +50,7 @@ dependencies:
|
|||||||
dropdown_search: ^5.0.6
|
dropdown_search: ^5.0.6
|
||||||
flutter_dotenv: ^5.1.0
|
flutter_dotenv: ^5.1.0
|
||||||
fl_chart: ^0.69.0
|
fl_chart: ^0.69.0
|
||||||
|
time_picker_spinner: ^1.0.0
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_test:
|
flutter_test:
|
||||||
|
Reference in New Issue
Block a user