push updated code after merge

This commit is contained in:
ashrafzarkanisala
2024-11-23 21:53:21 +03:00
18 changed files with 1219 additions and 5 deletions

View 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));
}
}

View File

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

View File

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

View 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'));
}
}
}

View 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];
}

View 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];
}

View 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 "";
}
}
}

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

View 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,
};
}
}

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

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

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

View File

@ -1,6 +1,7 @@
import 'package:flutter/material.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/routiens/helper/setting_helper.dart';
import 'package:syncrow_web/utils/color_manager.dart';
import 'package:syncrow_web/utils/style.dart';
@ -26,8 +27,8 @@ class RoutineSearchAndButtons extends StatelessWidget {
crossAxisAlignment: WrapCrossAlignment.end,
children: [
ConstrainedBox(
constraints:
BoxConstraints(maxWidth: constraints.maxWidth > 700 ? 450 : constraints.maxWidth - 32),
constraints: BoxConstraints(
maxWidth: constraints.maxWidth > 700 ? 450 : constraints.maxWidth - 32),
child: StatefulTextField(
title: 'Routine Name',
height: 40,
@ -46,7 +47,10 @@ class RoutineSearchAndButtons extends StatelessWidget {
width: 200,
child: Center(
child: DefaultButton(
onPressed: () {},
onPressed: () async {
final result = await SettingHelper.showSettingDialog(
context: context, isAutomation: true);
},
borderRadius: 15,
elevation: 0,
borderColor: ColorsManager.greyColor,

View File

@ -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/services/api/http_service.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
static Future<List<ScenesModel>> getScenesByUnitId(String unitId) async {

View File

@ -12,11 +12,13 @@ abstract class ApiEndpoints {
static const String getDevices = '/visitor-password/devices';
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
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}';
@ -44,4 +46,5 @@ abstract class ApiEndpoints {
static const String powerClamp = '/device/{powerClampUuid}/power-clamp/status';
static const String getSpaceScenes = '/scene/tap-to-run/{unitUuid}';
static const String getSpaceAutomation = '/automation/{unitUuid}';
static const String getIconScene = '/scene/icon';
}

View File

@ -103,3 +103,11 @@ enum AcValuesEnums {
Heating,
Ventilation,
}
enum EnumEffectivePeriodOptions {
allDay,
daytime,
night,
custom,
none,
}

View File

@ -573,6 +573,14 @@ packages:
url: "https://pub.dev"
source: hosted
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:
dependency: transitive
description:

View File

@ -50,6 +50,7 @@ dependencies:
dropdown_search: ^5.0.6
flutter_dotenv: ^5.1.0
fl_chart: ^0.69.0
time_picker_spinner: ^1.0.0
dev_dependencies:
flutter_test: