mirror of
https://github.com/SyncrowIOT/web.git
synced 2026-10-27 01:57:56 +00:00
Compare commits
54
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d8f40badc0 | ||
|
|
fdd5d0feed | ||
|
|
fb1f79c7bb | ||
|
|
1923ac7014 | ||
|
|
c114161357 | ||
|
|
fe1dbb66ac | ||
|
|
b4de07de2f | ||
|
|
acefe6b433 | ||
|
|
63bc7a56de | ||
|
|
7b3635deae | ||
|
|
58755eafe1 | ||
|
|
ce225818fb | ||
|
|
8762a7aaa8 | ||
|
|
8dc833b2c3 | ||
|
|
13cef151aa | ||
|
|
ab23be9828 | ||
|
|
687b68ab22 | ||
|
|
25614c3dd0 | ||
|
|
7cbe20ae88 | ||
|
|
349fe6c555 | ||
|
|
9779f3783c | ||
|
|
fe3db663b6 | ||
|
|
888d444752 | ||
|
|
bab5e06968 | ||
|
|
d7b6174dee | ||
|
|
6ef0b2f9d1 | ||
|
|
3ceb03826e | ||
|
|
52608b1f35 | ||
|
|
ac2996629e | ||
|
|
51c52c66cb | ||
|
|
0f56273d99 | ||
|
|
34d4d892d9 | ||
|
|
3193fd26fe | ||
|
|
43802a9fad | ||
|
|
6e0b1775f0 | ||
|
|
233fb2ee2c | ||
|
|
b26928b3d5 | ||
|
|
6fc35a7b9a | ||
|
|
756457927c | ||
|
|
f30d7c0117 | ||
|
|
976d6e385a | ||
|
|
ff07e7509d | ||
|
|
17a582ab99 | ||
|
|
09fb604acc | ||
|
|
2068df173d | ||
|
|
bfc2a381d2 | ||
|
|
778257644d | ||
|
|
c8e540e938 | ||
|
|
ba20998067 | ||
|
|
75b0b24543 | ||
|
|
c03b8f290c | ||
|
|
2c684a9495 | ||
|
|
fbc45b465f | ||
|
|
d1bb7b129f |
@@ -0,0 +1,3 @@
|
|||||||
|
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M16.25 2.1875H14.6875V1.875C14.6875 1.62636 14.5887 1.3879 14.4129 1.21209C14.2371 1.03627 13.9986 0.9375 13.75 0.9375C13.5014 0.9375 13.2629 1.03627 13.0871 1.21209C12.9113 1.3879 12.8125 1.62636 12.8125 1.875V2.1875H7.1875V1.875C7.1875 1.62636 7.08873 1.3879 6.91291 1.21209C6.7371 1.03627 6.49864 0.9375 6.25 0.9375C6.00136 0.9375 5.7629 1.03627 5.58709 1.21209C5.41127 1.3879 5.3125 1.62636 5.3125 1.875V2.1875H3.75C3.3356 2.1875 2.93817 2.35212 2.64515 2.64515C2.35212 2.93817 2.1875 3.3356 2.1875 3.75V16.25C2.1875 16.6644 2.35212 17.0618 2.64515 17.3549C2.93817 17.6479 3.3356 17.8125 3.75 17.8125H16.25C16.6644 17.8125 17.0618 17.6479 17.3549 17.3549C17.6479 17.0618 17.8125 16.6644 17.8125 16.25V3.75C17.8125 3.3356 17.6479 2.93817 17.3549 2.64515C17.0618 2.35212 16.6644 2.1875 16.25 2.1875ZM5.3125 4.0625C5.3125 4.31114 5.41127 4.5496 5.58709 4.72541C5.7629 4.90123 6.00136 5 6.25 5C6.49864 5 6.7371 4.90123 6.91291 4.72541C7.08873 4.5496 7.1875 4.31114 7.1875 4.0625H12.8125C12.8125 4.31114 12.9113 4.5496 13.0871 4.72541C13.2629 4.90123 13.5014 5 13.75 5C13.9986 5 14.2371 4.90123 14.4129 4.72541C14.5887 4.5496 14.6875 4.31114 14.6875 4.0625H15.9375V5.9375H4.0625V4.0625H5.3125ZM4.0625 15.9375V7.8125H15.9375V15.9375H4.0625Z" fill="#475569"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 1.3 KiB |
@@ -0,0 +1,18 @@
|
|||||||
|
import 'package:bloc/bloc.dart';
|
||||||
|
import 'package:equatable/equatable.dart';
|
||||||
|
import 'package:syncrow_web/pages/analytics/modules/analytics/enums/analytics_page_tab.dart';
|
||||||
|
|
||||||
|
part 'analytics_tab_event.dart';
|
||||||
|
|
||||||
|
class AnalyticsTabBloc extends Bloc<AnalyticsTabEvent, AnalyticsPageTab> {
|
||||||
|
AnalyticsTabBloc() : super(AnalyticsPageTab.energyManagement) {
|
||||||
|
on<UpdateAnalyticsTabEvent>(_onUpdateAnalyticsTabEvent);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _onUpdateAnalyticsTabEvent(
|
||||||
|
UpdateAnalyticsTabEvent event,
|
||||||
|
Emitter<AnalyticsPageTab> emit,
|
||||||
|
) {
|
||||||
|
emit(event.analyticsTab);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
part of 'analytics_tab_bloc.dart';
|
||||||
|
|
||||||
|
sealed class AnalyticsTabEvent extends Equatable {
|
||||||
|
const AnalyticsTabEvent();
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<Object> get props => [];
|
||||||
|
}
|
||||||
|
|
||||||
|
class UpdateAnalyticsTabEvent extends AnalyticsTabEvent {
|
||||||
|
const UpdateAnalyticsTabEvent(this.analyticsTab);
|
||||||
|
|
||||||
|
final AnalyticsPageTab analyticsTab;
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<Object> get props => [analyticsTab];
|
||||||
|
}
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:syncrow_web/pages/analytics/modules/energy_management/views/analytics_energy_management_view.dart';
|
||||||
|
import 'package:syncrow_web/pages/analytics/modules/occupancy/views/analytics_occupancy_view.dart';
|
||||||
|
import 'package:syncrow_web/pages/analytics/modules/overview/views/analytics_overview_view.dart';
|
||||||
|
|
||||||
|
enum AnalyticsPageTab {
|
||||||
|
overview(
|
||||||
|
title: 'Overview',
|
||||||
|
child: AnalyticsOverviewView(),
|
||||||
|
),
|
||||||
|
energyManagement(
|
||||||
|
title: 'Energy Management',
|
||||||
|
child: AnalyticsEnergyManagementView(),
|
||||||
|
),
|
||||||
|
occupancy(
|
||||||
|
title: 'Occupancy',
|
||||||
|
child: AnalyticsOccupancyView(),
|
||||||
|
);
|
||||||
|
|
||||||
|
const AnalyticsPageTab({
|
||||||
|
required this.title,
|
||||||
|
required this.child,
|
||||||
|
});
|
||||||
|
|
||||||
|
final Widget child;
|
||||||
|
final String title;
|
||||||
|
}
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
|
import 'package:syncrow_web/pages/analytics/modules/analytics/blocs/bloc/analytics_tab_bloc.dart';
|
||||||
|
import 'package:syncrow_web/pages/analytics/modules/analytics/widgets/analytics_communities_sidebar.dart';
|
||||||
|
import 'package:syncrow_web/pages/analytics/modules/analytics/widgets/analytics_page_tabs_and_children.dart';
|
||||||
|
import 'package:syncrow_web/pages/device_managment/shared/navigate_home_grid_view.dart';
|
||||||
|
import 'package:syncrow_web/utils/theme/responsive_text_theme.dart';
|
||||||
|
import 'package:syncrow_web/web_layout/web_scaffold.dart';
|
||||||
|
|
||||||
|
class AnalyticsPage extends StatelessWidget {
|
||||||
|
const AnalyticsPage({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return BlocProvider<AnalyticsTabBloc>(
|
||||||
|
create: (context) => AnalyticsTabBloc(),
|
||||||
|
child: const AnalyticsPageForm(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class AnalyticsPageForm extends StatelessWidget {
|
||||||
|
const AnalyticsPageForm({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return WebScaffold(
|
||||||
|
rightBody: const NavigateHomeGridView(),
|
||||||
|
appBarTitle: Text(
|
||||||
|
'Syncrow Analytics',
|
||||||
|
style: ResponsiveTextTheme.of(context).deviceManagementTitle,
|
||||||
|
),
|
||||||
|
enableMenuSidebar: false,
|
||||||
|
scaffoldBody: const Row(
|
||||||
|
children: [
|
||||||
|
AnalyticsCommunitiesSidebar(),
|
||||||
|
Expanded(flex: 5, child: AnalyticsPageTabsAndChildren()),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:syncrow_web/pages/space_tree/view/space_tree_view.dart';
|
||||||
|
|
||||||
|
class AnalyticsCommunitiesSidebar extends StatelessWidget {
|
||||||
|
const AnalyticsCommunitiesSidebar({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Expanded(
|
||||||
|
child: SpaceTreeView(
|
||||||
|
title: const Text('Communities'),
|
||||||
|
shouldDisableDeselectingChildrenOfSelectedParent: true,
|
||||||
|
onSelect: () {},
|
||||||
|
isSide: false,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,57 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_svg/svg.dart';
|
||||||
|
import 'package:intl/intl.dart';
|
||||||
|
import 'package:syncrow_web/utils/color_manager.dart';
|
||||||
|
import 'package:syncrow_web/utils/constants/assets.dart';
|
||||||
|
|
||||||
|
class AnalyticsDateFilterButton extends StatelessWidget {
|
||||||
|
const AnalyticsDateFilterButton({super.key});
|
||||||
|
|
||||||
|
static final _color = ColorsManager.blackColor.withValues(alpha: 0.8);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return TextButton.icon(
|
||||||
|
style: TextButton.styleFrom(
|
||||||
|
foregroundColor: _color,
|
||||||
|
shape: RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.circular(16),
|
||||||
|
side: const BorderSide(
|
||||||
|
color: ColorsManager.greyColor,
|
||||||
|
width: 1,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
backgroundColor: ColorsManager.transparentColor,
|
||||||
|
padding: const EdgeInsets.symmetric(
|
||||||
|
horizontal: 32,
|
||||||
|
vertical: 16,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
icon: SvgPicture.asset(
|
||||||
|
Assets.blankCalendar,
|
||||||
|
height: 20,
|
||||||
|
width: 20,
|
||||||
|
colorFilter: ColorFilter.mode(_color, BlendMode.srcIn),
|
||||||
|
),
|
||||||
|
label: Text(
|
||||||
|
_concatenateDate(DateTime(2024, 1), DateTime(2024, 12)),
|
||||||
|
style: const TextStyle(
|
||||||
|
fontWeight: FontWeight.w700,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
onPressed: () {},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
String _formatDate(DateTime date) {
|
||||||
|
final formatter = DateFormat('MMM yyyy');
|
||||||
|
final formattedDate = formatter.format(date);
|
||||||
|
return formattedDate;
|
||||||
|
}
|
||||||
|
|
||||||
|
String _concatenateDate(DateTime startDate, DateTime endDate) {
|
||||||
|
final formattedStartDate = _formatDate(startDate);
|
||||||
|
final formattedEndDate = _formatDate(endDate);
|
||||||
|
return '$formattedStartDate - $formattedEndDate';
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
|
import 'package:syncrow_web/pages/analytics/modules/analytics/blocs/bloc/analytics_tab_bloc.dart';
|
||||||
|
import 'package:syncrow_web/pages/analytics/modules/analytics/enums/analytics_page_tab.dart';
|
||||||
|
import 'package:syncrow_web/utils/color_manager.dart';
|
||||||
|
|
||||||
|
class AnalyticsPageTabButton extends StatelessWidget {
|
||||||
|
const AnalyticsPageTabButton({
|
||||||
|
super.key,
|
||||||
|
required this.tab,
|
||||||
|
required this.isSelected,
|
||||||
|
});
|
||||||
|
|
||||||
|
final AnalyticsPageTab tab;
|
||||||
|
final bool isSelected;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return TextButton(
|
||||||
|
onPressed: () => context.read<AnalyticsTabBloc>().add(
|
||||||
|
UpdateAnalyticsTabEvent(tab),
|
||||||
|
),
|
||||||
|
child: Text(
|
||||||
|
tab.title,
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
maxLines: 1,
|
||||||
|
softWrap: false,
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
style: TextStyle(
|
||||||
|
fontWeight: isSelected ? FontWeight.w700 : FontWeight.w400,
|
||||||
|
fontSize: 16,
|
||||||
|
color:
|
||||||
|
isSelected ? ColorsManager.slidingBlueColor : ColorsManager.textGray,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,84 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
|
import 'package:syncrow_web/pages/analytics/modules/analytics/blocs/bloc/analytics_tab_bloc.dart';
|
||||||
|
import 'package:syncrow_web/pages/analytics/modules/analytics/enums/analytics_page_tab.dart';
|
||||||
|
import 'package:syncrow_web/pages/analytics/modules/analytics/widgets/analytics_date_filter_button.dart';
|
||||||
|
import 'package:syncrow_web/pages/analytics/modules/analytics/widgets/analytics_page_tab_button.dart';
|
||||||
|
import 'package:syncrow_web/utils/style.dart';
|
||||||
|
|
||||||
|
class AnalyticsPageTabsAndChildren extends StatelessWidget {
|
||||||
|
const AnalyticsPageTabsAndChildren({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return BlocBuilder<AnalyticsTabBloc, AnalyticsPageTab>(
|
||||||
|
buildWhen: (previous, current) => previous != current,
|
||||||
|
builder: (context, selectedTab) => Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
Expanded(
|
||||||
|
child: Container(
|
||||||
|
width: MediaQuery.sizeOf(context).width * 1,
|
||||||
|
decoration: subSectionContainerDecoration,
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 32),
|
||||||
|
child: Row(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
Expanded(
|
||||||
|
flex: 4,
|
||||||
|
child: FittedBox(
|
||||||
|
alignment: AlignmentDirectional.centerStart,
|
||||||
|
fit: BoxFit.scaleDown,
|
||||||
|
child: Row(
|
||||||
|
spacing: 32,
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
...AnalyticsPageTab.values.map(
|
||||||
|
(tab) => AnimatedSwitcher(
|
||||||
|
switchInCurve: Curves.easeIn,
|
||||||
|
duration: const Duration(milliseconds: 200),
|
||||||
|
child: AnalyticsPageTabButton(
|
||||||
|
key: ValueKey(selectedTab),
|
||||||
|
tab: tab,
|
||||||
|
isSelected: tab == selectedTab,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const Spacer(),
|
||||||
|
const Expanded(
|
||||||
|
flex: 2,
|
||||||
|
child: FittedBox(
|
||||||
|
fit: BoxFit.scaleDown,
|
||||||
|
alignment: AlignmentDirectional.centerEnd,
|
||||||
|
child: AnalyticsDateFilterButton(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Expanded(
|
||||||
|
flex: 8,
|
||||||
|
child: SizedBox(
|
||||||
|
width: MediaQuery.sizeOf(context).width,
|
||||||
|
child: AnimatedSwitcher(
|
||||||
|
switchInCurve: Curves.easeIn,
|
||||||
|
duration: const Duration(milliseconds: 200),
|
||||||
|
child: selectedTab.child,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
+12
@@ -0,0 +1,12 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
class AnalyticsEnergyManagementView extends StatelessWidget {
|
||||||
|
const AnalyticsEnergyManagementView({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return const Center(
|
||||||
|
child: Text('EnergyManagementView is Working!'),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
class AnalyticsOccupancyView extends StatelessWidget {
|
||||||
|
const AnalyticsOccupancyView({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return const Center(
|
||||||
|
child: Text('AnalyticsOccupancyView is Working!'),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
class AnalyticsOverviewView extends StatelessWidget {
|
||||||
|
const AnalyticsOverviewView({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return const Center(
|
||||||
|
child: Text('Coming Soon!'),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,8 +1,8 @@
|
|||||||
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/utils/color_manager.dart';
|
||||||
import 'package:syncrow_web/utils/extension/build_context_x.dart';
|
import 'package:syncrow_web/utils/extension/build_context_x.dart';
|
||||||
import 'package:syncrow_web/utils/style.dart';
|
import 'package:syncrow_web/utils/style.dart';
|
||||||
import 'package:syncrow_web/utils/color_manager.dart';
|
|
||||||
|
|
||||||
class SearchResetButtons extends StatelessWidget {
|
class SearchResetButtons extends StatelessWidget {
|
||||||
const SearchResetButtons({
|
const SearchResetButtons({
|
||||||
@@ -17,8 +17,10 @@ class SearchResetButtons extends StatelessWidget {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Row(
|
return Row(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
children: [
|
||||||
Column(
|
Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
crossAxisAlignment: CrossAxisAlignment.center,
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
children: [
|
children: [
|
||||||
const SizedBox(height: 25),
|
const SizedBox(height: 25),
|
||||||
|
|||||||
@@ -60,7 +60,15 @@ class _CurrentTempState extends State<CurrentTemp> {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@override
|
||||||
|
void didUpdateWidget(CurrentTemp oldWidget) {
|
||||||
|
super.didUpdateWidget(oldWidget);
|
||||||
|
if (oldWidget.tempSet != widget.tempSet) {
|
||||||
|
setState(() {
|
||||||
|
_adjustedValue = _initialAdjustedValue(widget.tempSet);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@override
|
@override
|
||||||
void dispose() {
|
void dispose() {
|
||||||
_debounce?.cancel();
|
_debounce?.cancel();
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import 'package:syncrow_web/pages/device_managment/curtain/view/curtain_batch_st
|
|||||||
import 'package:syncrow_web/pages/device_managment/curtain/view/curtain_status_view.dart';
|
import 'package:syncrow_web/pages/device_managment/curtain/view/curtain_status_view.dart';
|
||||||
import 'package:syncrow_web/pages/device_managment/door_lock/view/door_lock_batch_control_view.dart';
|
import 'package:syncrow_web/pages/device_managment/door_lock/view/door_lock_batch_control_view.dart';
|
||||||
import 'package:syncrow_web/pages/device_managment/door_lock/view/door_lock_control_view.dart';
|
import 'package:syncrow_web/pages/device_managment/door_lock/view/door_lock_control_view.dart';
|
||||||
|
import 'package:syncrow_web/pages/device_managment/flush_mounted_presence_sensor/views/flush_mounted_presence_sensor_batch_control_view.dart';
|
||||||
import 'package:syncrow_web/pages/device_managment/flush_mounted_presence_sensor/views/flush_mounted_presence_sensor_control_view.dart';
|
import 'package:syncrow_web/pages/device_managment/flush_mounted_presence_sensor/views/flush_mounted_presence_sensor_control_view.dart';
|
||||||
import 'package:syncrow_web/pages/device_managment/garage_door/view/garage_door_batch_control_view.dart';
|
import 'package:syncrow_web/pages/device_managment/garage_door/view/garage_door_batch_control_view.dart';
|
||||||
import 'package:syncrow_web/pages/device_managment/garage_door/view/garage_door_control_view.dart';
|
import 'package:syncrow_web/pages/device_managment/garage_door/view/garage_door_control_view.dart';
|
||||||
@@ -198,6 +199,10 @@ mixin RouteControlsBasedCode {
|
|||||||
return SOSBatchControlView(
|
return SOSBatchControlView(
|
||||||
deviceIds: devices.where((e) => (e.productType == 'SOS')).map((e) => e.uuid!).toList(),
|
deviceIds: devices.where((e) => (e.productType == 'SOS')).map((e) => e.uuid!).toList(),
|
||||||
);
|
);
|
||||||
|
case 'NCPS':
|
||||||
|
return FlushMountedPresenceSensorBatchControlView(
|
||||||
|
devicesIds: devices.where((e) => (e.productType == 'NCPS')).map((e) => e.uuid!).toList(),
|
||||||
|
);
|
||||||
default:
|
default:
|
||||||
return const SizedBox();
|
return const SizedBox();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -108,7 +108,7 @@ class DeviceManagementPage extends StatelessWidget with HelperResponsiveLayout {
|
|||||||
return DeviceManagementBody(
|
return DeviceManagementBody(
|
||||||
devices: deviceState.filteredDevices);
|
devices: deviceState.filteredDevices);
|
||||||
} else {
|
} else {
|
||||||
return const Center(child: Text('Error fetching Devices'));
|
return const DeviceManagementBody(devices: []);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -72,6 +72,7 @@ class DeviceManagementBody extends StatelessWidget with HelperResponsiveLayout {
|
|||||||
child: state is DeviceManagementLoading
|
child: state is DeviceManagementLoading
|
||||||
? const Center(child: CircularProgressIndicator())
|
? const Center(child: CircularProgressIndicator())
|
||||||
: Column(
|
: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
Container(
|
Container(
|
||||||
padding: isLargeScreenSize(context)
|
padding: isLargeScreenSize(context)
|
||||||
|
|||||||
@@ -14,29 +14,29 @@ class DeviceSearchFilters extends StatefulWidget {
|
|||||||
|
|
||||||
class _DeviceSearchFiltersState extends State<DeviceSearchFilters>
|
class _DeviceSearchFiltersState extends State<DeviceSearchFilters>
|
||||||
with HelperResponsiveLayout {
|
with HelperResponsiveLayout {
|
||||||
final _unitNameController = TextEditingController();
|
late final TextEditingController _unitNameController;
|
||||||
final _productNameController = TextEditingController();
|
late final TextEditingController _productNameController;
|
||||||
|
|
||||||
List<Widget> get _widgets => [
|
@override
|
||||||
_buildSearchField("Space Name", _unitNameController, 200),
|
void initState() {
|
||||||
_buildSearchField("Device Name / Product Name", _productNameController, 300),
|
_unitNameController = TextEditingController();
|
||||||
_buildSearchResetButtons(),
|
_productNameController = TextEditingController();
|
||||||
];
|
super.initState();
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
if (isExtraLargeScreenSize(context)) {
|
|
||||||
return Row(
|
|
||||||
children: _widgets
|
|
||||||
.map((e) => Padding(padding: const EdgeInsets.all(10), child: e))
|
|
||||||
.toList(),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return Wrap(
|
return Wrap(
|
||||||
|
alignment: WrapAlignment.start,
|
||||||
|
runAlignment: WrapAlignment.start,
|
||||||
|
crossAxisAlignment: WrapCrossAlignment.center,
|
||||||
spacing: 20,
|
spacing: 20,
|
||||||
runSpacing: 10,
|
runSpacing: 10,
|
||||||
children: _widgets,
|
children: [
|
||||||
|
_buildSearchField("Space Name", _unitNameController, 200),
|
||||||
|
_buildSearchField("Device Name / Product Name", _productNameController, 300),
|
||||||
|
_buildSearchResetButtons(),
|
||||||
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -16,7 +16,8 @@ import 'package:syncrow_web/pages/device_managment/shared/table/report_table.dar
|
|||||||
import 'package:syncrow_web/utils/constants/assets.dart';
|
import 'package:syncrow_web/utils/constants/assets.dart';
|
||||||
import 'package:syncrow_web/utils/helpers/responsice_layout_helper/responsive_layout_helper.dart';
|
import 'package:syncrow_web/utils/helpers/responsice_layout_helper/responsive_layout_helper.dart';
|
||||||
|
|
||||||
class CeilingSensorControlsView extends StatelessWidget with HelperResponsiveLayout {
|
class CeilingSensorControlsView extends StatelessWidget
|
||||||
|
with HelperResponsiveLayout {
|
||||||
const CeilingSensorControlsView({super.key, required this.device});
|
const CeilingSensorControlsView({super.key, required this.device});
|
||||||
|
|
||||||
final AllDevicesModel device;
|
final AllDevicesModel device;
|
||||||
@@ -31,29 +32,35 @@ class CeilingSensorControlsView extends StatelessWidget with HelperResponsiveLay
|
|||||||
..add(CeilingInitialEvent(device.uuid ?? '')),
|
..add(CeilingInitialEvent(device.uuid ?? '')),
|
||||||
child: BlocBuilder<CeilingSensorBloc, CeilingSensorState>(
|
child: BlocBuilder<CeilingSensorBloc, CeilingSensorState>(
|
||||||
builder: (context, state) {
|
builder: (context, state) {
|
||||||
if (state is CeilingLoadingInitialState || state is CeilingReportsLoadingState) {
|
if (state is CeilingLoadingInitialState ||
|
||||||
|
state is CeilingReportsLoadingState) {
|
||||||
return const Center(child: CircularProgressIndicator());
|
return const Center(child: CircularProgressIndicator());
|
||||||
} else if (state is CeilingUpdateState) {
|
} else if (state is CeilingUpdateState) {
|
||||||
return _buildGridView(
|
return _buildGridView(context, state.ceilingSensorModel,
|
||||||
context, state.ceilingSensorModel, isExtraLarge, isLarge, isMedium);
|
isExtraLarge, isLarge, isMedium);
|
||||||
} else if (state is CeilingReportsState) {
|
} else if (state is CeilingReportsState) {
|
||||||
return ReportsTable(
|
return ReportsTable(
|
||||||
report: state.deviceReport,
|
report: state.deviceReport,
|
||||||
onRowTap: (index) {},
|
onRowTap: (index) {},
|
||||||
onClose: () {
|
onClose: () {
|
||||||
context.read<CeilingSensorBloc>().add(BackToCeilingGridViewEvent());
|
context
|
||||||
|
.read<CeilingSensorBloc>()
|
||||||
|
.add(BackToCeilingGridViewEvent());
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
} else if (state is ShowCeilingDescriptionState) {
|
} else if (state is ShowCeilingDescriptionState) {
|
||||||
return DescriptionView(
|
return DescriptionView(
|
||||||
description: state.description,
|
description: state.description,
|
||||||
onClose: () {
|
onClose: () {
|
||||||
context.read<CeilingSensorBloc>().add(BackToCeilingGridViewEvent());
|
context
|
||||||
|
.read<CeilingSensorBloc>()
|
||||||
|
.add(BackToCeilingGridViewEvent());
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
} else if (state is CeilingReportsFailedState) {
|
} else if (state is CeilingReportsFailedState) {
|
||||||
final model = context.read<CeilingSensorBloc>().deviceStatus;
|
final model = context.read<CeilingSensorBloc>().deviceStatus;
|
||||||
return _buildGridView(context, model, isExtraLarge, isLarge, isMedium);
|
return _buildGridView(
|
||||||
|
context, model, isExtraLarge, isLarge, isMedium);
|
||||||
}
|
}
|
||||||
return const Center(child: Text('Error fetching status'));
|
return const Center(child: Text('Error fetching status'));
|
||||||
},
|
},
|
||||||
@@ -61,8 +68,8 @@ class CeilingSensorControlsView extends StatelessWidget with HelperResponsiveLay
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildGridView(BuildContext context, CeilingSensorModel model, bool isExtraLarge,
|
Widget _buildGridView(BuildContext context, CeilingSensorModel model,
|
||||||
bool isLarge, bool isMedium) {
|
bool isExtraLarge, bool isLarge, bool isMedium) {
|
||||||
return GridView(
|
return GridView(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 50),
|
padding: const EdgeInsets.symmetric(horizontal: 50),
|
||||||
shrinkWrap: true,
|
shrinkWrap: true,
|
||||||
@@ -143,8 +150,8 @@ class CeilingSensorControlsView extends StatelessWidget with HelperResponsiveLay
|
|||||||
),
|
),
|
||||||
GestureDetector(
|
GestureDetector(
|
||||||
onTap: () {
|
onTap: () {
|
||||||
context.read<CeilingSensorBloc>().add(
|
context.read<CeilingSensorBloc>().add(GetCeilingDeviceReportsEvent(
|
||||||
GetCeilingDeviceReportsEvent(code: 'presence_state', deviceUuid: device.uuid!));
|
code: 'presence_state', deviceUuid: device.uuid!));
|
||||||
},
|
},
|
||||||
child: const PresenceStaticWidget(
|
child: const PresenceStaticWidget(
|
||||||
icon: Assets.illuminanceRecordIcon,
|
icon: Assets.illuminanceRecordIcon,
|
||||||
@@ -153,9 +160,8 @@ class CeilingSensorControlsView extends StatelessWidget with HelperResponsiveLay
|
|||||||
),
|
),
|
||||||
GestureDetector(
|
GestureDetector(
|
||||||
onTap: () {
|
onTap: () {
|
||||||
context
|
context.read<CeilingSensorBloc>().add(GetCeilingDeviceReportsEvent(
|
||||||
.read<CeilingSensorBloc>()
|
code: '', deviceUuid: device.uuid!));
|
||||||
.add(GetCeilingDeviceReportsEvent(code: '', deviceUuid: device.uuid!));
|
|
||||||
},
|
},
|
||||||
child: const PresenceStaticWidget(
|
child: const PresenceStaticWidget(
|
||||||
icon: Assets.helpDescriptionIcon,
|
icon: Assets.helpDescriptionIcon,
|
||||||
|
|||||||
+34
-21
@@ -49,6 +49,9 @@ class FlushMountedPresenceSensorBloc
|
|||||||
on<FlushMountedPresenceSensorFactoryResetEvent>(
|
on<FlushMountedPresenceSensorFactoryResetEvent>(
|
||||||
_onFlushMountedPresenceSensorFactoryResetEvent,
|
_onFlushMountedPresenceSensorFactoryResetEvent,
|
||||||
);
|
);
|
||||||
|
on<FlushMountedPresenceSensorStatusUpdatedEvent>(
|
||||||
|
_onFlushMountedPresenceSensorStatusUpdatedEvent,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
void _onFlushMountedPresenceSensorFetchStatusEvent(
|
void _onFlushMountedPresenceSensorFetchStatusEvent(
|
||||||
@@ -60,7 +63,7 @@ class FlushMountedPresenceSensorBloc
|
|||||||
final response = await DevicesManagementApi().getDeviceStatus(deviceId);
|
final response = await DevicesManagementApi().getDeviceStatus(deviceId);
|
||||||
deviceStatus = FlushMountedPresenceSensorModel.fromJson(response.status);
|
deviceStatus = FlushMountedPresenceSensorModel.fromJson(response.status);
|
||||||
emit(FlushMountedPresenceSensorUpdateState(model: deviceStatus));
|
emit(FlushMountedPresenceSensorUpdateState(model: deviceStatus));
|
||||||
_listenToChanges(emit, deviceId);
|
_listenToChanges(deviceId);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
emit(FlushMountedPresenceSensorFailedState(error: e.toString()));
|
emit(FlushMountedPresenceSensorFailedState(error: e.toString()));
|
||||||
return;
|
return;
|
||||||
@@ -81,31 +84,33 @@ class FlushMountedPresenceSensorBloc
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _listenToChanges(
|
void _listenToChanges(String deviceId) {
|
||||||
Emitter<FlushMountedPresenceSensorState> emit,
|
try {
|
||||||
String deviceId,
|
final ref = FirebaseDatabase.instance.ref(
|
||||||
) async {
|
'device-status/$deviceId',
|
||||||
final ref = FirebaseDatabase.instance.ref(
|
);
|
||||||
'device-status/$deviceId',
|
|
||||||
);
|
ref.onValue.listen((event) {
|
||||||
|
final eventsMap = event.snapshot.value as Map<dynamic, dynamic>;
|
||||||
|
|
||||||
await ref.onValue.listen(
|
|
||||||
(DatabaseEvent event) async {
|
|
||||||
Map<dynamic, dynamic> usersMap =
|
|
||||||
event.snapshot.value as Map<dynamic, dynamic>;
|
|
||||||
List<Status> statusList = [];
|
List<Status> statusList = [];
|
||||||
|
eventsMap['status'].forEach((element) {
|
||||||
(usersMap['status'] as List<dynamic>?)?.forEach((element) {
|
statusList.add(
|
||||||
statusList.add(Status(code: element['code'], value: element['value']));
|
Status(code: element['code'], value: element['value']),
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
deviceStatus = FlushMountedPresenceSensorModel.fromJson(statusList);
|
deviceStatus = FlushMountedPresenceSensorModel.fromJson(statusList);
|
||||||
if (!emit.isDone) {
|
if (!isClosed) {
|
||||||
emit(FlushMountedPresenceSensorLoadingNewSate(model: deviceStatus));
|
add(FlushMountedPresenceSensorStatusUpdatedEvent(deviceStatus));
|
||||||
}
|
}
|
||||||
},
|
});
|
||||||
onError: (error) => log(error.toString(), name: 'FirebaseDatabaseError'),
|
} catch (_) {
|
||||||
).asFuture();
|
log(
|
||||||
|
'Error listening to changes',
|
||||||
|
name: 'FlushMountedPresenceSensorBloc._listenToChanges',
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void _onFlushMountedPresenceSensorChangeValueEvent(
|
void _onFlushMountedPresenceSensorChangeValueEvent(
|
||||||
@@ -234,4 +239,12 @@ class FlushMountedPresenceSensorBloc
|
|||||||
emit(FlushMountedPresenceSensorFailedState(error: e.toString()));
|
emit(FlushMountedPresenceSensorFailedState(error: e.toString()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void _onFlushMountedPresenceSensorStatusUpdatedEvent(
|
||||||
|
FlushMountedPresenceSensorStatusUpdatedEvent event,
|
||||||
|
Emitter<FlushMountedPresenceSensorState> emit,
|
||||||
|
) {
|
||||||
|
deviceStatus = event.model;
|
||||||
|
emit(FlushMountedPresenceSensorUpdateState(model: deviceStatus));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+11
-2
@@ -10,15 +10,24 @@ sealed class FlushMountedPresenceSensorEvent extends Equatable {
|
|||||||
class FlushMountedPresenceSensorFetchStatusEvent
|
class FlushMountedPresenceSensorFetchStatusEvent
|
||||||
extends FlushMountedPresenceSensorEvent {}
|
extends FlushMountedPresenceSensorEvent {}
|
||||||
|
|
||||||
|
class FlushMountedPresenceSensorStatusUpdatedEvent
|
||||||
|
extends FlushMountedPresenceSensorEvent {
|
||||||
|
const FlushMountedPresenceSensorStatusUpdatedEvent(this.model);
|
||||||
|
|
||||||
|
final FlushMountedPresenceSensorModel model;
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<Object> get props => [model];
|
||||||
|
}
|
||||||
|
|
||||||
class FlushMountedPresenceSensorChangeValueEvent
|
class FlushMountedPresenceSensorChangeValueEvent
|
||||||
extends FlushMountedPresenceSensorEvent {
|
extends FlushMountedPresenceSensorEvent {
|
||||||
final int value;
|
final int value;
|
||||||
final String code;
|
final String code;
|
||||||
final bool isBatchControl;
|
|
||||||
const FlushMountedPresenceSensorChangeValueEvent({
|
const FlushMountedPresenceSensorChangeValueEvent({
|
||||||
required this.value,
|
required this.value,
|
||||||
required this.code,
|
required this.code,
|
||||||
this.isBatchControl = false,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
|||||||
+1
-1
@@ -16,6 +16,6 @@ abstract final class FlushMountedPresenceSensorBlocFactory {
|
|||||||
batchControlDevicesService: DebouncedBatchControlDevicesService(
|
batchControlDevicesService: DebouncedBatchControlDevicesService(
|
||||||
decoratee: RemoteBatchControlDevicesService(),
|
decoratee: RemoteBatchControlDevicesService(),
|
||||||
),
|
),
|
||||||
)..add(FlushMountedPresenceSensorFetchStatusEvent());
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+31
-22
@@ -22,7 +22,7 @@ class FlushMountedPresenceSensorBatchControlView extends StatelessWidget
|
|||||||
return BlocProvider(
|
return BlocProvider(
|
||||||
create: (context) => FlushMountedPresenceSensorBlocFactory.create(
|
create: (context) => FlushMountedPresenceSensorBlocFactory.create(
|
||||||
deviceId: devicesIds.first,
|
deviceId: devicesIds.first,
|
||||||
),
|
)..add(FlushMountedPresenceSensorFetchBatchStatusEvent(devicesIds)),
|
||||||
child: BlocBuilder<FlushMountedPresenceSensorBloc,
|
child: BlocBuilder<FlushMountedPresenceSensorBloc,
|
||||||
FlushMountedPresenceSensorState>(
|
FlushMountedPresenceSensorState>(
|
||||||
builder: (context, state) {
|
builder: (context, state) {
|
||||||
@@ -67,14 +67,15 @@ class FlushMountedPresenceSensorBatchControlView extends StatelessWidget
|
|||||||
maxValue: 9,
|
maxValue: 9,
|
||||||
steps: 1,
|
steps: 1,
|
||||||
action: (int value) => context.read<FlushMountedPresenceSensorBloc>().add(
|
action: (int value) => context.read<FlushMountedPresenceSensorBloc>().add(
|
||||||
FlushMountedPresenceSensorChangeValueEvent(
|
FlushMountedPresenceSensorBatchControlEvent(
|
||||||
|
deviceIds: devicesIds,
|
||||||
code: FlushMountedPresenceSensorModel.codeSensitivity,
|
code: FlushMountedPresenceSensorModel.codeSensitivity,
|
||||||
value: value,
|
value: value,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
PresenceUpdateData(
|
PresenceUpdateData(
|
||||||
value: (model.nearDetection / 100).toDouble(),
|
value: (model.nearDetection / 100).clamp(0.0, double.infinity),
|
||||||
title: 'Nearest Detect Dist:',
|
title: 'Nearest Detect Dist:',
|
||||||
description: 'm',
|
description: 'm',
|
||||||
minValue: 0.0,
|
minValue: 0.0,
|
||||||
@@ -83,14 +84,15 @@ class FlushMountedPresenceSensorBatchControlView extends StatelessWidget
|
|||||||
valuesPercision: 1,
|
valuesPercision: 1,
|
||||||
action: (double value) =>
|
action: (double value) =>
|
||||||
context.read<FlushMountedPresenceSensorBloc>().add(
|
context.read<FlushMountedPresenceSensorBloc>().add(
|
||||||
FlushMountedPresenceSensorChangeValueEvent(
|
FlushMountedPresenceSensorBatchControlEvent(
|
||||||
|
deviceIds: devicesIds,
|
||||||
code: FlushMountedPresenceSensorModel.codeNearDetection,
|
code: FlushMountedPresenceSensorModel.codeNearDetection,
|
||||||
value: (value * 100).toInt(),
|
value: (value * 100).toInt(),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
PresenceUpdateData(
|
PresenceUpdateData(
|
||||||
value: (model.farDetection / 100).toDouble(),
|
value: (model.farDetection / 100).clamp(0.0, double.infinity),
|
||||||
title: 'Max Detect Dist:',
|
title: 'Max Detect Dist:',
|
||||||
description: 'm',
|
description: 'm',
|
||||||
minValue: 0.0,
|
minValue: 0.0,
|
||||||
@@ -99,51 +101,57 @@ class FlushMountedPresenceSensorBatchControlView extends StatelessWidget
|
|||||||
valuesPercision: 1,
|
valuesPercision: 1,
|
||||||
action: (double value) =>
|
action: (double value) =>
|
||||||
context.read<FlushMountedPresenceSensorBloc>().add(
|
context.read<FlushMountedPresenceSensorBloc>().add(
|
||||||
FlushMountedPresenceSensorChangeValueEvent(
|
FlushMountedPresenceSensorBatchControlEvent(
|
||||||
|
deviceIds: devicesIds,
|
||||||
code: FlushMountedPresenceSensorModel.codeFarDetection,
|
code: FlushMountedPresenceSensorModel.codeFarDetection,
|
||||||
value: (value * 100).toInt(),
|
value: (value * 100).toInt(),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
PresenceUpdateData(
|
PresenceUpdateData(
|
||||||
value: model.presenceDelay.toDouble(),
|
value: model.sensiReduce.toDouble(),
|
||||||
title: 'Trigger Level:',
|
title: 'Trigger Level:',
|
||||||
minValue: 0,
|
minValue: 0,
|
||||||
maxValue: 3,
|
maxValue: 3,
|
||||||
steps: 1,
|
steps: 1,
|
||||||
action: (int value) => context.read<FlushMountedPresenceSensorBloc>().add(
|
action: (int value) => context.read<FlushMountedPresenceSensorBloc>().add(
|
||||||
FlushMountedPresenceSensorChangeValueEvent(
|
FlushMountedPresenceSensorBatchControlEvent(
|
||||||
code: FlushMountedPresenceSensorModel.codePresenceDelay,
|
deviceIds: devicesIds,
|
||||||
|
code: FlushMountedPresenceSensorModel.codeSensiReduce,
|
||||||
value: value,
|
value: value,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
PresenceUpdateData(
|
PresenceUpdateData(
|
||||||
value: (model.occurDistReduce.toDouble()),
|
value: model.occurDistReduce.toDouble(),
|
||||||
title: 'Indent Level:',
|
title: 'Indent Level:',
|
||||||
minValue: 0,
|
minValue: 0,
|
||||||
maxValue: 3,
|
maxValue: 3,
|
||||||
steps: 1,
|
steps: 1,
|
||||||
action: (int value) => context.read<FlushMountedPresenceSensorBloc>().add(
|
action: (int value) => context.read<FlushMountedPresenceSensorBloc>().add(
|
||||||
FlushMountedPresenceSensorChangeValueEvent(
|
FlushMountedPresenceSensorBatchControlEvent(
|
||||||
|
deviceIds: devicesIds,
|
||||||
code: FlushMountedPresenceSensorModel.codeOccurDistReduce,
|
code: FlushMountedPresenceSensorModel.codeOccurDistReduce,
|
||||||
value: value,
|
value: value,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
PresenceUpdateData(
|
PresenceUpdateData(
|
||||||
value: (model.sensiReduce.toDouble()),
|
value: (model.presenceDelay / 10).toDouble(),
|
||||||
title: 'Target Confirm Time:',
|
title: 'Target Confirm Time:',
|
||||||
description: 's',
|
description: 's',
|
||||||
minValue: 0,
|
minValue: 0.0,
|
||||||
maxValue: 3,
|
maxValue: 0.5,
|
||||||
steps: 1,
|
steps: 0.1,
|
||||||
action: (int value) => context.read<FlushMountedPresenceSensorBloc>().add(
|
valuesPercision: 1,
|
||||||
FlushMountedPresenceSensorChangeValueEvent(
|
action: (double value) =>
|
||||||
code: FlushMountedPresenceSensorModel.codeSensiReduce,
|
context.read<FlushMountedPresenceSensorBloc>().add(
|
||||||
value: value,
|
FlushMountedPresenceSensorBatchControlEvent(
|
||||||
),
|
deviceIds: devicesIds,
|
||||||
),
|
code: FlushMountedPresenceSensorModel.codePresenceDelay,
|
||||||
|
value: (value * 10).toInt(),
|
||||||
|
),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
PresenceUpdateData(
|
PresenceUpdateData(
|
||||||
value: ((model.noneDelay / 10).toDouble()),
|
value: ((model.noneDelay / 10).toDouble()),
|
||||||
@@ -154,7 +162,8 @@ class FlushMountedPresenceSensorBatchControlView extends StatelessWidget
|
|||||||
steps: 1,
|
steps: 1,
|
||||||
action: (double value) =>
|
action: (double value) =>
|
||||||
context.read<FlushMountedPresenceSensorBloc>().add(
|
context.read<FlushMountedPresenceSensorBloc>().add(
|
||||||
FlushMountedPresenceSensorChangeValueEvent(
|
FlushMountedPresenceSensorBatchControlEvent(
|
||||||
|
deviceIds: devicesIds,
|
||||||
code: FlushMountedPresenceSensorModel.codeNoneDelay,
|
code: FlushMountedPresenceSensorModel.codeNoneDelay,
|
||||||
value: (value * 10).round(),
|
value: (value * 10).round(),
|
||||||
),
|
),
|
||||||
|
|||||||
+20
-18
@@ -15,7 +15,7 @@ import 'package:syncrow_web/utils/helpers/responsice_layout_helper/responsive_la
|
|||||||
|
|
||||||
class FlushMountedPresenceSensorControlView extends StatelessWidget
|
class FlushMountedPresenceSensorControlView extends StatelessWidget
|
||||||
with HelperResponsiveLayout {
|
with HelperResponsiveLayout {
|
||||||
const FlushMountedPresenceSensorControlView({super.key, required this.device});
|
const FlushMountedPresenceSensorControlView({required this.device, super.key});
|
||||||
|
|
||||||
final AllDevicesModel device;
|
final AllDevicesModel device;
|
||||||
|
|
||||||
@@ -24,7 +24,7 @@ class FlushMountedPresenceSensorControlView extends StatelessWidget
|
|||||||
return BlocProvider(
|
return BlocProvider(
|
||||||
create: (context) => FlushMountedPresenceSensorBlocFactory.create(
|
create: (context) => FlushMountedPresenceSensorBlocFactory.create(
|
||||||
deviceId: device.uuid ?? '-1',
|
deviceId: device.uuid ?? '-1',
|
||||||
),
|
)..add(FlushMountedPresenceSensorFetchStatusEvent()),
|
||||||
child: BlocBuilder<FlushMountedPresenceSensorBloc,
|
child: BlocBuilder<FlushMountedPresenceSensorBloc,
|
||||||
FlushMountedPresenceSensorState>(
|
FlushMountedPresenceSensorState>(
|
||||||
builder: (context, state) {
|
builder: (context, state) {
|
||||||
@@ -113,7 +113,7 @@ class FlushMountedPresenceSensorControlView extends StatelessWidget
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
PresenceUpdateData(
|
PresenceUpdateData(
|
||||||
value: (model.nearDetection / 100).toDouble(),
|
value: (model.nearDetection / 100).clamp(0.0, double.infinity),
|
||||||
title: 'Nearest Detect Dist:',
|
title: 'Nearest Detect Dist:',
|
||||||
description: 'm',
|
description: 'm',
|
||||||
minValue: 0.0,
|
minValue: 0.0,
|
||||||
@@ -129,7 +129,7 @@ class FlushMountedPresenceSensorControlView extends StatelessWidget
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
PresenceUpdateData(
|
PresenceUpdateData(
|
||||||
value: (model.farDetection / 100).toDouble(),
|
value: (model.farDetection / 100).clamp(0.0, double.infinity),
|
||||||
title: 'Max Detect Dist:',
|
title: 'Max Detect Dist:',
|
||||||
description: 'm',
|
description: 'm',
|
||||||
minValue: 0.0,
|
minValue: 0.0,
|
||||||
@@ -145,20 +145,20 @@ class FlushMountedPresenceSensorControlView extends StatelessWidget
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
PresenceUpdateData(
|
PresenceUpdateData(
|
||||||
value: (model.presenceDelay.toDouble()),
|
value: model.sensiReduce.toDouble(),
|
||||||
title: 'Trigger Level:',
|
title: 'Trigger Level:',
|
||||||
minValue: 0,
|
minValue: 0,
|
||||||
maxValue: 3,
|
maxValue: 3,
|
||||||
steps: 1,
|
steps: 1,
|
||||||
action: (int value) => context.read<FlushMountedPresenceSensorBloc>().add(
|
action: (int value) => context.read<FlushMountedPresenceSensorBloc>().add(
|
||||||
FlushMountedPresenceSensorChangeValueEvent(
|
FlushMountedPresenceSensorChangeValueEvent(
|
||||||
code: FlushMountedPresenceSensorModel.codePresenceDelay,
|
code: FlushMountedPresenceSensorModel.codeSensiReduce,
|
||||||
value: value,
|
value: value,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
PresenceUpdateData(
|
PresenceUpdateData(
|
||||||
value: (model.occurDistReduce.toDouble()),
|
value: model.occurDistReduce.toDouble(),
|
||||||
title: 'Indent Level:',
|
title: 'Indent Level:',
|
||||||
minValue: 0,
|
minValue: 0,
|
||||||
maxValue: 3,
|
maxValue: 3,
|
||||||
@@ -171,21 +171,23 @@ class FlushMountedPresenceSensorControlView extends StatelessWidget
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
PresenceUpdateData(
|
PresenceUpdateData(
|
||||||
value: (model.sensiReduce.toDouble()),
|
value: (model.presenceDelay / 10).toDouble(),
|
||||||
|
valuesPercision: 1,
|
||||||
title: 'Target Confirm Time:',
|
title: 'Target Confirm Time:',
|
||||||
description: 's',
|
description: 's',
|
||||||
minValue: 0,
|
minValue: 0.0,
|
||||||
maxValue: 3,
|
maxValue: 0.5,
|
||||||
steps: 1,
|
steps: 0.1,
|
||||||
action: (int value) => context.read<FlushMountedPresenceSensorBloc>().add(
|
action: (double value) =>
|
||||||
FlushMountedPresenceSensorChangeValueEvent(
|
context.read<FlushMountedPresenceSensorBloc>().add(
|
||||||
code: FlushMountedPresenceSensorModel.codeSensiReduce,
|
FlushMountedPresenceSensorChangeValueEvent(
|
||||||
value: value,
|
code: FlushMountedPresenceSensorModel.codePresenceDelay,
|
||||||
),
|
value: (value * 10).toInt(),
|
||||||
),
|
),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
PresenceUpdateData(
|
PresenceUpdateData(
|
||||||
value: ((model.noneDelay / 10).toDouble()),
|
value: (model.noneDelay / 10).toDouble(),
|
||||||
description: 's',
|
description: 's',
|
||||||
title: 'Disappe Delay:',
|
title: 'Disappe Delay:',
|
||||||
minValue: 20,
|
minValue: 20,
|
||||||
|
|||||||
@@ -217,29 +217,31 @@ class SmartPowerBloc extends Bloc<SmartPowerEvent, SmartPowerState> {
|
|||||||
try {
|
try {
|
||||||
var status =
|
var status =
|
||||||
await DevicesManagementApi().getPowerClampInfo(event.deviceId);
|
await DevicesManagementApi().getPowerClampInfo(event.deviceId);
|
||||||
deviceStatus = PowerClampModel.fromJson(status);
|
deviceStatus = PowerClampModel.fromJson(status as Map<String, Object?>? ??{});
|
||||||
|
final phaseADataPoints = deviceStatus.status.phaseA.dataPoints;
|
||||||
|
final phaseBDataPoints = deviceStatus.status.phaseB.dataPoints;
|
||||||
|
final phaseCDataPoints = deviceStatus.status.phaseC.dataPoints;
|
||||||
phaseData = [
|
phaseData = [
|
||||||
{
|
{
|
||||||
'name': 'Phase A',
|
'name': 'Phase A',
|
||||||
'voltage': '${deviceStatus.status.phaseA.dataPoints[0].value / 10} V',
|
'voltage': '${(phaseADataPoints.elementAtOrNull(0)?.value as num? ?? 0) / 10} V',
|
||||||
'current': '${deviceStatus.status.phaseA.dataPoints[1].value / 10} A',
|
'current': '${(phaseADataPoints.elementAtOrNull(1)?.value as num? ?? 0) / 10} A',
|
||||||
'activePower': '${deviceStatus.status.phaseA.dataPoints[2].value} W',
|
'activePower': '${phaseADataPoints.elementAtOrNull(2)?.value??'N/A'} W',
|
||||||
'powerFactor': '${deviceStatus.status.phaseA.dataPoints[3].value}',
|
'powerFactor': '${phaseADataPoints.elementAtOrNull(3)?.value??'N/A'}',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
'name': 'Phase B',
|
'name': 'Phase B',
|
||||||
'voltage': '${deviceStatus.status.phaseB.dataPoints[0].value / 10} V',
|
'voltage': '${(phaseBDataPoints .elementAtOrNull(0)?.value as num? ?? 0) / 10} V',
|
||||||
'current': '${deviceStatus.status.phaseB.dataPoints[1].value / 10} A',
|
'current': '${(phaseBDataPoints .elementAtOrNull(1)?.value as num? ?? 0) / 10} A',
|
||||||
'activePower': '${deviceStatus.status.phaseB.dataPoints[2].value} W',
|
'activePower': '${phaseBDataPoints.elementAtOrNull(2)?.value??'N/A'} W',
|
||||||
'powerFactor': '${deviceStatus.status.phaseB.dataPoints[3].value}',
|
'powerFactor': '${phaseBDataPoints.elementAtOrNull(3)?.value??'N/A'}',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
'name': 'Phase C',
|
'name': 'Phase C',
|
||||||
'voltage': '${deviceStatus.status.phaseC.dataPoints[0].value / 10} V',
|
'voltage': '${(phaseCDataPoints.elementAtOrNull(0)?.value as num? ?? 0) / 10} V',
|
||||||
'current': '${deviceStatus.status.phaseC.dataPoints[1].value / 10} A',
|
'current': '${(phaseCDataPoints.elementAtOrNull(1)?.value as num? ?? 0) / 10} A',
|
||||||
'activePower': '${deviceStatus.status.phaseC.dataPoints[2].value} W',
|
'activePower': '${phaseCDataPoints.elementAtOrNull(2)?.value ?? 'N/A'} W',
|
||||||
'powerFactor': '${deviceStatus.status.phaseC.dataPoints[3].value}',
|
'powerFactor': '${phaseCDataPoints.elementAtOrNull(3)?.value ?? 'N/A'}',
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
emit(GetDeviceStatus());
|
emit(GetDeviceStatus());
|
||||||
@@ -785,7 +787,7 @@ class SmartPowerBloc extends Bloc<SmartPowerEvent, SmartPowerState> {
|
|||||||
void selectDateRange() async {
|
void selectDateRange() async {
|
||||||
DateTime startDate = dateTime!;
|
DateTime startDate = dateTime!;
|
||||||
DateTime endDate = DateTime(startDate.year, startDate.month + 1, 1)
|
DateTime endDate = DateTime(startDate.year, startDate.month + 1, 1)
|
||||||
.subtract(Duration(days: 1));
|
.subtract(const Duration(days: 1));
|
||||||
String formattedEndDate = DateFormat('dd/MM/yyyy').format(endDate);
|
String formattedEndDate = DateFormat('dd/MM/yyyy').format(endDate);
|
||||||
endChartDate = ' - $formattedEndDate';
|
endChartDate = ' - $formattedEndDate';
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,9 +12,9 @@ class PowerClampModel {
|
|||||||
|
|
||||||
factory PowerClampModel.fromJson(Map<String, dynamic> json) {
|
factory PowerClampModel.fromJson(Map<String, dynamic> json) {
|
||||||
return PowerClampModel(
|
return PowerClampModel(
|
||||||
productUuid: json['productUuid'],
|
productUuid: json['productUuid'] as String? ?? '',
|
||||||
productType: json['productType'],
|
productType: json['productType'] as String? ?? '',
|
||||||
status: PowerStatus.fromJson(json['status']),
|
status: PowerStatus.fromJson(json['status'] as Map<String, dynamic>? ?? {}),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -26,7 +26,7 @@ class PowerClampModel {
|
|||||||
return PowerClampModel(
|
return PowerClampModel(
|
||||||
productUuid: productUuid ?? this.productUuid,
|
productUuid: productUuid ?? this.productUuid,
|
||||||
productType: productType ?? this.productType,
|
productType: productType ?? this.productType,
|
||||||
status: statusPower ?? this.status,
|
status: statusPower ?? status,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -46,12 +46,10 @@ class PowerStatus {
|
|||||||
|
|
||||||
factory PowerStatus.fromJson(Map<String, dynamic> json) {
|
factory PowerStatus.fromJson(Map<String, dynamic> json) {
|
||||||
return PowerStatus(
|
return PowerStatus(
|
||||||
phaseA: Phase.fromJson(json['phaseA']),
|
phaseA: Phase.fromJson(json['phaseA']as List<dynamic>? ?? []),
|
||||||
phaseB: Phase.fromJson(json['phaseB']),
|
phaseB: Phase.fromJson(json['phaseB']as List<dynamic>? ?? []),
|
||||||
phaseC: Phase.fromJson(json['phaseC']),
|
phaseC: Phase.fromJson(json['phaseC']as List<dynamic>? ?? []),
|
||||||
general: Phase.fromJson(json['general']
|
general: Phase.fromJson(json['general']as List<dynamic>? ?? []
|
||||||
// List<DataPoint>.from(
|
|
||||||
// json['general'].map((x) => DataPoint.fromJson(x))),
|
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -69,30 +67,30 @@ class Phase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class DataPoint {
|
class DataPoint {
|
||||||
dynamic code;
|
final String? code;
|
||||||
dynamic customName;
|
final String? customName;
|
||||||
dynamic dpId;
|
final int? dpId;
|
||||||
dynamic time;
|
final int? time;
|
||||||
dynamic type;
|
final String? type;
|
||||||
dynamic value;
|
final dynamic value;
|
||||||
|
|
||||||
DataPoint({
|
DataPoint({
|
||||||
required this.code,
|
this.code,
|
||||||
required this.customName,
|
this.customName,
|
||||||
required this.dpId,
|
this.dpId,
|
||||||
required this.time,
|
this.time,
|
||||||
required this.type,
|
this.type,
|
||||||
required this.value,
|
this.value,
|
||||||
});
|
});
|
||||||
|
|
||||||
factory DataPoint.fromJson(Map<String, dynamic> json) {
|
factory DataPoint.fromJson(Map<String, dynamic> json) {
|
||||||
return DataPoint(
|
return DataPoint(
|
||||||
code: json['code'],
|
code: json['code'] as String?,
|
||||||
customName: json['customName'],
|
customName: json['customName'] as String?,
|
||||||
dpId: json['dpId'],
|
dpId: json['dpId'] as int?,
|
||||||
time: json['time'],
|
time: json['time'] as int?,
|
||||||
type: json['type'],
|
type: json['type'] as String?,
|
||||||
value: json['value'],
|
value: json['value'] as dynamic,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:fl_chart/fl_chart.dart';
|
import 'package:fl_chart/fl_chart.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
import 'package:syncrow_web/utils/color_manager.dart';
|
import 'package:syncrow_web/utils/color_manager.dart';
|
||||||
|
|
||||||
class EnergyConsumptionPage extends StatefulWidget {
|
class EnergyConsumptionPage extends StatefulWidget {
|
||||||
@@ -10,7 +10,8 @@ class EnergyConsumptionPage extends StatefulWidget {
|
|||||||
final Widget widget;
|
final Widget widget;
|
||||||
final Function()? onTap;
|
final Function()? onTap;
|
||||||
|
|
||||||
EnergyConsumptionPage({
|
const EnergyConsumptionPage({
|
||||||
|
super.key,
|
||||||
required this.chartData,
|
required this.chartData,
|
||||||
required this.totalConsumption,
|
required this.totalConsumption,
|
||||||
required this.date,
|
required this.date,
|
||||||
@@ -91,11 +92,12 @@ class _EnergyConsumptionPageState extends State<EnergyConsumptionPage> {
|
|||||||
],
|
],
|
||||||
),
|
),
|
||||||
Column(
|
Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
children: [
|
||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.only(top: 10),
|
padding: const EdgeInsets.only(top: 10),
|
||||||
child: SizedBox(
|
child: SizedBox(
|
||||||
height: MediaQuery.of(context).size.height * 0.11,
|
height: MediaQuery.sizeOf(context).height * 0.09,
|
||||||
child: LineChart(
|
child: LineChart(
|
||||||
LineChartData(
|
LineChartData(
|
||||||
lineTouchData: LineTouchData(
|
lineTouchData: LineTouchData(
|
||||||
@@ -151,7 +153,7 @@ class _EnergyConsumptionPageState extends State<EnergyConsumptionPage> {
|
|||||||
child: RotatedBox(
|
child: RotatedBox(
|
||||||
quarterTurns: -1,
|
quarterTurns: -1,
|
||||||
child: Text(_chartData[index].time,
|
child: Text(_chartData[index].time,
|
||||||
style: TextStyle(fontSize: 10)),
|
style: const TextStyle(fontSize: 10)),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -190,8 +192,8 @@ class _EnergyConsumptionPageState extends State<EnergyConsumptionPage> {
|
|||||||
spots: _chartData
|
spots: _chartData
|
||||||
.asMap()
|
.asMap()
|
||||||
.entries
|
.entries
|
||||||
.map((entry) => FlSpot(entry.key.toDouble(),
|
.map((entry) => FlSpot(
|
||||||
entry.value.consumption))
|
entry.key.toDouble(), entry.value.consumption))
|
||||||
.toList(),
|
.toList(),
|
||||||
isCurved: true,
|
isCurved: true,
|
||||||
color: ColorsManager.primaryColor.withOpacity(0.6),
|
color: ColorsManager.primaryColor.withOpacity(0.6),
|
||||||
@@ -218,7 +220,7 @@ class _EnergyConsumptionPageState extends State<EnergyConsumptionPage> {
|
|||||||
borderData: FlBorderData(
|
borderData: FlBorderData(
|
||||||
show: false,
|
show: false,
|
||||||
border: Border.all(
|
border: Border.all(
|
||||||
color: Color(0xff023DFE).withOpacity(0.7),
|
color: const Color(0xff023DFE).withOpacity(0.7),
|
||||||
width: 10,
|
width: 10,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -253,11 +255,9 @@ class _EnergyConsumptionPageState extends State<EnergyConsumptionPage> {
|
|||||||
child: InkWell(
|
child: InkWell(
|
||||||
onTap: widget.onTap,
|
onTap: widget.onTap,
|
||||||
child: Center(
|
child: Center(
|
||||||
child: SizedBox(
|
child: Padding(
|
||||||
child: Padding(
|
padding: const EdgeInsets.all(5),
|
||||||
padding: const EdgeInsets.all(5),
|
child: Text(widget.date),
|
||||||
child: Text(widget.date),
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -12,8 +12,7 @@ import 'package:syncrow_web/utils/constants/assets.dart';
|
|||||||
import 'package:syncrow_web/utils/helpers/responsice_layout_helper/responsive_layout_helper.dart';
|
import 'package:syncrow_web/utils/helpers/responsice_layout_helper/responsive_layout_helper.dart';
|
||||||
|
|
||||||
//Smart Power Clamp
|
//Smart Power Clamp
|
||||||
class SmartPowerDeviceControl extends StatelessWidget
|
class SmartPowerDeviceControl extends StatelessWidget with HelperResponsiveLayout {
|
||||||
with HelperResponsiveLayout {
|
|
||||||
final String deviceId;
|
final String deviceId;
|
||||||
|
|
||||||
const SmartPowerDeviceControl({super.key, required this.deviceId});
|
const SmartPowerDeviceControl({super.key, required this.deviceId});
|
||||||
@@ -25,27 +24,27 @@ class SmartPowerDeviceControl extends StatelessWidget
|
|||||||
..add(SmartPowerFetchDeviceEvent(deviceId)),
|
..add(SmartPowerFetchDeviceEvent(deviceId)),
|
||||||
child: BlocBuilder<SmartPowerBloc, SmartPowerState>(
|
child: BlocBuilder<SmartPowerBloc, SmartPowerState>(
|
||||||
builder: (context, state) {
|
builder: (context, state) {
|
||||||
final _blocProvider = BlocProvider.of<SmartPowerBloc>(context);
|
final blocProvider = BlocProvider.of<SmartPowerBloc>(context);
|
||||||
|
|
||||||
if (state is SmartPowerLoading) {
|
if (state is SmartPowerLoading) {
|
||||||
return const Center(child: CircularProgressIndicator());
|
return const Center(child: CircularProgressIndicator());
|
||||||
} else if (state is FakeState) {
|
} else if (state is FakeState) {
|
||||||
return _buildStatusControls(
|
return _buildStatusControls(
|
||||||
currentPage: _blocProvider.currentPage,
|
currentPage: blocProvider.currentPage,
|
||||||
context: context,
|
context: context,
|
||||||
blocProvider: _blocProvider,
|
blocProvider: blocProvider,
|
||||||
);
|
);
|
||||||
} else if (state is GetDeviceStatus) {
|
} else if (state is GetDeviceStatus) {
|
||||||
return _buildStatusControls(
|
return _buildStatusControls(
|
||||||
currentPage: _blocProvider.currentPage,
|
currentPage: blocProvider.currentPage,
|
||||||
context: context,
|
context: context,
|
||||||
blocProvider: _blocProvider,
|
blocProvider: blocProvider,
|
||||||
);
|
);
|
||||||
} else if (state is FilterRecordsState) {
|
} else if (state is FilterRecordsState) {
|
||||||
return _buildStatusControls(
|
return _buildStatusControls(
|
||||||
currentPage: _blocProvider.currentPage,
|
currentPage: blocProvider.currentPage,
|
||||||
context: context,
|
context: context,
|
||||||
blocProvider: _blocProvider,
|
blocProvider: blocProvider,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return const Center(child: CircularProgressIndicator());
|
return const Center(child: CircularProgressIndicator());
|
||||||
@@ -60,7 +59,7 @@ class SmartPowerDeviceControl extends StatelessWidget
|
|||||||
required SmartPowerBloc blocProvider,
|
required SmartPowerBloc blocProvider,
|
||||||
required int currentPage,
|
required int currentPage,
|
||||||
}) {
|
}) {
|
||||||
PageController _pageController = PageController(initialPage: currentPage);
|
PageController pageController = PageController(initialPage: currentPage);
|
||||||
return Container(
|
return Container(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 50),
|
padding: const EdgeInsets.symmetric(horizontal: 50),
|
||||||
child: DeviceControlsContainer(
|
child: DeviceControlsContainer(
|
||||||
@@ -85,25 +84,31 @@ class SmartPowerDeviceControl extends StatelessWidget
|
|||||||
PowerClampInfoCard(
|
PowerClampInfoCard(
|
||||||
iconPath: Assets.powerActiveIcon,
|
iconPath: Assets.powerActiveIcon,
|
||||||
title: 'Active',
|
title: 'Active',
|
||||||
value: blocProvider
|
value: blocProvider.deviceStatus.status.general.dataPoints
|
||||||
.deviceStatus.status.general.dataPoints[2].value
|
.elementAtOrNull(2)
|
||||||
.toString(),
|
?.value
|
||||||
|
.toString() ??
|
||||||
|
'',
|
||||||
unit: '',
|
unit: '',
|
||||||
),
|
),
|
||||||
PowerClampInfoCard(
|
PowerClampInfoCard(
|
||||||
iconPath: Assets.voltMeterIcon,
|
iconPath: Assets.voltMeterIcon,
|
||||||
title: 'Current',
|
title: 'Current',
|
||||||
value: blocProvider
|
value: blocProvider.deviceStatus.status.general.dataPoints
|
||||||
.deviceStatus.status.general.dataPoints[1].value
|
.elementAtOrNull(1)
|
||||||
.toString(),
|
?.value
|
||||||
|
.toString() ??
|
||||||
|
'',
|
||||||
unit: ' A',
|
unit: ' A',
|
||||||
),
|
),
|
||||||
PowerClampInfoCard(
|
PowerClampInfoCard(
|
||||||
iconPath: Assets.frequencyIcon,
|
iconPath: Assets.frequencyIcon,
|
||||||
title: 'Frequency',
|
title: 'Frequency',
|
||||||
value: blocProvider
|
value: blocProvider.deviceStatus.status.general.dataPoints
|
||||||
.deviceStatus.status.general.dataPoints[4].value
|
.elementAtOrNull(4)
|
||||||
.toString(),
|
?.value
|
||||||
|
.toString() ??
|
||||||
|
'',
|
||||||
unit: ' Hz',
|
unit: ' Hz',
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
@@ -142,7 +147,7 @@ class SmartPowerDeviceControl extends StatelessWidget
|
|||||||
icon: const Icon(Icons.arrow_left),
|
icon: const Icon(Icons.arrow_left),
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
blocProvider.add(SmartPowerArrowPressedEvent(-1));
|
blocProvider.add(SmartPowerArrowPressedEvent(-1));
|
||||||
_pageController.previousPage(
|
pageController.previousPage(
|
||||||
duration: const Duration(milliseconds: 300),
|
duration: const Duration(milliseconds: 300),
|
||||||
curve: Curves.easeInOut,
|
curve: Curves.easeInOut,
|
||||||
);
|
);
|
||||||
@@ -162,7 +167,7 @@ class SmartPowerDeviceControl extends StatelessWidget
|
|||||||
icon: const Icon(Icons.arrow_right),
|
icon: const Icon(Icons.arrow_right),
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
blocProvider.add(SmartPowerArrowPressedEvent(1));
|
blocProvider.add(SmartPowerArrowPressedEvent(1));
|
||||||
_pageController.nextPage(
|
pageController.nextPage(
|
||||||
duration: const Duration(milliseconds: 300),
|
duration: const Duration(milliseconds: 300),
|
||||||
curve: Curves.easeInOut,
|
curve: Curves.easeInOut,
|
||||||
);
|
);
|
||||||
@@ -177,7 +182,7 @@ class SmartPowerDeviceControl extends StatelessWidget
|
|||||||
Expanded(
|
Expanded(
|
||||||
flex: 2,
|
flex: 2,
|
||||||
child: PageView(
|
child: PageView(
|
||||||
controller: _pageController,
|
controller: pageController,
|
||||||
onPageChanged: (int page) {
|
onPageChanged: (int page) {
|
||||||
blocProvider.add(SmartPowerPageChangedEvent(page));
|
blocProvider.add(SmartPowerPageChangedEvent(page));
|
||||||
},
|
},
|
||||||
@@ -190,8 +195,8 @@ class SmartPowerDeviceControl extends StatelessWidget
|
|||||||
blocProvider.add(SelectDateEvent(context: context));
|
blocProvider.add(SelectDateEvent(context: context));
|
||||||
blocProvider.add(FilterRecordsByDateEvent(
|
blocProvider.add(FilterRecordsByDateEvent(
|
||||||
selectedDate: blocProvider.dateTime!,
|
selectedDate: blocProvider.dateTime!,
|
||||||
viewType: blocProvider
|
viewType:
|
||||||
.views[blocProvider.currentIndex]));
|
blocProvider.views[blocProvider.currentIndex]));
|
||||||
},
|
},
|
||||||
widget: blocProvider.dateSwitcher(),
|
widget: blocProvider.dateSwitcher(),
|
||||||
chartData: blocProvider.energyDataList.isNotEmpty
|
chartData: blocProvider.energyDataList.isNotEmpty
|
||||||
|
|||||||
@@ -84,6 +84,16 @@ class _PresenceUpdateDataState extends State<PresenceNoBodyTime> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void didUpdateWidget(PresenceNoBodyTime oldWidget) {
|
||||||
|
super.didUpdateWidget(oldWidget);
|
||||||
|
if (oldWidget.value != widget.value) {
|
||||||
|
setState(() {
|
||||||
|
_currentValue = widget.value;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return DeviceControlsContainer(
|
return DeviceControlsContainer(
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ class WallSensorBloc extends Bloc<WallSensorEvent, WallSensorState> {
|
|||||||
on<ShowDescriptionEvent>(_showDescription);
|
on<ShowDescriptionEvent>(_showDescription);
|
||||||
on<BackToGridViewEvent>(_backToGridView);
|
on<BackToGridViewEvent>(_backToGridView);
|
||||||
on<WallSensorFactoryResetEvent>(_onFactoryReset);
|
on<WallSensorFactoryResetEvent>(_onFactoryReset);
|
||||||
|
on<WallSensorRealtimeUpdateEvent>(_onRealtimeUpdate);
|
||||||
}
|
}
|
||||||
|
|
||||||
void _fetchWallSensorStatus(
|
void _fetchWallSensorStatus(
|
||||||
@@ -30,7 +31,7 @@ class WallSensorBloc extends Bloc<WallSensorEvent, WallSensorState> {
|
|||||||
var response = await DevicesManagementApi().getDeviceStatus(deviceId);
|
var response = await DevicesManagementApi().getDeviceStatus(deviceId);
|
||||||
deviceStatus = WallSensorModel.fromJson(response.status);
|
deviceStatus = WallSensorModel.fromJson(response.status);
|
||||||
emit(WallSensorUpdateState(wallSensorModel: deviceStatus));
|
emit(WallSensorUpdateState(wallSensorModel: deviceStatus));
|
||||||
_listenToChanges(emit, deviceId);
|
_listenToChanges(deviceId);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
emit(WallSensorFailedState(error: e.toString()));
|
emit(WallSensorFailedState(error: e.toString()));
|
||||||
return;
|
return;
|
||||||
@@ -52,28 +53,27 @@ class WallSensorBloc extends Bloc<WallSensorEvent, WallSensorState> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_listenToChanges(Emitter<WallSensorState> emit, deviceId) {
|
void _listenToChanges(String deviceId) {
|
||||||
try {
|
DatabaseReference ref =
|
||||||
DatabaseReference ref =
|
FirebaseDatabase.instance.ref('device-status/$deviceId');
|
||||||
FirebaseDatabase.instance.ref('device-status/$deviceId');
|
ref.onValue.listen((DatabaseEvent event) {
|
||||||
Stream<DatabaseEvent> stream = ref.onValue;
|
final data = event.snapshot.value as Map<dynamic, dynamic>?;
|
||||||
|
if (data == null) return;
|
||||||
|
|
||||||
stream.listen((DatabaseEvent event) {
|
final statusList = (data['status'] as List?)
|
||||||
Map<dynamic, dynamic> usersMap =
|
?.map((e) => Status(code: e['code'], value: e['value']))
|
||||||
event.snapshot.value as Map<dynamic, dynamic>;
|
.toList();
|
||||||
List<Status> statusList = [];
|
|
||||||
|
|
||||||
usersMap['status'].forEach((element) {
|
if (statusList != null) {
|
||||||
statusList
|
final updatedDeviceStatus = WallSensorModel.fromJson(statusList);
|
||||||
.add(Status(code: element['code'], value: element['value']));
|
if (!isClosed) {
|
||||||
});
|
add(WallSensorRealtimeUpdateEvent(updatedDeviceStatus));
|
||||||
|
}
|
||||||
deviceStatus = WallSensorModel.fromJson(statusList);
|
}
|
||||||
emit(WallSensorLoadingNewSate(wallSensorModel: deviceStatus));
|
});
|
||||||
});
|
|
||||||
} catch (_) {}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void _changeValue(
|
void _changeValue(
|
||||||
WallSensorChangeValueEvent event, Emitter<WallSensorState> emit) async {
|
WallSensorChangeValueEvent event, Emitter<WallSensorState> emit) async {
|
||||||
emit(WallSensorLoadingNewSate(wallSensorModel: deviceStatus));
|
emit(WallSensorLoadingNewSate(wallSensorModel: deviceStatus));
|
||||||
@@ -195,4 +195,12 @@ class WallSensorBloc extends Bloc<WallSensorEvent, WallSensorState> {
|
|||||||
emit(WallSensorFailedState(error: e.toString()));
|
emit(WallSensorFailedState(error: e.toString()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void _onRealtimeUpdate(
|
||||||
|
WallSensorRealtimeUpdateEvent event,
|
||||||
|
Emitter<WallSensorState> emit,
|
||||||
|
) {
|
||||||
|
deviceStatus = event.deviceStatus;
|
||||||
|
emit(WallSensorUpdateState(wallSensorModel: deviceStatus));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import 'package:equatable/equatable.dart';
|
import 'package:equatable/equatable.dart';
|
||||||
import 'package:syncrow_web/pages/device_managment/all_devices/models/factory_reset_model.dart';
|
import 'package:syncrow_web/pages/device_managment/all_devices/models/factory_reset_model.dart';
|
||||||
|
import 'package:syncrow_web/pages/device_managment/wall_sensor/model/wall_sensor_model.dart';
|
||||||
|
|
||||||
abstract class WallSensorEvent extends Equatable {
|
abstract class WallSensorEvent extends Equatable {
|
||||||
const WallSensorEvent();
|
const WallSensorEvent();
|
||||||
@@ -70,3 +71,8 @@ class WallSensorFactoryResetEvent extends WallSensorEvent {
|
|||||||
required this.factoryReset,
|
required this.factoryReset,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class WallSensorRealtimeUpdateEvent extends WallSensorEvent {
|
||||||
|
final WallSensorModel deviceStatus;
|
||||||
|
const WallSensorRealtimeUpdateEvent(this.deviceStatus);
|
||||||
|
}
|
||||||
|
|||||||
@@ -142,6 +142,19 @@ class HomeBloc extends Bloc<HomeEvent, HomeState> {
|
|||||||
},
|
},
|
||||||
color: ColorsManager.primaryColor,
|
color: ColorsManager.primaryColor,
|
||||||
),
|
),
|
||||||
|
|
||||||
|
HomeItemModel(
|
||||||
|
title: 'Syncrow Analytics',
|
||||||
|
icon: Assets.devicesIcon,
|
||||||
|
active: true,
|
||||||
|
onPress: (context) {
|
||||||
|
context.read<SpaceTreeBloc>().add(ClearCachedData());
|
||||||
|
BlocProvider.of<RoutineBloc>(context)
|
||||||
|
.add(const TriggerSwitchTabsEvent(isRoutineTab: false));
|
||||||
|
context.go(RoutesConst.analytics);
|
||||||
|
},
|
||||||
|
color: ColorsManager.primaryColor,
|
||||||
|
),
|
||||||
|
|
||||||
// HomeItemModel(
|
// HomeItemModel(
|
||||||
// title: 'Move in',
|
// title: 'Move in',
|
||||||
|
|||||||
@@ -50,8 +50,9 @@ class HomeMobilePage extends StatelessWidget {
|
|||||||
height: size.height * 0.6,
|
height: size.height * 0.6,
|
||||||
width: size.width * 0.68,
|
width: size.width * 0.68,
|
||||||
child: GridView.builder(
|
child: GridView.builder(
|
||||||
itemCount: 3,
|
itemCount: homeItems.length,
|
||||||
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
|
gridDelegate:
|
||||||
|
const SliverGridDelegateWithFixedCrossAxisCount(
|
||||||
crossAxisCount: 2,
|
crossAxisCount: 2,
|
||||||
crossAxisSpacing: 20.0,
|
crossAxisSpacing: 20.0,
|
||||||
mainAxisSpacing: 20.0,
|
mainAxisSpacing: 20.0,
|
||||||
@@ -60,10 +61,11 @@ class HomeMobilePage extends StatelessWidget {
|
|||||||
itemBuilder: (context, index) {
|
itemBuilder: (context, index) {
|
||||||
return HomeCard(
|
return HomeCard(
|
||||||
index: index,
|
index: index,
|
||||||
active: homeItems[index]['active'],
|
active: homeBloc.homeItems[index].active!,
|
||||||
name: homeItems[index]['title'],
|
name: homeBloc.homeItems[index].title!,
|
||||||
img: homeItems[index]['icon'],
|
img: homeBloc.homeItems[index].icon!,
|
||||||
onTap: () => homeBloc.homeItems[index].onPress(context),
|
onTap: () =>
|
||||||
|
homeBloc.homeItems[index].onPress(context),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
@@ -94,6 +96,11 @@ class HomeMobilePage extends StatelessWidget {
|
|||||||
'icon': Assets.devicesIcon,
|
'icon': Assets.devicesIcon,
|
||||||
'active': true,
|
'active': true,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
'title': 'Syncrow Analytics',
|
||||||
|
'icon': Assets.iconEdit,
|
||||||
|
'active': true,
|
||||||
|
},
|
||||||
// {
|
// {
|
||||||
// 'title': 'Move in',
|
// 'title': 'Move in',
|
||||||
// 'icon': Assets.moveinIcon,
|
// 'icon': Assets.moveinIcon,
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
import 'package:flutter_svg/svg.dart';
|
import 'package:flutter_svg/svg.dart';
|
||||||
import 'package:syncrow_web/pages/home/bloc/home_event.dart';
|
|
||||||
import 'package:syncrow_web/pages/home/view/agreement_and_privacy_dialog.dart';
|
|
||||||
import 'package:syncrow_web/pages/home/bloc/home_bloc.dart';
|
import 'package:syncrow_web/pages/home/bloc/home_bloc.dart';
|
||||||
|
import 'package:syncrow_web/pages/home/bloc/home_event.dart';
|
||||||
import 'package:syncrow_web/pages/home/bloc/home_state.dart';
|
import 'package:syncrow_web/pages/home/bloc/home_state.dart';
|
||||||
|
import 'package:syncrow_web/pages/home/view/agreement_and_privacy_dialog.dart';
|
||||||
import 'package:syncrow_web/pages/home/view/home_card.dart';
|
import 'package:syncrow_web/pages/home/view/home_card.dart';
|
||||||
import 'package:syncrow_web/utils/constants/assets.dart';
|
import 'package:syncrow_web/utils/constants/assets.dart';
|
||||||
import 'package:syncrow_web/web_layout/web_scaffold.dart';
|
import 'package:syncrow_web/web_layout/web_scaffold.dart';
|
||||||
@@ -24,7 +24,7 @@ class _HomeWebPageState extends State<HomeWebPage> {
|
|||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
final homeBloc = BlocProvider.of<HomeBloc>(context);
|
final homeBloc = BlocProvider.of<HomeBloc>(context);
|
||||||
homeBloc.add(FetchUserInfo());
|
homeBloc.add(const FetchUserInfo());
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -97,7 +97,7 @@ class _HomeWebPageState extends State<HomeWebPage> {
|
|||||||
height: size.height * 0.6,
|
height: size.height * 0.6,
|
||||||
width: size.width * 0.68,
|
width: size.width * 0.68,
|
||||||
child: GridView.builder(
|
child: GridView.builder(
|
||||||
itemCount: 3, // Change this count if needed.
|
itemCount: homeBloc.homeItems.length,
|
||||||
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
|
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
|
||||||
crossAxisCount: 3, // Adjust as needed.
|
crossAxisCount: 3, // Adjust as needed.
|
||||||
crossAxisSpacing: 20.0,
|
crossAxisSpacing: 20.0,
|
||||||
|
|||||||
@@ -30,8 +30,8 @@ class _RoutinesViewState extends State<RoutinesView> {
|
|||||||
final spaceId = result['space'];
|
final spaceId = result['space'];
|
||||||
final bloc = BlocProvider.of<CreateRoutineBloc>(context);
|
final bloc = BlocProvider.of<CreateRoutineBloc>(context);
|
||||||
final routineBloc = context.read<RoutineBloc>();
|
final routineBloc = context.read<RoutineBloc>();
|
||||||
bloc.add(
|
bloc.add(SaveCommunityIdAndSpaceIdEvent(
|
||||||
SaveCommunityIdAndSpaceIdEvent(communityID: communityId, spaceID: spaceId));
|
communityID: communityId, spaceID: spaceId));
|
||||||
await Future.delayed(const Duration(seconds: 1));
|
await Future.delayed(const Duration(seconds: 1));
|
||||||
routineBloc.add(const CreateNewRoutineViewEvent(createRoutineView: true));
|
routineBloc.add(const CreateNewRoutineViewEvent(createRoutineView: true));
|
||||||
}
|
}
|
||||||
@@ -61,34 +61,38 @@ class _RoutinesViewState extends State<RoutinesView> {
|
|||||||
width: context.screenWidth,
|
width: context.screenWidth,
|
||||||
child: SingleChildScrollView(
|
child: SingleChildScrollView(
|
||||||
padding: const EdgeInsetsDirectional.all(16),
|
padding: const EdgeInsetsDirectional.all(16),
|
||||||
child: Column(
|
child: Padding(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
padding: const EdgeInsets.only(left: 20),
|
||||||
mainAxisAlignment: MainAxisAlignment.start,
|
child: Column(
|
||||||
spacing: 16,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
Text(
|
spacing: 16,
|
||||||
"Create New Routines",
|
children: [
|
||||||
style: Theme.of(context).textTheme.titleLarge?.copyWith(
|
Text(
|
||||||
color: ColorsManager.grayColor,
|
"Create New Routines",
|
||||||
fontWeight: FontWeight.bold,
|
style:
|
||||||
),
|
Theme.of(context).textTheme.titleLarge?.copyWith(
|
||||||
),
|
color: ColorsManager.grayColor,
|
||||||
RoutineViewCard(
|
fontWeight: FontWeight.bold,
|
||||||
isLoading: false,
|
),
|
||||||
onChanged: (v) {},
|
),
|
||||||
status: '',
|
RoutineViewCard(
|
||||||
spaceId: '',
|
isLoading: false,
|
||||||
automationId: '',
|
onChanged: (v) {},
|
||||||
communityId: '',
|
status: '',
|
||||||
sceneId: '',
|
spaceId: '',
|
||||||
cardType: '',
|
automationId: '',
|
||||||
spaceName: '',
|
communityId: '',
|
||||||
onTap: () => _handleRoutineCreation(context),
|
sceneId: '',
|
||||||
icon: Icons.add,
|
cardType: '',
|
||||||
textString: '',
|
spaceName: '',
|
||||||
),
|
onTap: () => _handleRoutineCreation(context),
|
||||||
const FetchRoutineScenesAutomation(),
|
icon: Icons.add,
|
||||||
],
|
textString: '',
|
||||||
|
),
|
||||||
|
const FetchRoutineScenesAutomation(),
|
||||||
|
],
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -16,7 +16,8 @@ class FetchRoutineScenesAutomation extends StatelessWidget
|
|||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return BlocBuilder<RoutineBloc, RoutineState>(
|
return BlocBuilder<RoutineBloc, RoutineState>(
|
||||||
builder: (context, state) {
|
builder: (context, state) {
|
||||||
if (state.isLoading) return const Center(child: CircularProgressIndicator());
|
if (state.isLoading)
|
||||||
|
return const Center(child: CircularProgressIndicator());
|
||||||
|
|
||||||
return SingleChildScrollView(
|
return SingleChildScrollView(
|
||||||
child: Padding(
|
child: Padding(
|
||||||
@@ -40,7 +41,8 @@ class FetchRoutineScenesAutomation extends StatelessWidget
|
|||||||
const SizedBox(height: 3),
|
const SizedBox(height: 3),
|
||||||
Visibility(
|
Visibility(
|
||||||
visible: state.automations.isNotEmpty,
|
visible: state.automations.isNotEmpty,
|
||||||
replacement: _buildEmptyState(context, "No automations found"),
|
replacement:
|
||||||
|
_buildEmptyState(context, "No automations found"),
|
||||||
child: SizedBox(
|
child: SizedBox(
|
||||||
height: 200,
|
height: 200,
|
||||||
child: _buildAutomations(state),
|
child: _buildAutomations(state),
|
||||||
@@ -59,7 +61,8 @@ class FetchRoutineScenesAutomation extends StatelessWidget
|
|||||||
scrollDirection: Axis.horizontal,
|
scrollDirection: Axis.horizontal,
|
||||||
itemCount: state.automations.length,
|
itemCount: state.automations.length,
|
||||||
itemBuilder: (context, index) {
|
itemBuilder: (context, index) {
|
||||||
final isLoading = state.automations.contains(state.automations[index].id);
|
final isLoading =
|
||||||
|
state.automations.contains(state.automations[index].id);
|
||||||
|
|
||||||
return Column(
|
return Column(
|
||||||
children: [
|
children: [
|
||||||
@@ -179,10 +182,13 @@ class FetchRoutineScenesAutomation extends StatelessWidget
|
|||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildEmptyState(BuildContext context, String title) {
|
Widget _buildEmptyState(BuildContext context, String title) {
|
||||||
return Text(
|
return Padding(
|
||||||
title,
|
padding: const EdgeInsets.only(bottom: 100),
|
||||||
style: context.textTheme.bodyMedium?.copyWith(
|
child: Text(
|
||||||
color: ColorsManager.grayColor,
|
title,
|
||||||
|
style: context.textTheme.bodyMedium?.copyWith(
|
||||||
|
color: ColorsManager.grayColor,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import 'package:syncrow_web/pages/routines/models/ac/ac_operational_value.dart';
|
|||||||
import 'package:syncrow_web/pages/routines/models/device_functions.dart';
|
import 'package:syncrow_web/pages/routines/models/device_functions.dart';
|
||||||
import 'package:syncrow_web/pages/routines/widgets/dialog_footer.dart';
|
import 'package:syncrow_web/pages/routines/widgets/dialog_footer.dart';
|
||||||
import 'package:syncrow_web/pages/routines/widgets/dialog_header.dart';
|
import 'package:syncrow_web/pages/routines/widgets/dialog_header.dart';
|
||||||
|
import 'package:syncrow_web/pages/routines/widgets/routine_dialogs/helpers/routine_tap_function_helper.dart';
|
||||||
import 'package:syncrow_web/utils/color_manager.dart';
|
import 'package:syncrow_web/utils/color_manager.dart';
|
||||||
import 'package:syncrow_web/utils/extension/build_context_x.dart';
|
import 'package:syncrow_web/utils/extension/build_context_x.dart';
|
||||||
|
|
||||||
@@ -74,11 +75,26 @@ class ACHelper {
|
|||||||
child: _buildFunctionsList(
|
child: _buildFunctionsList(
|
||||||
context: context,
|
context: context,
|
||||||
acFunctions: acFunctions,
|
acFunctions: acFunctions,
|
||||||
onFunctionSelected: (functionCode, operationName) =>
|
device: device,
|
||||||
context.read<FunctionBloc>().add(SelectFunction(
|
onFunctionSelected: (functionCode, operationName) {
|
||||||
functionCode: functionCode,
|
RoutineTapFunctionHelper.onTapFunction(
|
||||||
operationName: operationName,
|
context,
|
||||||
)),
|
functionCode: functionCode,
|
||||||
|
functionOperationName: operationName,
|
||||||
|
functionValueDescription:
|
||||||
|
selectedFunctionData.valueDescription,
|
||||||
|
deviceUuid: device?.uuid,
|
||||||
|
codesToAddIntoFunctionsWithDefaultValue: [
|
||||||
|
'temp_set',
|
||||||
|
'temp_current',
|
||||||
|
],
|
||||||
|
defaultValue: functionCode == 'temp_set'
|
||||||
|
? 200
|
||||||
|
: functionCode == 'temp_current'
|
||||||
|
? -100
|
||||||
|
: 0,
|
||||||
|
);
|
||||||
|
},
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
// Value selector
|
// Value selector
|
||||||
@@ -137,6 +153,7 @@ class ACHelper {
|
|||||||
required BuildContext context,
|
required BuildContext context,
|
||||||
required List<ACFunction> acFunctions,
|
required List<ACFunction> acFunctions,
|
||||||
required Function(String, String) onFunctionSelected,
|
required Function(String, String) onFunctionSelected,
|
||||||
|
required AllDevicesModel? device,
|
||||||
}) {
|
}) {
|
||||||
return ListView.separated(
|
return ListView.separated(
|
||||||
shrinkWrap: false,
|
shrinkWrap: false,
|
||||||
|
|||||||
+6
-1
@@ -131,7 +131,12 @@ class _CeilingSensorDialogState extends State<CeilingSensorDialog> {
|
|||||||
return Row(
|
return Row(
|
||||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||||
children: [
|
children: [
|
||||||
CpsFunctionsList(cpsFunctions: _cpsFunctions),
|
CpsFunctionsList(
|
||||||
|
cpsFunctions: _cpsFunctions,
|
||||||
|
device: widget.device,
|
||||||
|
selectedFunctionData: selectedFunctionData,
|
||||||
|
dialogType: widget.dialogType,
|
||||||
|
),
|
||||||
if (state.selectedFunction != null)
|
if (state.selectedFunction != null)
|
||||||
Expanded(
|
Expanded(
|
||||||
child: isToggleFunction
|
child: isToggleFunction
|
||||||
|
|||||||
@@ -1,14 +1,24 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:syncrow_web/pages/device_managment/all_devices/models/devices_model.dart';
|
||||||
import 'package:syncrow_web/pages/routines/bloc/functions_bloc/functions_bloc_bloc.dart';
|
|
||||||
import 'package:syncrow_web/pages/routines/models/ceiling_presence_sensor_functions.dart';
|
import 'package:syncrow_web/pages/routines/models/ceiling_presence_sensor_functions.dart';
|
||||||
|
import 'package:syncrow_web/pages/routines/models/device_functions.dart';
|
||||||
import 'package:syncrow_web/pages/routines/widgets/routine_dialog_function_list_tile.dart';
|
import 'package:syncrow_web/pages/routines/widgets/routine_dialog_function_list_tile.dart';
|
||||||
|
import 'package:syncrow_web/pages/routines/widgets/routine_dialogs/helpers/routine_tap_function_helper.dart';
|
||||||
import 'package:syncrow_web/utils/color_manager.dart';
|
import 'package:syncrow_web/utils/color_manager.dart';
|
||||||
|
|
||||||
class CpsFunctionsList extends StatelessWidget {
|
class CpsFunctionsList extends StatelessWidget {
|
||||||
const CpsFunctionsList({required this.cpsFunctions, super.key});
|
const CpsFunctionsList({
|
||||||
|
required this.cpsFunctions,
|
||||||
|
required this.device,
|
||||||
|
required this.selectedFunctionData,
|
||||||
|
required this.dialogType,
|
||||||
|
super.key,
|
||||||
|
});
|
||||||
|
|
||||||
final List<CpsFunctions> cpsFunctions;
|
final List<CpsFunctions> cpsFunctions;
|
||||||
|
final AllDevicesModel? device;
|
||||||
|
final DeviceFunctionData? selectedFunctionData;
|
||||||
|
final String dialogType;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
@@ -26,12 +36,27 @@ class CpsFunctionsList extends StatelessWidget {
|
|||||||
return RoutineDialogFunctionListTile(
|
return RoutineDialogFunctionListTile(
|
||||||
iconPath: function.icon,
|
iconPath: function.icon,
|
||||||
operationName: function.operationName,
|
operationName: function.operationName,
|
||||||
onTap: () => context.read<FunctionBloc>().add(
|
onTap: () => RoutineTapFunctionHelper.onTapFunction(
|
||||||
SelectFunction(
|
context,
|
||||||
functionCode: function.code,
|
functionCode: function.code,
|
||||||
operationName: function.operationName,
|
functionOperationName: function.operationName,
|
||||||
),
|
functionValueDescription: selectedFunctionData?.valueDescription,
|
||||||
),
|
deviceUuid: device?.uuid,
|
||||||
|
codesToAddIntoFunctionsWithDefaultValue: [
|
||||||
|
'static_max_dis',
|
||||||
|
'presence_reference',
|
||||||
|
'moving_reference',
|
||||||
|
'perceptual_boundary',
|
||||||
|
'moving_boundary',
|
||||||
|
'moving_rigger_time',
|
||||||
|
'moving_static_time',
|
||||||
|
'none_body_time',
|
||||||
|
'moving_max_dis',
|
||||||
|
'moving_range',
|
||||||
|
'presence_range',
|
||||||
|
if (dialogType == "IF") 'sensitivity',
|
||||||
|
],
|
||||||
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -0,0 +1,46 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
|
import 'package:syncrow_web/pages/routines/bloc/functions_bloc/functions_bloc_bloc.dart';
|
||||||
|
import 'package:syncrow_web/pages/routines/models/device_functions.dart';
|
||||||
|
|
||||||
|
abstract final class RoutineTapFunctionHelper {
|
||||||
|
const RoutineTapFunctionHelper._();
|
||||||
|
|
||||||
|
static void onTapFunction(
|
||||||
|
BuildContext context, {
|
||||||
|
required String functionCode,
|
||||||
|
required String functionOperationName,
|
||||||
|
required String? functionValueDescription,
|
||||||
|
required String? deviceUuid,
|
||||||
|
required List<String> codesToAddIntoFunctionsWithDefaultValue,
|
||||||
|
int defaultValue = 0,
|
||||||
|
}) {
|
||||||
|
final functionsBloc = context.read<FunctionBloc>();
|
||||||
|
functionsBloc.add(
|
||||||
|
SelectFunction(
|
||||||
|
functionCode: functionCode,
|
||||||
|
operationName: functionOperationName,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
final addedFunctions = functionsBloc.state.addedFunctions;
|
||||||
|
final isFunctionAlreadyAdded = addedFunctions.any(
|
||||||
|
(e) => e.functionCode == functionCode,
|
||||||
|
);
|
||||||
|
final shouldAddFunction =
|
||||||
|
codesToAddIntoFunctionsWithDefaultValue.contains(functionCode);
|
||||||
|
if (!isFunctionAlreadyAdded && shouldAddFunction) {
|
||||||
|
context.read<FunctionBloc>().add(
|
||||||
|
AddFunction(
|
||||||
|
functionData: DeviceFunctionData(
|
||||||
|
entityId: deviceUuid ?? '',
|
||||||
|
functionCode: functionCode,
|
||||||
|
operationName: functionOperationName,
|
||||||
|
value: defaultValue,
|
||||||
|
condition: '==',
|
||||||
|
valueDescription: functionValueDescription,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -11,6 +11,7 @@ import 'package:syncrow_web/pages/routines/models/gang_switches/one_gang_switch/
|
|||||||
import 'package:syncrow_web/pages/routines/models/gang_switches/switch_operational_value.dart';
|
import 'package:syncrow_web/pages/routines/models/gang_switches/switch_operational_value.dart';
|
||||||
import 'package:syncrow_web/pages/routines/widgets/dialog_footer.dart';
|
import 'package:syncrow_web/pages/routines/widgets/dialog_footer.dart';
|
||||||
import 'package:syncrow_web/pages/routines/widgets/dialog_header.dart';
|
import 'package:syncrow_web/pages/routines/widgets/dialog_header.dart';
|
||||||
|
import 'package:syncrow_web/pages/routines/widgets/routine_dialogs/helpers/routine_tap_function_helper.dart';
|
||||||
import 'package:syncrow_web/utils/color_manager.dart';
|
import 'package:syncrow_web/utils/color_manager.dart';
|
||||||
import 'package:syncrow_web/utils/extension/build_context_x.dart';
|
import 'package:syncrow_web/utils/extension/build_context_x.dart';
|
||||||
|
|
||||||
@@ -24,21 +25,23 @@ class OneGangSwitchHelper {
|
|||||||
required String uniqueCustomId,
|
required String uniqueCustomId,
|
||||||
required bool removeComparetors,
|
required bool removeComparetors,
|
||||||
}) async {
|
}) async {
|
||||||
List<BaseSwitchFunction> oneGangFunctions = functions.whereType<BaseSwitchFunction>().toList();
|
List<BaseSwitchFunction> oneGangFunctions =
|
||||||
|
functions.whereType<BaseSwitchFunction>().toList();
|
||||||
|
|
||||||
return showDialog<Map<String, dynamic>?>(
|
return showDialog<Map<String, dynamic>?>(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (BuildContext context) {
|
builder: (BuildContext context) {
|
||||||
return BlocProvider(
|
return BlocProvider(
|
||||||
create: (_) => FunctionBloc()..add(InitializeFunctions(deviceSelectedFunctions ?? [])),
|
create: (_) => FunctionBloc()
|
||||||
|
..add(InitializeFunctions(deviceSelectedFunctions ?? [])),
|
||||||
child: AlertDialog(
|
child: AlertDialog(
|
||||||
contentPadding: EdgeInsets.zero,
|
contentPadding: EdgeInsets.zero,
|
||||||
content: BlocBuilder<FunctionBloc, FunctionBlocState>(
|
content: BlocBuilder<FunctionBloc, FunctionBlocState>(
|
||||||
builder: (context, state) {
|
builder: (context, state) {
|
||||||
final selectedFunction = state.selectedFunction;
|
final selectedFunction = state.selectedFunction;
|
||||||
final selectedOperationName = state.selectedOperationName;
|
final selectedOperationName = state.selectedOperationName;
|
||||||
final selectedFunctionData =
|
final selectedFunctionData = state.addedFunctions
|
||||||
state.addedFunctions.firstWhere((f) => f.functionCode == selectedFunction,
|
.firstWhere((f) => f.functionCode == selectedFunction,
|
||||||
orElse: () => DeviceFunctionData(
|
orElse: () => DeviceFunctionData(
|
||||||
entityId: '',
|
entityId: '',
|
||||||
functionCode: selectedFunction ?? '',
|
functionCode: selectedFunction ?? '',
|
||||||
@@ -84,12 +87,19 @@ class OneGangSwitchHelper {
|
|||||||
size: 16,
|
size: 16,
|
||||||
color: ColorsManager.textGray,
|
color: ColorsManager.textGray,
|
||||||
),
|
),
|
||||||
onTap: () {
|
onTap: () =>
|
||||||
context.read<FunctionBloc>().add(SelectFunction(
|
RoutineTapFunctionHelper.onTapFunction(
|
||||||
functionCode: function.code,
|
context,
|
||||||
operationName: function.operationName,
|
functionCode: function.code,
|
||||||
));
|
functionOperationName:
|
||||||
},
|
function.operationName,
|
||||||
|
functionValueDescription:
|
||||||
|
selectedFunctionData.valueDescription,
|
||||||
|
deviceUuid: device?.uuid,
|
||||||
|
codesToAddIntoFunctionsWithDefaultValue: [
|
||||||
|
'countdown_1',
|
||||||
|
],
|
||||||
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
@@ -220,11 +230,11 @@ class OneGangSwitchHelper {
|
|||||||
selectedFunctionData,
|
selectedFunctionData,
|
||||||
),
|
),
|
||||||
const SizedBox(height: 20),
|
const SizedBox(height: 20),
|
||||||
_buildCountDownDisplay(
|
_buildCountDownDisplay(context, initialValue, device, operationName,
|
||||||
context, initialValue, device, operationName, selectedFunctionData, selectCode),
|
selectedFunctionData, selectCode),
|
||||||
const SizedBox(height: 20),
|
const SizedBox(height: 20),
|
||||||
_buildCountDownSlider(
|
_buildCountDownSlider(context, initialValue, device, operationName,
|
||||||
context, initialValue, device, operationName, selectedFunctionData, selectCode),
|
selectedFunctionData, selectCode),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -314,9 +324,10 @@ class OneGangSwitchHelper {
|
|||||||
value: (initialValue ?? 0).toDouble(),
|
value: (initialValue ?? 0).toDouble(),
|
||||||
min: operationalValues.minValue?.toDouble() ?? 0.0,
|
min: operationalValues.minValue?.toDouble() ?? 0.0,
|
||||||
max: operationalValues.maxValue?.toDouble() ?? 0.0,
|
max: operationalValues.maxValue?.toDouble() ?? 0.0,
|
||||||
divisions: (((operationalValues.maxValue ?? 0) - (operationalValues.minValue ?? 0)) /
|
divisions:
|
||||||
(operationalValues.stepValue ?? 1))
|
(((operationalValues.maxValue ?? 0) - (operationalValues.minValue ?? 0)) /
|
||||||
.round(),
|
(operationalValues.stepValue ?? 1))
|
||||||
|
.round(),
|
||||||
onChanged: (value) {
|
onChanged: (value) {
|
||||||
context.read<FunctionBloc>().add(
|
context.read<FunctionBloc>().add(
|
||||||
AddFunction(
|
AddFunction(
|
||||||
@@ -368,7 +379,9 @@ class OneGangSwitchHelper {
|
|||||||
trailing: Icon(
|
trailing: Icon(
|
||||||
isSelected ? Icons.radio_button_checked : Icons.radio_button_unchecked,
|
isSelected ? Icons.radio_button_checked : Icons.radio_button_unchecked,
|
||||||
size: 24,
|
size: 24,
|
||||||
color: isSelected ? ColorsManager.primaryColorWithOpacity : ColorsManager.textGray,
|
color: isSelected
|
||||||
|
? ColorsManager.primaryColorWithOpacity
|
||||||
|
: ColorsManager.textGray,
|
||||||
),
|
),
|
||||||
onTap: () {
|
onTap: () {
|
||||||
if (!isSelected) {
|
if (!isSelected) {
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import 'package:syncrow_web/pages/routines/models/gang_switches/base_switch_func
|
|||||||
import 'package:syncrow_web/pages/routines/models/gang_switches/switch_operational_value.dart';
|
import 'package:syncrow_web/pages/routines/models/gang_switches/switch_operational_value.dart';
|
||||||
import 'package:syncrow_web/pages/routines/widgets/dialog_footer.dart';
|
import 'package:syncrow_web/pages/routines/widgets/dialog_footer.dart';
|
||||||
import 'package:syncrow_web/pages/routines/widgets/dialog_header.dart';
|
import 'package:syncrow_web/pages/routines/widgets/dialog_header.dart';
|
||||||
|
import 'package:syncrow_web/pages/routines/widgets/routine_dialogs/helpers/routine_tap_function_helper.dart';
|
||||||
import 'package:syncrow_web/utils/color_manager.dart';
|
import 'package:syncrow_web/utils/color_manager.dart';
|
||||||
import 'package:syncrow_web/utils/extension/build_context_x.dart';
|
import 'package:syncrow_web/utils/extension/build_context_x.dart';
|
||||||
|
|
||||||
@@ -85,15 +86,21 @@ class ThreeGangSwitchHelper {
|
|||||||
size: 16,
|
size: 16,
|
||||||
color: ColorsManager.textGray,
|
color: ColorsManager.textGray,
|
||||||
),
|
),
|
||||||
onTap: () {
|
onTap: () =>
|
||||||
context
|
RoutineTapFunctionHelper.onTapFunction(
|
||||||
.read<FunctionBloc>()
|
context,
|
||||||
.add(SelectFunction(
|
functionCode: function.code,
|
||||||
functionCode: function.code,
|
functionOperationName:
|
||||||
operationName:
|
function.operationName,
|
||||||
function.operationName,
|
functionValueDescription:
|
||||||
));
|
selectedFunctionData.valueDescription,
|
||||||
},
|
deviceUuid: device?.uuid,
|
||||||
|
codesToAddIntoFunctionsWithDefaultValue: [
|
||||||
|
'countdown_1',
|
||||||
|
'countdown_2',
|
||||||
|
'countdown_3',
|
||||||
|
],
|
||||||
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
@@ -183,8 +190,7 @@ class ThreeGangSwitchHelper {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
final selectedFn =
|
final selectedFn = switchFunctions.firstWhere((f) => f.code == selectedFunction);
|
||||||
switchFunctions.firstWhere((f) => f.code == selectedFunction);
|
|
||||||
final values = selectedFn.getOperationalValues();
|
final values = selectedFn.getOperationalValues();
|
||||||
|
|
||||||
return _buildOperationalValuesList(
|
return _buildOperationalValuesList(
|
||||||
@@ -266,8 +272,7 @@ class ThreeGangSwitchHelper {
|
|||||||
minHeight: 40.0,
|
minHeight: 40.0,
|
||||||
minWidth: 40.0,
|
minWidth: 40.0,
|
||||||
),
|
),
|
||||||
isSelected:
|
isSelected: conditions.map((c) => c == (currentCondition ?? "==")).toList(),
|
||||||
conditions.map((c) => c == (currentCondition ?? "==")).toList(),
|
|
||||||
children: conditions.map((c) => Text(c)).toList(),
|
children: conditions.map((c) => Text(c)).toList(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -316,10 +321,10 @@ class ThreeGangSwitchHelper {
|
|||||||
value: (initialValue ?? 0).toDouble(),
|
value: (initialValue ?? 0).toDouble(),
|
||||||
min: operationalValues.minValue?.toDouble() ?? 0.0,
|
min: operationalValues.minValue?.toDouble() ?? 0.0,
|
||||||
max: operationalValues.maxValue?.toDouble() ?? 0.0,
|
max: operationalValues.maxValue?.toDouble() ?? 0.0,
|
||||||
divisions: (((operationalValues.maxValue ?? 0) -
|
divisions:
|
||||||
(operationalValues.minValue ?? 0)) /
|
(((operationalValues.maxValue ?? 0) - (operationalValues.minValue ?? 0)) /
|
||||||
(operationalValues.stepValue ?? 1))
|
(operationalValues.stepValue ?? 1))
|
||||||
.round(),
|
.round(),
|
||||||
onChanged: (value) {
|
onChanged: (value) {
|
||||||
context.read<FunctionBloc>().add(
|
context.read<FunctionBloc>().add(
|
||||||
AddFunction(
|
AddFunction(
|
||||||
@@ -369,9 +374,7 @@ class ThreeGangSwitchHelper {
|
|||||||
style: context.textTheme.bodyMedium,
|
style: context.textTheme.bodyMedium,
|
||||||
),
|
),
|
||||||
trailing: Icon(
|
trailing: Icon(
|
||||||
isSelected
|
isSelected ? Icons.radio_button_checked : Icons.radio_button_unchecked,
|
||||||
? Icons.radio_button_checked
|
|
||||||
: Icons.radio_button_unchecked,
|
|
||||||
size: 24,
|
size: 24,
|
||||||
color: isSelected
|
color: isSelected
|
||||||
? ColorsManager.primaryColorWithOpacity
|
? ColorsManager.primaryColorWithOpacity
|
||||||
@@ -387,8 +390,7 @@ class ThreeGangSwitchHelper {
|
|||||||
operationName: operationName,
|
operationName: operationName,
|
||||||
value: value.value,
|
value: value.value,
|
||||||
condition: selectedFunctionData?.condition,
|
condition: selectedFunctionData?.condition,
|
||||||
valueDescription:
|
valueDescription: selectedFunctionData?.valueDescription,
|
||||||
selectedFunctionData?.valueDescription,
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import 'package:syncrow_web/pages/routines/models/gang_switches/base_switch_func
|
|||||||
import 'package:syncrow_web/pages/routines/models/gang_switches/switch_operational_value.dart';
|
import 'package:syncrow_web/pages/routines/models/gang_switches/switch_operational_value.dart';
|
||||||
import 'package:syncrow_web/pages/routines/widgets/dialog_footer.dart';
|
import 'package:syncrow_web/pages/routines/widgets/dialog_footer.dart';
|
||||||
import 'package:syncrow_web/pages/routines/widgets/dialog_header.dart';
|
import 'package:syncrow_web/pages/routines/widgets/dialog_header.dart';
|
||||||
|
import 'package:syncrow_web/pages/routines/widgets/routine_dialogs/helpers/routine_tap_function_helper.dart';
|
||||||
import 'package:syncrow_web/utils/color_manager.dart';
|
import 'package:syncrow_web/utils/color_manager.dart';
|
||||||
import 'package:syncrow_web/utils/extension/build_context_x.dart';
|
import 'package:syncrow_web/utils/extension/build_context_x.dart';
|
||||||
|
|
||||||
@@ -85,15 +86,20 @@ class TwoGangSwitchHelper {
|
|||||||
size: 16,
|
size: 16,
|
||||||
color: ColorsManager.textGray,
|
color: ColorsManager.textGray,
|
||||||
),
|
),
|
||||||
onTap: () {
|
onTap: () =>
|
||||||
context
|
RoutineTapFunctionHelper.onTapFunction(
|
||||||
.read<FunctionBloc>()
|
context,
|
||||||
.add(SelectFunction(
|
functionCode: function.code,
|
||||||
functionCode: function.code,
|
functionOperationName:
|
||||||
operationName:
|
function.operationName,
|
||||||
function.operationName,
|
functionValueDescription:
|
||||||
));
|
selectedFunctionData.valueDescription,
|
||||||
},
|
deviceUuid: device?.uuid,
|
||||||
|
codesToAddIntoFunctionsWithDefaultValue: [
|
||||||
|
'countdown_1',
|
||||||
|
'countdown_2',
|
||||||
|
],
|
||||||
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
@@ -167,8 +173,7 @@ class TwoGangSwitchHelper {
|
|||||||
required String operationName,
|
required String operationName,
|
||||||
required bool removeComparetors,
|
required bool removeComparetors,
|
||||||
}) {
|
}) {
|
||||||
if (selectedFunction == 'countdown_1' ||
|
if (selectedFunction == 'countdown_1' || selectedFunction == 'countdown_2') {
|
||||||
selectedFunction == 'countdown_2') {
|
|
||||||
final initialValue = selectedFunctionData?.value ?? 0;
|
final initialValue = selectedFunctionData?.value ?? 0;
|
||||||
return _buildTemperatureSelector(
|
return _buildTemperatureSelector(
|
||||||
context: context,
|
context: context,
|
||||||
@@ -182,8 +187,7 @@ class TwoGangSwitchHelper {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
final selectedFn =
|
final selectedFn = switchFunctions.firstWhere((f) => f.code == selectedFunction);
|
||||||
switchFunctions.firstWhere((f) => f.code == selectedFunction);
|
|
||||||
final values = selectedFn.getOperationalValues();
|
final values = selectedFn.getOperationalValues();
|
||||||
|
|
||||||
return _buildOperationalValuesList(
|
return _buildOperationalValuesList(
|
||||||
@@ -265,8 +269,7 @@ class TwoGangSwitchHelper {
|
|||||||
minHeight: 40.0,
|
minHeight: 40.0,
|
||||||
minWidth: 40.0,
|
minWidth: 40.0,
|
||||||
),
|
),
|
||||||
isSelected:
|
isSelected: conditions.map((c) => c == (currentCondition ?? "==")).toList(),
|
||||||
conditions.map((c) => c == (currentCondition ?? "==")).toList(),
|
|
||||||
children: conditions.map((c) => Text(c)).toList(),
|
children: conditions.map((c) => Text(c)).toList(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -315,10 +318,10 @@ class TwoGangSwitchHelper {
|
|||||||
value: (initialValue ?? 0).toDouble(),
|
value: (initialValue ?? 0).toDouble(),
|
||||||
min: operationalValues.minValue?.toDouble() ?? 0.0,
|
min: operationalValues.minValue?.toDouble() ?? 0.0,
|
||||||
max: operationalValues.maxValue?.toDouble() ?? 0.0,
|
max: operationalValues.maxValue?.toDouble() ?? 0.0,
|
||||||
divisions: (((operationalValues.maxValue ?? 0) -
|
divisions:
|
||||||
(operationalValues.minValue ?? 0)) /
|
(((operationalValues.maxValue ?? 0) - (operationalValues.minValue ?? 0)) /
|
||||||
(operationalValues.stepValue ?? 1))
|
(operationalValues.stepValue ?? 1))
|
||||||
.round(),
|
.round(),
|
||||||
onChanged: (value) {
|
onChanged: (value) {
|
||||||
context.read<FunctionBloc>().add(
|
context.read<FunctionBloc>().add(
|
||||||
AddFunction(
|
AddFunction(
|
||||||
@@ -368,9 +371,7 @@ class TwoGangSwitchHelper {
|
|||||||
style: context.textTheme.bodyMedium,
|
style: context.textTheme.bodyMedium,
|
||||||
),
|
),
|
||||||
trailing: Icon(
|
trailing: Icon(
|
||||||
isSelected
|
isSelected ? Icons.radio_button_checked : Icons.radio_button_unchecked,
|
||||||
? Icons.radio_button_checked
|
|
||||||
: Icons.radio_button_unchecked,
|
|
||||||
size: 24,
|
size: 24,
|
||||||
color: isSelected
|
color: isSelected
|
||||||
? ColorsManager.primaryColorWithOpacity
|
? ColorsManager.primaryColorWithOpacity
|
||||||
@@ -386,8 +387,7 @@ class TwoGangSwitchHelper {
|
|||||||
operationName: operationName,
|
operationName: operationName,
|
||||||
value: value.value,
|
value: value.value,
|
||||||
condition: selectedFunctionData?.condition,
|
condition: selectedFunctionData?.condition,
|
||||||
valueDescription:
|
valueDescription: selectedFunctionData?.valueDescription,
|
||||||
selectedFunctionData?.valueDescription,
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import 'package:syncrow_web/pages/routines/models/device_functions.dart';
|
|||||||
import 'package:syncrow_web/pages/routines/models/wps/wps_functions.dart';
|
import 'package:syncrow_web/pages/routines/models/wps/wps_functions.dart';
|
||||||
import 'package:syncrow_web/pages/routines/widgets/dialog_footer.dart';
|
import 'package:syncrow_web/pages/routines/widgets/dialog_footer.dart';
|
||||||
import 'package:syncrow_web/pages/routines/widgets/dialog_header.dart';
|
import 'package:syncrow_web/pages/routines/widgets/dialog_header.dart';
|
||||||
|
import 'package:syncrow_web/pages/routines/widgets/routine_dialogs/helpers/routine_tap_function_helper.dart';
|
||||||
import 'package:syncrow_web/pages/routines/widgets/routine_dialogs/wall_sensor/wps_value_selector_widget.dart';
|
import 'package:syncrow_web/pages/routines/widgets/routine_dialogs/wall_sensor/wps_value_selector_widget.dart';
|
||||||
import 'package:syncrow_web/utils/color_manager.dart';
|
import 'package:syncrow_web/utils/color_manager.dart';
|
||||||
import 'package:syncrow_web/utils/extension/build_context_x.dart';
|
import 'package:syncrow_web/utils/extension/build_context_x.dart';
|
||||||
@@ -111,13 +112,23 @@ class _WallPresenceSensorState extends State<WallPresenceSensor> {
|
|||||||
return Row(
|
return Row(
|
||||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||||
children: [
|
children: [
|
||||||
_buildFunctionList(context),
|
_buildFunctionList(context, state),
|
||||||
if (state.selectedFunction != null) _buildValueSelector(context, state),
|
if (state.selectedFunction != null) _buildValueSelector(context, state),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildFunctionList(BuildContext context) {
|
Widget _buildFunctionList(BuildContext context, FunctionBlocState state) {
|
||||||
|
final selectedFunction = state.selectedFunction;
|
||||||
|
final selectedFunctionData = state.addedFunctions.firstWhere(
|
||||||
|
(f) => f.functionCode == selectedFunction,
|
||||||
|
orElse: () => DeviceFunctionData(
|
||||||
|
entityId: '',
|
||||||
|
functionCode: selectedFunction ?? '',
|
||||||
|
operationName: '',
|
||||||
|
value: null,
|
||||||
|
),
|
||||||
|
);
|
||||||
return SizedBox(
|
return SizedBox(
|
||||||
width: 360,
|
width: 360,
|
||||||
child: ListView.separated(
|
child: ListView.separated(
|
||||||
@@ -149,12 +160,18 @@ class _WallPresenceSensorState extends State<WallPresenceSensor> {
|
|||||||
size: 16,
|
size: 16,
|
||||||
color: ColorsManager.textGray,
|
color: ColorsManager.textGray,
|
||||||
),
|
),
|
||||||
onTap: () => context.read<FunctionBloc>().add(
|
onTap: () => RoutineTapFunctionHelper.onTapFunction(
|
||||||
SelectFunction(
|
context,
|
||||||
functionCode: function.code,
|
functionCode: function.code,
|
||||||
operationName: function.operationName,
|
functionOperationName: function.operationName,
|
||||||
),
|
functionValueDescription: selectedFunctionData.valueDescription,
|
||||||
),
|
deviceUuid: widget.device?.uuid,
|
||||||
|
codesToAddIntoFunctionsWithDefaultValue: [
|
||||||
|
'dis_current',
|
||||||
|
'presence_time',
|
||||||
|
'illuminance_value',
|
||||||
|
],
|
||||||
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -17,7 +17,15 @@ import 'package:syncrow_web/utils/style.dart';
|
|||||||
class SpaceTreeView extends StatefulWidget {
|
class SpaceTreeView extends StatefulWidget {
|
||||||
final bool? isSide;
|
final bool? isSide;
|
||||||
final Function onSelect;
|
final Function onSelect;
|
||||||
const SpaceTreeView({required this.onSelect, this.isSide, super.key});
|
final bool shouldDisableDeselectingChildrenOfSelectedParent;
|
||||||
|
final Widget? title;
|
||||||
|
const SpaceTreeView({
|
||||||
|
required this.onSelect,
|
||||||
|
this.isSide,
|
||||||
|
super.key,
|
||||||
|
this.shouldDisableDeselectingChildrenOfSelectedParent = false,
|
||||||
|
this.title,
|
||||||
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<SpaceTreeView> createState() => _SpaceTreeViewState();
|
State<SpaceTreeView> createState() => _SpaceTreeViewState();
|
||||||
@@ -41,17 +49,31 @@ class _SpaceTreeViewState extends State<SpaceTreeView> {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return BlocBuilder<SpaceTreeBloc, SpaceTreeState>(builder: (context, state) {
|
return BlocBuilder<SpaceTreeBloc, SpaceTreeState>(builder: (context, state) {
|
||||||
final communities =
|
final communities = state.searchQuery.isNotEmpty
|
||||||
state.searchQuery.isNotEmpty ? state.filteredCommunity : state.communityList;
|
? state.filteredCommunity
|
||||||
|
: state.communityList;
|
||||||
return Container(
|
return Container(
|
||||||
height: MediaQuery.sizeOf(context).height,
|
height: MediaQuery.sizeOf(context).height,
|
||||||
decoration: widget.isSide == true
|
decoration: widget.isSide == true
|
||||||
? subSectionContainerDecoration.copyWith(color: ColorsManager.whiteColors)
|
? subSectionContainerDecoration.copyWith(
|
||||||
|
color: ColorsManager.whiteColors)
|
||||||
: const BoxDecoration(color: ColorsManager.whiteColors),
|
: const BoxDecoration(color: ColorsManager.whiteColors),
|
||||||
child: state is SpaceTreeLoadingState
|
child: state is SpaceTreeLoadingState
|
||||||
? const Center(child: CircularProgressIndicator())
|
? const Center(child: CircularProgressIndicator())
|
||||||
: Column(
|
: Column(
|
||||||
children: [
|
children: [
|
||||||
|
if (widget.title != null)
|
||||||
|
Container(
|
||||||
|
alignment: AlignmentDirectional.centerStart,
|
||||||
|
padding: const EdgeInsets.all(24),
|
||||||
|
child: DefaultTextStyle(
|
||||||
|
style: context.textTheme.titleMedium!.copyWith(
|
||||||
|
color: ColorsManager.blackColor,
|
||||||
|
fontSize: 20,
|
||||||
|
),
|
||||||
|
child: widget.title!,
|
||||||
|
),
|
||||||
|
),
|
||||||
if (widget.isSide == true)
|
if (widget.isSide == true)
|
||||||
Container(
|
Container(
|
||||||
decoration: const BoxDecoration(
|
decoration: const BoxDecoration(
|
||||||
@@ -79,10 +101,12 @@ class _SpaceTreeViewState extends State<SpaceTreeView> {
|
|||||||
style: context.textTheme.bodyMedium?.copyWith(
|
style: context.textTheme.bodyMedium?.copyWith(
|
||||||
color: ColorsManager.blackColor,
|
color: ColorsManager.blackColor,
|
||||||
),
|
),
|
||||||
onChanged: (value) => context.read<SpaceTreeBloc>().add(
|
onChanged: (value) =>
|
||||||
SearchQueryEvent(value),
|
context.read<SpaceTreeBloc>().add(
|
||||||
),
|
SearchQueryEvent(value),
|
||||||
decoration: textBoxDecoration(radios: 20)?.copyWith(
|
),
|
||||||
|
decoration:
|
||||||
|
textBoxDecoration(radios: 20)?.copyWith(
|
||||||
fillColor: Colors.white,
|
fillColor: Colors.white,
|
||||||
suffixIcon: Padding(
|
suffixIcon: Padding(
|
||||||
padding: const EdgeInsets.only(right: 16),
|
padding: const EdgeInsets.only(right: 16),
|
||||||
@@ -92,7 +116,8 @@ class _SpaceTreeViewState extends State<SpaceTreeView> {
|
|||||||
height: 24,
|
height: 24,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
hintStyle: context.textTheme.bodyMedium?.copyWith(
|
hintStyle:
|
||||||
|
context.textTheme.bodyMedium?.copyWith(
|
||||||
fontWeight: FontWeight.w400,
|
fontWeight: FontWeight.w400,
|
||||||
fontSize: 12,
|
fontSize: 12,
|
||||||
color: ColorsManager.textGray,
|
color: ColorsManager.textGray,
|
||||||
@@ -131,15 +156,16 @@ class _SpaceTreeViewState extends State<SpaceTreeView> {
|
|||||||
itemBuilder: (context, index) {
|
itemBuilder: (context, index) {
|
||||||
return CustomExpansionTileSpaceTree(
|
return CustomExpansionTileSpaceTree(
|
||||||
title: communities[index].name,
|
title: communities[index].name,
|
||||||
isSelected:
|
isSelected: state.selectedCommunities
|
||||||
state.selectedCommunities.contains(communities[index].uuid),
|
.contains(communities[index].uuid),
|
||||||
isSoldCheck:
|
isSoldCheck: state.selectedCommunities
|
||||||
state.selectedCommunities.contains(communities[index].uuid),
|
.contains(communities[index].uuid),
|
||||||
onExpansionChanged: () => context.read<SpaceTreeBloc>().add(
|
onExpansionChanged: () =>
|
||||||
OnCommunityExpanded(
|
context.read<SpaceTreeBloc>().add(
|
||||||
communities[index].uuid,
|
OnCommunityExpanded(
|
||||||
),
|
communities[index].uuid,
|
||||||
),
|
),
|
||||||
|
),
|
||||||
isExpanded: state.expandedCommunities.contains(
|
isExpanded: state.expandedCommunities.contains(
|
||||||
communities[index].uuid,
|
communities[index].uuid,
|
||||||
),
|
),
|
||||||
@@ -156,8 +182,19 @@ class _SpaceTreeViewState extends State<SpaceTreeView> {
|
|||||||
(space) {
|
(space) {
|
||||||
return CustomExpansionTileSpaceTree(
|
return CustomExpansionTileSpaceTree(
|
||||||
title: space.name,
|
title: space.name,
|
||||||
isExpanded: state.expandedSpaces.contains(space.uuid),
|
isExpanded:
|
||||||
|
state.expandedSpaces.contains(space.uuid),
|
||||||
onItemSelected: () {
|
onItemSelected: () {
|
||||||
|
final isParentSelected = _isParentSelected(
|
||||||
|
state,
|
||||||
|
communities[index],
|
||||||
|
space,
|
||||||
|
);
|
||||||
|
if (widget
|
||||||
|
.shouldDisableDeselectingChildrenOfSelectedParent &&
|
||||||
|
isParentSelected) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
context.read<SpaceTreeBloc>().add(
|
context.read<SpaceTreeBloc>().add(
|
||||||
OnSpaceSelected(
|
OnSpaceSelected(
|
||||||
communities[index],
|
communities[index],
|
||||||
@@ -167,15 +204,18 @@ class _SpaceTreeViewState extends State<SpaceTreeView> {
|
|||||||
);
|
);
|
||||||
widget.onSelect();
|
widget.onSelect();
|
||||||
},
|
},
|
||||||
onExpansionChanged: () => context.read<SpaceTreeBloc>().add(
|
onExpansionChanged: () =>
|
||||||
OnSpaceExpanded(
|
context.read<SpaceTreeBloc>().add(
|
||||||
communities[index].uuid,
|
OnSpaceExpanded(
|
||||||
space.uuid ?? '',
|
communities[index].uuid,
|
||||||
),
|
space.uuid ?? '',
|
||||||
),
|
),
|
||||||
isSelected: state.selectedSpaces.contains(space.uuid) ||
|
),
|
||||||
|
isSelected: state.selectedSpaces
|
||||||
|
.contains(space.uuid) ||
|
||||||
|
state.soldCheck.contains(space.uuid),
|
||||||
|
isSoldCheck:
|
||||||
state.soldCheck.contains(space.uuid),
|
state.soldCheck.contains(space.uuid),
|
||||||
isSoldCheck: state.soldCheck.contains(space.uuid),
|
|
||||||
children: _buildNestedSpaces(
|
children: _buildNestedSpaces(
|
||||||
context,
|
context,
|
||||||
state,
|
state,
|
||||||
@@ -196,6 +236,13 @@ class _SpaceTreeViewState extends State<SpaceTreeView> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool _isParentSelected(
|
||||||
|
SpaceTreeState state, CommunityModel community, SpaceModel space) {
|
||||||
|
return state.selectedCommunities.contains(community.uuid) ||
|
||||||
|
(space.spaceModel?.uuid != null &&
|
||||||
|
state.selectedSpaces.contains(space.spaceModel?.uuid));
|
||||||
|
}
|
||||||
|
|
||||||
List<Widget> _buildNestedSpaces(
|
List<Widget> _buildNestedSpaces(
|
||||||
BuildContext context,
|
BuildContext context,
|
||||||
SpaceTreeState state,
|
SpaceTreeState state,
|
||||||
@@ -204,8 +251,8 @@ class _SpaceTreeViewState extends State<SpaceTreeView> {
|
|||||||
) {
|
) {
|
||||||
return space.children.map((child) {
|
return space.children.map((child) {
|
||||||
return CustomExpansionTileSpaceTree(
|
return CustomExpansionTileSpaceTree(
|
||||||
isSelected:
|
isSelected: state.selectedSpaces.contains(child.uuid) ||
|
||||||
state.selectedSpaces.contains(child.uuid) || state.soldCheck.contains(child.uuid),
|
state.soldCheck.contains(child.uuid),
|
||||||
isSoldCheck: state.soldCheck.contains(child.uuid),
|
isSoldCheck: state.soldCheck.contains(child.uuid),
|
||||||
title: child.name,
|
title: child.name,
|
||||||
isExpanded: state.expandedSpaces.contains(child.uuid),
|
isExpanded: state.expandedSpaces.contains(child.uuid),
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import 'package:flutter_bloc/flutter_bloc.dart';
|
|||||||
import 'package:syncrow_web/pages/common/bloc/project_manager.dart';
|
import 'package:syncrow_web/pages/common/bloc/project_manager.dart';
|
||||||
import 'package:syncrow_web/pages/space_tree/bloc/space_tree_bloc.dart';
|
import 'package:syncrow_web/pages/space_tree/bloc/space_tree_bloc.dart';
|
||||||
import 'package:syncrow_web/pages/space_tree/bloc/space_tree_event.dart';
|
import 'package:syncrow_web/pages/space_tree/bloc/space_tree_event.dart';
|
||||||
|
import 'package:syncrow_web/pages/space_tree/bloc/space_tree_state.dart';
|
||||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/community_model.dart';
|
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/community_model.dart';
|
||||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/create_subspace_model.dart';
|
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/create_subspace_model.dart';
|
||||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/product_model.dart';
|
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/product_model.dart';
|
||||||
@@ -246,7 +247,9 @@ class SpaceManagementBloc extends Bloc<SpaceManagementEvent, SpaceManagementStat
|
|||||||
final previousState = state;
|
final previousState = state;
|
||||||
final projectUuid = await ProjectManager.getProjectUUID() ?? '';
|
final projectUuid = await ProjectManager.getProjectUUID() ?? '';
|
||||||
var spaceBloc = event.context.read<SpaceTreeBloc>();
|
var spaceBloc = event.context.read<SpaceTreeBloc>();
|
||||||
List<CommunityModel> communities = await _waitForCommunityList(spaceBloc);
|
var spaceTreeState = event.context.read<SpaceTreeBloc>().state;
|
||||||
|
|
||||||
|
List<CommunityModel> communities = await _waitForCommunityList(spaceBloc, spaceTreeState);
|
||||||
await fetchSpaceModels();
|
await fetchSpaceModels();
|
||||||
await fetchTags();
|
await fetchTags();
|
||||||
|
|
||||||
@@ -277,11 +280,13 @@ class SpaceManagementBloc extends Bloc<SpaceManagementEvent, SpaceManagementStat
|
|||||||
LoadCommunityAndSpacesEvent event,
|
LoadCommunityAndSpacesEvent event,
|
||||||
Emitter<SpaceManagementState> emit,
|
Emitter<SpaceManagementState> emit,
|
||||||
) async {
|
) async {
|
||||||
|
var spaceTreeState = event.context.read<SpaceTreeBloc>().state;
|
||||||
var spaceBloc = event.context.read<SpaceTreeBloc>();
|
var spaceBloc = event.context.read<SpaceTreeBloc>();
|
||||||
|
|
||||||
_onloadProducts();
|
_onloadProducts();
|
||||||
await fetchTags();
|
await fetchTags();
|
||||||
// Wait until `communityList` is loaded
|
// Wait until `communityList` is loaded
|
||||||
List<CommunityModel> communities = await _waitForCommunityList(spaceBloc);
|
List<CommunityModel> communities = await _waitForCommunityList(spaceBloc, spaceTreeState);
|
||||||
|
|
||||||
// Fetch space models after communities are available
|
// Fetch space models after communities are available
|
||||||
final prevSpaceModels = await fetchSpaceModels();
|
final prevSpaceModels = await fetchSpaceModels();
|
||||||
@@ -292,23 +297,38 @@ class SpaceManagementBloc extends Bloc<SpaceManagementEvent, SpaceManagementStat
|
|||||||
allTags: _cachedTags ?? []));
|
allTags: _cachedTags ?? []));
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<List<CommunityModel>> _waitForCommunityList(SpaceTreeBloc spaceBloc) async {
|
Future<List<CommunityModel>> _waitForCommunityList(
|
||||||
|
SpaceTreeBloc spaceBloc, SpaceTreeState spaceTreeState) async {
|
||||||
// Check if communityList is already populated
|
// Check if communityList is already populated
|
||||||
if (spaceBloc.state.communityList.isNotEmpty) {
|
final filteredCommunities = spaceTreeState.searchQuery.isNotEmpty
|
||||||
return spaceBloc.state.communityList;
|
? spaceTreeState.filteredCommunity
|
||||||
|
: spaceTreeState.communityList;
|
||||||
|
if (filteredCommunities.isNotEmpty) {
|
||||||
|
return filteredCommunities;
|
||||||
}
|
}
|
||||||
|
|
||||||
final completer = Completer<List<CommunityModel>>();
|
final completer = Completer<List<CommunityModel>>();
|
||||||
final subscription = spaceBloc.stream.listen((state) {
|
final subscription = spaceBloc.stream.listen((state) {
|
||||||
if (state.communityList.isNotEmpty) {
|
if (!completer.isCompleted && state.communityList.isNotEmpty) {
|
||||||
completer.complete(state.communityList);
|
completer
|
||||||
|
.complete(state.searchQuery.isNotEmpty ? state.filteredCommunity : state.communityList);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
try {
|
||||||
|
final communities = await completer.future.timeout(
|
||||||
|
const Duration(seconds: 10),
|
||||||
|
onTimeout: () {
|
||||||
|
if (!completer.isCompleted) {
|
||||||
|
completer.complete([]);
|
||||||
|
}
|
||||||
|
return [];
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
// Return the list once available, then cancel the listener
|
return communities;
|
||||||
final communities = await completer.future;
|
} finally {
|
||||||
await subscription.cancel();
|
await subscription.cancel();
|
||||||
return communities;
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void _onCommunityDelete(
|
void _onCommunityDelete(
|
||||||
@@ -446,6 +466,7 @@ class SpaceManagementBloc extends Bloc<SpaceManagementEvent, SpaceManagementStat
|
|||||||
|
|
||||||
if (previousState is SpaceManagementLoaded) {
|
if (previousState is SpaceManagementLoaded) {
|
||||||
await _updateLoadedState(
|
await _updateLoadedState(
|
||||||
|
event.context,
|
||||||
previousState,
|
previousState,
|
||||||
allSpaces,
|
allSpaces,
|
||||||
event.communityUuid,
|
event.communityUuid,
|
||||||
@@ -462,6 +483,7 @@ class SpaceManagementBloc extends Bloc<SpaceManagementEvent, SpaceManagementStat
|
|||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _updateLoadedState(
|
Future<void> _updateLoadedState(
|
||||||
|
BuildContext context,
|
||||||
SpaceManagementLoaded previousState,
|
SpaceManagementLoaded previousState,
|
||||||
List<SpaceModel> allSpaces,
|
List<SpaceModel> allSpaces,
|
||||||
String communityUuid,
|
String communityUuid,
|
||||||
@@ -469,7 +491,10 @@ class SpaceManagementBloc extends Bloc<SpaceManagementEvent, SpaceManagementStat
|
|||||||
) async {
|
) async {
|
||||||
var prevSpaceModels = await fetchSpaceModels();
|
var prevSpaceModels = await fetchSpaceModels();
|
||||||
await fetchTags();
|
await fetchTags();
|
||||||
final communities = List<CommunityModel>.from(previousState.communities);
|
final spaceTreeState = context.read<SpaceTreeBloc>().state;
|
||||||
|
final communities = spaceTreeState.searchQuery.isNotEmpty
|
||||||
|
? spaceTreeState.filteredCommunity
|
||||||
|
: spaceTreeState.communityList;
|
||||||
|
|
||||||
for (var community in communities) {
|
for (var community in communities) {
|
||||||
if (community.uuid == communityUuid) {
|
if (community.uuid == communityUuid) {
|
||||||
@@ -484,6 +509,8 @@ class SpaceManagementBloc extends Bloc<SpaceManagementEvent, SpaceManagementStat
|
|||||||
spaceModels: prevSpaceModels,
|
spaceModels: prevSpaceModels,
|
||||||
allTags: _cachedTags ?? []));
|
allTags: _cachedTags ?? []));
|
||||||
return;
|
return;
|
||||||
|
} else {
|
||||||
|
print("Community not found");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -491,12 +518,21 @@ class SpaceManagementBloc extends Bloc<SpaceManagementEvent, SpaceManagementStat
|
|||||||
Future<List<SpaceModel>> saveSpacesHierarchically(
|
Future<List<SpaceModel>> saveSpacesHierarchically(
|
||||||
BuildContext context, List<SpaceModel> spaces, String communityUuid) async {
|
BuildContext context, List<SpaceModel> spaces, String communityUuid) async {
|
||||||
final orderedSpaces = flattenHierarchy(spaces);
|
final orderedSpaces = flattenHierarchy(spaces);
|
||||||
|
|
||||||
final projectUuid = await ProjectManager.getProjectUUID() ?? '';
|
final projectUuid = await ProjectManager.getProjectUUID() ?? '';
|
||||||
var spaceBloc = context.read<SpaceTreeBloc>();
|
CommunityModel? selectedCommunity;
|
||||||
List<CommunityModel> communities = spaceBloc.state.communityList;
|
try {
|
||||||
CommunityModel? selectedCommunity = communities.firstWhere(
|
final spaceTreeState = context.read<SpaceTreeBloc>().state;
|
||||||
(community) => community.uuid == communityUuid,
|
final filteredCommunities = spaceTreeState.searchQuery.isNotEmpty
|
||||||
);
|
? spaceTreeState.filteredCommunity
|
||||||
|
: spaceTreeState.communityList;
|
||||||
|
|
||||||
|
selectedCommunity = filteredCommunities.firstWhere(
|
||||||
|
(community) => community.uuid == communityUuid,
|
||||||
|
);
|
||||||
|
} catch (e) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
final parentsToDelete = orderedSpaces.where((space) =>
|
final parentsToDelete = orderedSpaces.where((space) =>
|
||||||
space.status == SpaceStatus.deleted &&
|
space.status == SpaceStatus.deleted &&
|
||||||
@@ -605,11 +641,11 @@ class SpaceManagementBloc extends Bloc<SpaceManagementEvent, SpaceManagementStat
|
|||||||
projectId: projectUuid);
|
projectId: projectUuid);
|
||||||
} else {
|
} else {
|
||||||
// Call create if the space does not have a UUID
|
// Call create if the space does not have a UUID
|
||||||
final List<CreateTagBodyModel> tagBodyModels = space.tags != null
|
List<CreateTagBodyModel> tagBodyModels = space.tags != null
|
||||||
? space.tags!.map((tag) => tag.toCreateTagBodyModel()).toList()
|
? space.tags!.map((tag) => tag.toCreateTagBodyModel()).toList()
|
||||||
: [];
|
: [];
|
||||||
|
|
||||||
final createSubspaceBodyModels = space.subspaces?.map((subspace) {
|
var createSubspaceBodyModels = space.subspaces?.map((subspace) {
|
||||||
final tagBodyModels =
|
final tagBodyModels =
|
||||||
subspace.tags?.map((tag) => tag.toCreateTagBodyModel()).toList() ?? [];
|
subspace.tags?.map((tag) => tag.toCreateTagBodyModel()).toList() ?? [];
|
||||||
return CreateSubspaceModel()
|
return CreateSubspaceModel()
|
||||||
@@ -618,6 +654,11 @@ class SpaceManagementBloc extends Bloc<SpaceManagementEvent, SpaceManagementStat
|
|||||||
}).toList() ??
|
}).toList() ??
|
||||||
[];
|
[];
|
||||||
|
|
||||||
|
if (space.spaceModel?.uuid != null) {
|
||||||
|
tagBodyModels = [];
|
||||||
|
createSubspaceBodyModels = [];
|
||||||
|
}
|
||||||
|
|
||||||
final response = await _api.createSpace(
|
final response = await _api.createSpace(
|
||||||
communityId: communityUuid,
|
communityId: communityUuid,
|
||||||
name: space.name,
|
name: space.name,
|
||||||
@@ -669,9 +710,12 @@ class SpaceManagementBloc extends Bloc<SpaceManagementEvent, SpaceManagementStat
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
await fetchTags();
|
await fetchTags();
|
||||||
final projectUuid = await ProjectManager.getProjectUUID() ?? '';
|
final spaceTreeState = event.context.read<SpaceTreeBloc>().state;
|
||||||
var spaceBloc = event.context.read<SpaceTreeBloc>();
|
final filteredCommunities = spaceTreeState.searchQuery.isNotEmpty
|
||||||
List<CommunityModel> communities = spaceBloc.state.communityList;
|
? spaceTreeState.filteredCommunity
|
||||||
|
: spaceTreeState.communityList;
|
||||||
|
|
||||||
|
List<CommunityModel> communities = filteredCommunities;
|
||||||
|
|
||||||
var prevSpaceModels = await fetchSpaceModels();
|
var prevSpaceModels = await fetchSpaceModels();
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
// Flutter imports
|
// Flutter imports
|
||||||
|
import 'dart:math';
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
|
|
||||||
@@ -70,6 +72,8 @@ class _CommunityStructureAreaState extends State<CommunityStructureArea> {
|
|||||||
_nameController = TextEditingController(
|
_nameController = TextEditingController(
|
||||||
text: widget.selectedCommunity?.name ?? '',
|
text: widget.selectedCommunity?.name ?? '',
|
||||||
);
|
);
|
||||||
|
realignTree();
|
||||||
|
|
||||||
_transformationController = TransformationController();
|
_transformationController = TransformationController();
|
||||||
if (widget.selectedSpace != null) {
|
if (widget.selectedSpace != null) {
|
||||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||||
@@ -94,6 +98,7 @@ class _CommunityStructureAreaState extends State<CommunityStructureArea> {
|
|||||||
spaces = widget.spaces.isNotEmpty ? flattenSpaces(widget.spaces) : [];
|
spaces = widget.spaces.isNotEmpty ? flattenSpaces(widget.spaces) : [];
|
||||||
connections = widget.spaces.isNotEmpty ? createConnections(widget.spaces) : [];
|
connections = widget.spaces.isNotEmpty ? createConnections(widget.spaces) : [];
|
||||||
_adjustCanvasSizeForSpaces();
|
_adjustCanvasSizeForSpaces();
|
||||||
|
realignTree();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -336,6 +341,7 @@ class _CommunityStructureAreaState extends State<CommunityStructureArea> {
|
|||||||
}
|
}
|
||||||
spaces.add(newSpace);
|
spaces.add(newSpace);
|
||||||
_updateNodePosition(newSpace, newSpace.position);
|
_updateNodePosition(newSpace, newSpace.position);
|
||||||
|
realignTree();
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
@@ -401,12 +407,15 @@ class _CommunityStructureAreaState extends State<CommunityStructureArea> {
|
|||||||
|
|
||||||
List<SpaceModel> flattenSpaces(List<SpaceModel> spaces) {
|
List<SpaceModel> flattenSpaces(List<SpaceModel> spaces) {
|
||||||
List<SpaceModel> result = [];
|
List<SpaceModel> result = [];
|
||||||
|
Map<String, SpaceModel> idToSpace = {};
|
||||||
|
|
||||||
void flatten(SpaceModel space) {
|
void flatten(SpaceModel space) {
|
||||||
if (space.status == SpaceStatus.deleted || space.status == SpaceStatus.parentDeleted) {
|
if (space.status == SpaceStatus.deleted || space.status == SpaceStatus.parentDeleted) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
result.add(space);
|
result.add(space);
|
||||||
|
idToSpace[space.internalId] = space;
|
||||||
|
|
||||||
for (var child in space.children) {
|
for (var child in space.children) {
|
||||||
flatten(child);
|
flatten(child);
|
||||||
}
|
}
|
||||||
@@ -416,6 +425,12 @@ class _CommunityStructureAreaState extends State<CommunityStructureArea> {
|
|||||||
flatten(space);
|
flatten(space);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (var space in result) {
|
||||||
|
if (space.parent != null) {
|
||||||
|
space.parent = idToSpace[space.parent!.internalId];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -450,7 +465,6 @@ class _CommunityStructureAreaState extends State<CommunityStructureArea> {
|
|||||||
|
|
||||||
void _saveSpaces() {
|
void _saveSpaces() {
|
||||||
if (widget.selectedCommunity == null) {
|
if (widget.selectedCommunity == null) {
|
||||||
debugPrint("No community selected for saving spaces.");
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -465,7 +479,6 @@ class _CommunityStructureAreaState extends State<CommunityStructureArea> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
String communityUuid = widget.selectedCommunity!.uuid;
|
String communityUuid = widget.selectedCommunity!.uuid;
|
||||||
|
|
||||||
context.read<SpaceManagementBloc>().add(SaveSpacesEvent(
|
context.read<SpaceManagementBloc>().add(SaveSpacesEvent(
|
||||||
context,
|
context,
|
||||||
spaces: spacesToSave,
|
spaces: spacesToSave,
|
||||||
@@ -530,35 +543,83 @@ class _CommunityStructureAreaState extends State<CommunityStructureArea> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Offset getBalancedChildPosition(SpaceModel parent) {
|
Offset getBalancedChildPosition(SpaceModel parent) {
|
||||||
int totalSiblings = parent.children.length + 1;
|
const double nodeWidth = 200;
|
||||||
double totalWidth = (totalSiblings - 1) * 250; // Horizontal spacing
|
const double verticalGap = 180;
|
||||||
double startX = parent.position.dx - (totalWidth / 2);
|
|
||||||
|
|
||||||
Offset position = Offset(startX + (parent.children.length * 250), parent.position.dy + 180);
|
if (parent.children.isEmpty) {
|
||||||
|
// First child → exactly center
|
||||||
|
return Offset(parent.position.dx, parent.position.dy + verticalGap);
|
||||||
|
} else {
|
||||||
|
// More children → arrange them spaced horizontally
|
||||||
|
double totalWidth = (parent.children.length) * (nodeWidth + 60);
|
||||||
|
double startX = parent.position.dx - (totalWidth / 2);
|
||||||
|
|
||||||
// Check for overlaps & adjust
|
double childX = startX + (parent.children.length * (nodeWidth + 60));
|
||||||
while (spaces.any((s) => (s.position - position).distance < 250)) {
|
return Offset(childX, parent.position.dy + verticalGap);
|
||||||
position = Offset(position.dx + 250, position.dy);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return position;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void realignTree() {
|
void realignTree() {
|
||||||
void updatePositions(SpaceModel node, double x, double y) {
|
const double nodeWidth = 200;
|
||||||
node.position = Offset(x, y);
|
const double nodeHeight = 100;
|
||||||
|
const double horizontalGap = 60;
|
||||||
|
const double verticalGap = 180;
|
||||||
|
const double rootGap = 400; // extra space between different roots
|
||||||
|
|
||||||
int numChildren = node.children.length;
|
double canvasRightEdge = 1000;
|
||||||
double childStartX = x - ((numChildren - 1) * 250) / 2;
|
double canvasBottomEdge = 1000;
|
||||||
|
|
||||||
for (int i = 0; i < numChildren; i++) {
|
double calculateSubtreeWidth(SpaceModel node) {
|
||||||
updatePositions(node.children[i], childStartX + (i * 250), y + 180);
|
if (node.children.isEmpty) return nodeWidth;
|
||||||
|
double totalWidth = 0;
|
||||||
|
for (var child in node.children) {
|
||||||
|
totalWidth += calculateSubtreeWidth(child) + horizontalGap;
|
||||||
|
}
|
||||||
|
return totalWidth - horizontalGap;
|
||||||
|
}
|
||||||
|
|
||||||
|
void layoutSubtree(SpaceModel node, double startX, double y) {
|
||||||
|
double subtreeWidth = calculateSubtreeWidth(node);
|
||||||
|
double centerX = startX + subtreeWidth / 2 - nodeWidth / 2;
|
||||||
|
node.position = Offset(centerX, y);
|
||||||
|
|
||||||
|
canvasRightEdge = max(canvasRightEdge, centerX + nodeWidth);
|
||||||
|
canvasBottomEdge = max(canvasBottomEdge, y + nodeHeight);
|
||||||
|
|
||||||
|
if (node.children.length == 1) {
|
||||||
|
final child = node.children.first;
|
||||||
|
layoutSubtree(child, centerX, y + verticalGap);
|
||||||
|
} else {
|
||||||
|
double childX = startX;
|
||||||
|
for (var child in node.children) {
|
||||||
|
double childWidth = calculateSubtreeWidth(child);
|
||||||
|
layoutSubtree(child, childX, y + verticalGap);
|
||||||
|
childX += childWidth + horizontalGap;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (spaces.isNotEmpty) {
|
// ⚡ New: layout each root separately
|
||||||
updatePositions(spaces.first, spaces.first.position.dx, spaces.first.position.dy);
|
final List<SpaceModel> roots = spaces
|
||||||
|
.where((s) =>
|
||||||
|
s.parent == null &&
|
||||||
|
s.status != SpaceStatus.deleted &&
|
||||||
|
s.status != SpaceStatus.parentDeleted)
|
||||||
|
.toList();
|
||||||
|
|
||||||
|
double currentX = 100; // start some margin from left
|
||||||
|
double currentY = 100; // top margin
|
||||||
|
|
||||||
|
for (var root in roots) {
|
||||||
|
layoutSubtree(root, currentX, currentY);
|
||||||
|
double rootWidth = calculateSubtreeWidth(root);
|
||||||
|
currentX += rootWidth + rootGap;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setState(() {
|
||||||
|
canvasWidth = canvasRightEdge + 400;
|
||||||
|
canvasHeight = canvasBottomEdge + 400;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void _onDuplicate(BuildContext parentContext) {
|
void _onDuplicate(BuildContext parentContext) {
|
||||||
@@ -642,116 +703,72 @@ class _CommunityStructureAreaState extends State<CommunityStructureArea> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void _duplicateSpace(SpaceModel space) {
|
void _duplicateSpace(SpaceModel space) {
|
||||||
final Map<SpaceModel, SpaceModel> originalToDuplicate = {};
|
setState(() {
|
||||||
double horizontalGap = 250.0; // Increased spacing
|
SpaceModel? parent = space.parent;
|
||||||
double verticalGap = 180.0; // Adjusted for better visualization
|
|
||||||
|
|
||||||
print("🟢 Duplicating: ${space.name}");
|
SpaceModel duplicated = _deepCloneSpaceTree(space, parent: parent);
|
||||||
|
|
||||||
/// **Find a new position ensuring no overlap**
|
duplicated.position = Offset(space.position.dx + 300, space.position.dy + 100);
|
||||||
Offset getBalancedChildPosition(SpaceModel parent) {
|
List<SpaceModel> duplicatedSubtree = [];
|
||||||
int totalSiblings = parent.children.length + 1;
|
void collectSubtree(SpaceModel node) {
|
||||||
double totalWidth = (totalSiblings - 1) * horizontalGap;
|
duplicatedSubtree.add(node);
|
||||||
double startX = parent.position.dx - (totalWidth / 2);
|
for (var child in node.children) {
|
||||||
Offset position = Offset(
|
collectSubtree(child);
|
||||||
startX + (parent.children.length * horizontalGap), parent.position.dy + verticalGap);
|
|
||||||
|
|
||||||
// **Check for overlaps & adjust**
|
|
||||||
while (spaces.any((s) => (s.position - position).distance < horizontalGap)) {
|
|
||||||
position = Offset(position.dx + horizontalGap, position.dy);
|
|
||||||
}
|
|
||||||
|
|
||||||
print("🔹 New position for ${parent.name}: (${position.dx}, ${position.dy})");
|
|
||||||
return position;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// **Realign the entire tree after duplication**
|
|
||||||
void realignTree() {
|
|
||||||
void updatePositions(SpaceModel node, double x, double y) {
|
|
||||||
node.position = Offset(x, y);
|
|
||||||
print("✅ Adjusted ${node.name} to (${x}, ${y})");
|
|
||||||
|
|
||||||
int numChildren = node.children.length;
|
|
||||||
double childStartX = x - ((numChildren - 1) * horizontalGap) / 2;
|
|
||||||
|
|
||||||
for (int i = 0; i < numChildren; i++) {
|
|
||||||
updatePositions(node.children[i], childStartX + (i * horizontalGap), y + verticalGap);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (spaces.isNotEmpty) {
|
collectSubtree(duplicated);
|
||||||
print("🔄 Realigning tree...");
|
spaces.addAll(duplicatedSubtree);
|
||||||
updatePositions(spaces.first, spaces.first.position.dx, spaces.first.position.dy);
|
|
||||||
|
if (parent != null) {
|
||||||
|
parent.children.add(duplicated);
|
||||||
|
|
||||||
|
final newConnection = Connection(
|
||||||
|
startSpace: parent,
|
||||||
|
endSpace: duplicated,
|
||||||
|
direction: "down",
|
||||||
|
);
|
||||||
|
connections.add(newConnection);
|
||||||
|
duplicated.incomingConnection = newConnection;
|
||||||
|
parent.addOutgoingConnection(newConnection);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/// **Recursive duplication logic**
|
realignTree();
|
||||||
SpaceModel duplicateRecursive(SpaceModel original, SpaceModel? duplicatedParent) {
|
connections = createConnections(spaces);
|
||||||
Offset newPosition = duplicatedParent == null
|
});
|
||||||
? Offset(original.position.dx + horizontalGap, original.position.dy)
|
}
|
||||||
: getBalancedChildPosition(duplicatedParent);
|
|
||||||
|
|
||||||
final duplicatedName = SpaceHelper.generateUniqueSpaceName(original.name, spaces);
|
SpaceModel _deepCloneSpaceTree(SpaceModel original, {SpaceModel? parent}) {
|
||||||
print(
|
final duplicatedName = SpaceHelper.generateUniqueSpaceName(original.name, spaces);
|
||||||
"🟡 Duplicating ${original.name} → ${duplicatedName} at (${newPosition.dx}, ${newPosition.dy})");
|
|
||||||
|
|
||||||
final duplicated = SpaceModel(
|
final newSpace = SpaceModel(
|
||||||
name: duplicatedName,
|
name: duplicatedName,
|
||||||
icon: original.icon,
|
icon: original.icon,
|
||||||
position: newPosition,
|
position: original.position,
|
||||||
isPrivate: original.isPrivate,
|
isPrivate: original.isPrivate,
|
||||||
children: [],
|
children: [],
|
||||||
status: SpaceStatus.newSpace,
|
status: SpaceStatus.newSpace,
|
||||||
parent: duplicatedParent,
|
spaceModel: original.spaceModel,
|
||||||
spaceModel: original.spaceModel,
|
subspaces: original.subspaces,
|
||||||
subspaces: original.subspaces,
|
tags: original.tags,
|
||||||
tags: original.tags,
|
parent: parent,
|
||||||
|
);
|
||||||
|
|
||||||
|
for (var child in original.children) {
|
||||||
|
final duplicatedChild = _deepCloneSpaceTree(child, parent: newSpace);
|
||||||
|
newSpace.children.add(duplicatedChild);
|
||||||
|
|
||||||
|
final newConnection = Connection(
|
||||||
|
startSpace: newSpace,
|
||||||
|
endSpace: duplicatedChild,
|
||||||
|
direction: "down",
|
||||||
);
|
);
|
||||||
|
connections.add(newConnection);
|
||||||
|
|
||||||
setState(() {
|
duplicatedChild.incomingConnection = newConnection;
|
||||||
spaces.add(duplicated);
|
newSpace.addOutgoingConnection(newConnection);
|
||||||
_updateNodePosition(duplicated, duplicated.position);
|
|
||||||
|
|
||||||
if (duplicatedParent != null) {
|
|
||||||
final newConnection = Connection(
|
|
||||||
startSpace: duplicatedParent,
|
|
||||||
endSpace: duplicated,
|
|
||||||
direction: "down",
|
|
||||||
);
|
|
||||||
connections.add(newConnection);
|
|
||||||
duplicated.incomingConnection = newConnection;
|
|
||||||
duplicatedParent.addOutgoingConnection(newConnection);
|
|
||||||
duplicatedParent.children.add(duplicated);
|
|
||||||
print("🔗 Created connection: ${duplicatedParent.name} → ${duplicated.name}");
|
|
||||||
}
|
|
||||||
|
|
||||||
// **Recalculate the whole tree to avoid overlaps**
|
|
||||||
realignTree();
|
|
||||||
});
|
|
||||||
|
|
||||||
// Recursively duplicate children
|
|
||||||
for (var child in original.children) {
|
|
||||||
duplicateRecursive(child, duplicated);
|
|
||||||
}
|
|
||||||
|
|
||||||
return duplicated;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// **Handle root duplication**
|
return newSpace;
|
||||||
if (space.parent == null) {
|
|
||||||
print("🟠 Duplicating root node: ${space.name}");
|
|
||||||
SpaceModel duplicatedRoot = duplicateRecursive(space, null);
|
|
||||||
|
|
||||||
setState(() {
|
|
||||||
spaces.add(duplicatedRoot);
|
|
||||||
realignTree();
|
|
||||||
});
|
|
||||||
|
|
||||||
print("✅ Root duplication successful: ${duplicatedRoot.name}");
|
|
||||||
} else {
|
|
||||||
duplicateRecursive(space, space.parent);
|
|
||||||
}
|
|
||||||
|
|
||||||
print("🟢 Finished duplication process for: ${space.name}");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
import 'package:syncrow_web/pages/space_tree/bloc/space_tree_bloc.dart';
|
import 'package:syncrow_web/pages/space_tree/bloc/space_tree_bloc.dart';
|
||||||
|
import 'package:syncrow_web/pages/space_tree/bloc/space_tree_event.dart';
|
||||||
import 'package:syncrow_web/pages/space_tree/view/space_tree_view.dart';
|
import 'package:syncrow_web/pages/space_tree/view/space_tree_view.dart';
|
||||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/bloc/space_management_bloc.dart';
|
import 'package:syncrow_web/pages/spaces_management/all_spaces/bloc/space_management_bloc.dart';
|
||||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/bloc/space_management_event.dart';
|
import 'package:syncrow_web/pages/spaces_management/all_spaces/bloc/space_management_event.dart';
|
||||||
@@ -45,7 +46,6 @@ class _LoadedSpaceViewState extends State<LoadedSpaceView> {
|
|||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
|
|
||||||
_spaceModels = List.from(widget.spaceModels ?? []);
|
_spaceModels = List.from(widget.spaceModels ?? []);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -106,9 +106,8 @@ class _LoadedSpaceViewState extends State<LoadedSpaceView> {
|
|||||||
children: [
|
children: [
|
||||||
SidebarWidget(
|
SidebarWidget(
|
||||||
communities: widget.communities,
|
communities: widget.communities,
|
||||||
selectedSpaceUuid: widget.selectedSpace?.uuid ??
|
selectedSpaceUuid:
|
||||||
widget.selectedCommunity?.uuid ??
|
widget.selectedSpace?.uuid ?? widget.selectedCommunity?.uuid ?? '',
|
||||||
'',
|
|
||||||
onCreateCommunity: (name, description) {
|
onCreateCommunity: (name, description) {
|
||||||
context.read<SpaceManagementBloc>().add(
|
context.read<SpaceManagementBloc>().add(
|
||||||
CreateCommunityEvent(name, description, context),
|
CreateCommunityEvent(name, description, context),
|
||||||
|
|||||||
@@ -1,8 +1,11 @@
|
|||||||
|
import 'dart:async';
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
import 'package:syncrow_web/common/widgets/empty_search_result_widget.dart';
|
import 'package:syncrow_web/common/widgets/empty_search_result_widget.dart';
|
||||||
import 'package:syncrow_web/common/widgets/search_bar.dart';
|
import 'package:syncrow_web/common/widgets/search_bar.dart';
|
||||||
import 'package:syncrow_web/common/widgets/sidebar_communities_list.dart';
|
import 'package:syncrow_web/common/widgets/sidebar_communities_list.dart';
|
||||||
|
import 'package:syncrow_web/pages/space_tree/bloc/space_tree_bloc.dart';
|
||||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/bloc/space_management_bloc.dart';
|
import 'package:syncrow_web/pages/spaces_management/all_spaces/bloc/space_management_bloc.dart';
|
||||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/bloc/space_management_event.dart';
|
import 'package:syncrow_web/pages/spaces_management/all_spaces/bloc/space_management_event.dart';
|
||||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/community_model.dart';
|
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/community_model.dart';
|
||||||
@@ -15,6 +18,8 @@ import 'package:syncrow_web/pages/spaces_management/structure_selector/bloc/cent
|
|||||||
import 'package:syncrow_web/pages/spaces_management/structure_selector/bloc/center_body_event.dart';
|
import 'package:syncrow_web/pages/spaces_management/structure_selector/bloc/center_body_event.dart';
|
||||||
import 'package:syncrow_web/utils/style.dart';
|
import 'package:syncrow_web/utils/style.dart';
|
||||||
|
|
||||||
|
import '../../../space_tree/bloc/space_tree_event.dart';
|
||||||
|
|
||||||
class SidebarWidget extends StatefulWidget {
|
class SidebarWidget extends StatefulWidget {
|
||||||
final List<CommunityModel> communities;
|
final List<CommunityModel> communities;
|
||||||
final String? selectedSpaceUuid;
|
final String? selectedSpaceUuid;
|
||||||
@@ -33,6 +38,7 @@ class SidebarWidget extends StatefulWidget {
|
|||||||
|
|
||||||
class _SidebarWidgetState extends State<SidebarWidget> {
|
class _SidebarWidgetState extends State<SidebarWidget> {
|
||||||
late final ScrollController _scrollController;
|
late final ScrollController _scrollController;
|
||||||
|
Timer? _debounce;
|
||||||
|
|
||||||
String _searchQuery = '';
|
String _searchQuery = '';
|
||||||
String? _selectedSpaceUuid;
|
String? _selectedSpaceUuid;
|
||||||
@@ -40,17 +46,48 @@ class _SidebarWidgetState extends State<SidebarWidget> {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
_selectedId = widget.selectedSpaceUuid;
|
|
||||||
_scrollController = ScrollController();
|
|
||||||
super.initState();
|
super.initState();
|
||||||
|
_scrollController = ScrollController();
|
||||||
|
_scrollController.addListener(_onScroll);
|
||||||
|
_selectedId = widget.selectedSpaceUuid;
|
||||||
|
_searchQuery = '';
|
||||||
|
context.read<SpaceTreeBloc>().add(ClearCachedData());
|
||||||
|
}
|
||||||
|
|
||||||
|
void _onScroll() {
|
||||||
|
if (_scrollController.position.pixels >= _scrollController.position.maxScrollExtent - 100) {
|
||||||
|
// Trigger pagination event
|
||||||
|
final bloc = context.read<SpaceTreeBloc>();
|
||||||
|
if (!bloc.state.paginationIsLoading && bloc.state.paginationModel?.hasNext == true) {
|
||||||
|
bloc.add(
|
||||||
|
PaginationEvent(
|
||||||
|
bloc.state.paginationModel!,
|
||||||
|
bloc.state.communityList,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void dispose() {
|
void dispose() {
|
||||||
|
_scrollController.removeListener(_onScroll);
|
||||||
_scrollController.dispose();
|
_scrollController.dispose();
|
||||||
|
_debounce?.cancel();
|
||||||
super.dispose();
|
super.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void _onSearchChanged(String query) {
|
||||||
|
if (_debounce?.isActive ?? false) _debounce?.cancel();
|
||||||
|
|
||||||
|
_debounce = Timer(const Duration(milliseconds: 500), () {
|
||||||
|
setState(() {
|
||||||
|
_searchQuery = query;
|
||||||
|
});
|
||||||
|
context.read<SpaceTreeBloc>().add(SearchQueryEvent(query));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void didUpdateWidget(covariant SidebarWidget oldWidget) {
|
void didUpdateWidget(covariant SidebarWidget oldWidget) {
|
||||||
if (widget.selectedSpaceUuid != oldWidget.selectedSpaceUuid) {
|
if (widget.selectedSpaceUuid != oldWidget.selectedSpaceUuid) {
|
||||||
@@ -59,38 +96,6 @@ class _SidebarWidgetState extends State<SidebarWidget> {
|
|||||||
super.didUpdateWidget(oldWidget);
|
super.didUpdateWidget(oldWidget);
|
||||||
}
|
}
|
||||||
|
|
||||||
List<CommunityModel> _filteredCommunities() {
|
|
||||||
if (_searchQuery.isEmpty) {
|
|
||||||
_selectedSpaceUuid = null;
|
|
||||||
return widget.communities;
|
|
||||||
}
|
|
||||||
|
|
||||||
return widget.communities.where((community) {
|
|
||||||
final containsQueryInCommunity =
|
|
||||||
community.name.toLowerCase().contains(_searchQuery.toLowerCase());
|
|
||||||
final containsQueryInSpaces = community.spaces.any((space) =>
|
|
||||||
_containsQuery(space: space, query: _searchQuery.toLowerCase()));
|
|
||||||
|
|
||||||
return containsQueryInCommunity || containsQueryInSpaces;
|
|
||||||
}).toList();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool _containsQuery({
|
|
||||||
required SpaceModel space,
|
|
||||||
required String query,
|
|
||||||
}) {
|
|
||||||
final matchesSpace = space.name.toLowerCase().contains(query);
|
|
||||||
final matchesChildren = space.children.any(
|
|
||||||
(child) => _containsQuery(space: child, query: query),
|
|
||||||
);
|
|
||||||
|
|
||||||
if (matchesSpace || matchesChildren) {
|
|
||||||
_selectedSpaceUuid = space.uuid;
|
|
||||||
}
|
|
||||||
|
|
||||||
return matchesSpace || matchesChildren;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool _isSpaceOrChildSelected(SpaceModel space) {
|
bool _isSpaceOrChildSelected(SpaceModel space) {
|
||||||
final isSpaceSelected = _selectedSpaceUuid == space.uuid;
|
final isSpaceSelected = _selectedSpaceUuid == space.uuid;
|
||||||
final anySubSpaceIsSelected = space.children.any(_isSpaceOrChildSelected);
|
final anySubSpaceIsSelected = space.children.any(_isSpaceOrChildSelected);
|
||||||
@@ -101,7 +106,10 @@ class _SidebarWidgetState extends State<SidebarWidget> {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final filteredCommunities = _filteredCommunities();
|
final spaceTreeState = context.watch<SpaceTreeBloc>().state;
|
||||||
|
final filteredCommunities = spaceTreeState.searchQuery.isNotEmpty
|
||||||
|
? spaceTreeState.filteredCommunity
|
||||||
|
: spaceTreeState.communityList;
|
||||||
|
|
||||||
return Container(
|
return Container(
|
||||||
width: _width,
|
width: _width,
|
||||||
@@ -112,7 +120,7 @@ class _SidebarWidgetState extends State<SidebarWidget> {
|
|||||||
children: [
|
children: [
|
||||||
SidebarHeader(onAddCommunity: _onAddCommunity),
|
SidebarHeader(onAddCommunity: _onAddCommunity),
|
||||||
CustomSearchBar(
|
CustomSearchBar(
|
||||||
onSearchChanged: (query) => setState(() => _searchQuery = query),
|
onSearchChanged: _onSearchChanged,
|
||||||
),
|
),
|
||||||
const SizedBox(height: 16),
|
const SizedBox(height: 16),
|
||||||
Expanded(
|
Expanded(
|
||||||
@@ -120,14 +128,23 @@ class _SidebarWidgetState extends State<SidebarWidget> {
|
|||||||
visible: filteredCommunities.isNotEmpty,
|
visible: filteredCommunities.isNotEmpty,
|
||||||
replacement: const EmptySearchResultWidget(),
|
replacement: const EmptySearchResultWidget(),
|
||||||
child: SidebarCommunitiesList(
|
child: SidebarCommunitiesList(
|
||||||
scrollController: _scrollController,
|
scrollController: _scrollController,
|
||||||
onScrollToEnd: () {},
|
onScrollToEnd: () {},
|
||||||
communities: filteredCommunities,
|
communities: filteredCommunities,
|
||||||
itemBuilder: (context, index) => _buildCommunityTile(
|
itemBuilder: (context, index) {
|
||||||
context,
|
if (index == filteredCommunities.length) {
|
||||||
filteredCommunities[index],
|
final spaceTreeState = context.read<SpaceTreeBloc>().state;
|
||||||
),
|
if (spaceTreeState.paginationIsLoading) {
|
||||||
),
|
return const Padding(
|
||||||
|
padding: EdgeInsets.all(8.0),
|
||||||
|
child: Center(child: CircularProgressIndicator()),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return const SizedBox.shrink();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return _buildCommunityTile(context, filteredCommunities[index]);
|
||||||
|
}),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
@@ -205,9 +222,8 @@ class _SidebarWidgetState extends State<SidebarWidget> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
void _onAddCommunity() => _selectedId?.isNotEmpty ?? true
|
void _onAddCommunity() =>
|
||||||
? _clearSelection()
|
_selectedId?.isNotEmpty ?? true ? _clearSelection() : _showCreateCommunityDialog();
|
||||||
: _showCreateCommunityDialog();
|
|
||||||
|
|
||||||
void _clearSelection() {
|
void _clearSelection() {
|
||||||
setState(() => _selectedId = '');
|
setState(() => _selectedId = '');
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
|
import 'package:syncrow_web/pages/space_tree/bloc/space_tree_bloc.dart';
|
||||||
|
import 'package:syncrow_web/pages/space_tree/bloc/space_tree_event.dart';
|
||||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/product_model.dart';
|
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/product_model.dart';
|
||||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/tag.dart';
|
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/tag.dart';
|
||||||
import 'package:syncrow_web/pages/spaces_management/space_model/bloc/space_model_bloc.dart';
|
import 'package:syncrow_web/pages/spaces_management/space_model/bloc/space_model_bloc.dart';
|
||||||
@@ -27,6 +29,8 @@ class SpaceModelPage extends StatelessWidget {
|
|||||||
return const Center(child: CircularProgressIndicator());
|
return const Center(child: CircularProgressIndicator());
|
||||||
} else if (state is SpaceModelLoaded) {
|
} else if (state is SpaceModelLoaded) {
|
||||||
final spaceModels = state.spaceModels;
|
final spaceModels = state.spaceModels;
|
||||||
|
context.read<SpaceTreeBloc>().add(ClearCachedData());
|
||||||
|
|
||||||
final allTagValues = _getAllTagValues(spaceModels);
|
final allTagValues = _getAllTagValues(spaceModels);
|
||||||
final allSpaceModelNames = _getAllSpaceModelName(spaceModels);
|
final allSpaceModelNames = _getAllSpaceModelName(spaceModels);
|
||||||
|
|
||||||
|
|||||||
@@ -46,14 +46,14 @@ final class DebouncedBatchControlDevicesService
|
|||||||
final BatchControlDevicesService decoratee;
|
final BatchControlDevicesService decoratee;
|
||||||
final Duration debounceDuration;
|
final Duration debounceDuration;
|
||||||
|
|
||||||
final _pendingRequests = <(List<String> uuids, String code, Object value)>[];
|
|
||||||
var _isProcessing = false;
|
|
||||||
|
|
||||||
DebouncedBatchControlDevicesService({
|
DebouncedBatchControlDevicesService({
|
||||||
required this.decoratee,
|
required this.decoratee,
|
||||||
this.debounceDuration = const Duration(milliseconds: 1500),
|
this.debounceDuration = const Duration(milliseconds: 1500),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
final _pendingRequests = <(List<String> uuids, String code, Object value)>[];
|
||||||
|
var _isProcessing = false;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<bool> batchControlDevices({
|
Future<bool> batchControlDevices({
|
||||||
required List<String> uuids,
|
required List<String> uuids,
|
||||||
@@ -68,16 +68,26 @@ final class DebouncedBatchControlDevicesService
|
|||||||
|
|
||||||
await Future.delayed(debounceDuration);
|
await Future.delayed(debounceDuration);
|
||||||
|
|
||||||
final lastRequest = _pendingRequests.last;
|
final groupedRequests =
|
||||||
|
<String, (List<String> uuids, String code, Object value)>{};
|
||||||
|
for (final request in _pendingRequests) {
|
||||||
|
final (_, requestCode, requestValue) = request;
|
||||||
|
groupedRequests[requestCode] = request;
|
||||||
|
}
|
||||||
_pendingRequests.clear();
|
_pendingRequests.clear();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
final (lastRequestUuids, lastRequestCode, lastRequestValue) = lastRequest;
|
var allSuccessful = true;
|
||||||
return decoratee.batchControlDevices(
|
for (final request in groupedRequests.values) {
|
||||||
uuids: lastRequestUuids,
|
final (lastRequestUuids, lastRequestCode, lastRequestValue) = request;
|
||||||
code: lastRequestCode,
|
final success = await decoratee.batchControlDevices(
|
||||||
value: lastRequestValue,
|
uuids: lastRequestUuids,
|
||||||
);
|
code: lastRequestCode,
|
||||||
|
value: lastRequestValue,
|
||||||
|
);
|
||||||
|
if (!success) allSuccessful = false;
|
||||||
|
}
|
||||||
|
return allSuccessful;
|
||||||
} finally {
|
} finally {
|
||||||
_isProcessing = false;
|
_isProcessing = false;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -59,15 +59,24 @@ final class DebouncedControlDeviceService implements ControlDeviceService {
|
|||||||
|
|
||||||
await Future.delayed(debounceDuration);
|
await Future.delayed(debounceDuration);
|
||||||
|
|
||||||
final lastRequest = _pendingRequests.last;
|
final groupedRequests = <String, (String deviceUuid, Status status)>{};
|
||||||
|
for (final request in _pendingRequests) {
|
||||||
|
final (_, requestStatus) = request;
|
||||||
|
groupedRequests[requestStatus.code] = request;
|
||||||
|
}
|
||||||
_pendingRequests.clear();
|
_pendingRequests.clear();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
final (lastRequestDeviceUuid, lastRequestStatus) = lastRequest;
|
var allSuccessful = true;
|
||||||
return decoratee.controlDevice(
|
for (final request in groupedRequests.values) {
|
||||||
deviceUuid: lastRequestDeviceUuid,
|
final (lastRequestDeviceUuid, lastRequestStatus) = request;
|
||||||
status: lastRequestStatus,
|
final success = await decoratee.controlDevice(
|
||||||
);
|
deviceUuid: lastRequestDeviceUuid,
|
||||||
|
status: lastRequestStatus,
|
||||||
|
);
|
||||||
|
if (!success) allSuccessful = false;
|
||||||
|
}
|
||||||
|
return allSuccessful;
|
||||||
} finally {
|
} finally {
|
||||||
_isProcessing = false;
|
_isProcessing = false;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -281,6 +281,7 @@ class CommunitySpaceManagementApi {
|
|||||||
return json['success'] ?? false;
|
return json['success'] ?? false;
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
return response;
|
return response;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
debugPrint('Error updating space: $e');
|
debugPrint('Error updating space: $e');
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import 'package:go_router/go_router.dart';
|
import 'package:go_router/go_router.dart';
|
||||||
import 'package:syncrow_web/pages/access_management/view/access_management.dart';
|
import 'package:syncrow_web/pages/access_management/view/access_management.dart';
|
||||||
|
import 'package:syncrow_web/pages/analytics/modules/analytics/views/analytics_page.dart';
|
||||||
import 'package:syncrow_web/pages/auth/view/login_page.dart';
|
import 'package:syncrow_web/pages/auth/view/login_page.dart';
|
||||||
import 'package:syncrow_web/pages/device_managment/all_devices/view/device_managment_page.dart';
|
import 'package:syncrow_web/pages/device_managment/all_devices/view/device_managment_page.dart';
|
||||||
import 'package:syncrow_web/pages/home/view/home_page.dart';
|
import 'package:syncrow_web/pages/home/view/home_page.dart';
|
||||||
@@ -37,6 +38,11 @@ class AppRoutes {
|
|||||||
GoRoute(
|
GoRoute(
|
||||||
path: RoutesConst.rolesAndPermissions,
|
path: RoutesConst.rolesAndPermissions,
|
||||||
builder: (context, state) => const RolesAndPermissionPage()),
|
builder: (context, state) => const RolesAndPermissionPage()),
|
||||||
|
GoRoute(
|
||||||
|
path: RoutesConst.analytics,
|
||||||
|
name: 'analytics',
|
||||||
|
builder: (context, state) => const AnalyticsPage(),
|
||||||
|
),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -480,4 +480,5 @@ class Assets {
|
|||||||
static const String DisappeDelayIcon = 'assets/icons/disappe_delay_icon.svg';
|
static const String DisappeDelayIcon = 'assets/icons/disappe_delay_icon.svg';
|
||||||
static const String indentLevelIcon = 'assets/icons/indent_level_icon.svg';
|
static const String indentLevelIcon = 'assets/icons/indent_level_icon.svg';
|
||||||
static const String triggerLevelIcon = 'assets/icons/trigger_level_icon.svg';
|
static const String triggerLevelIcon = 'assets/icons/trigger_level_icon.svg';
|
||||||
|
static const String blankCalendar = 'assets/icons/blank_calendar.svg';
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,4 +6,5 @@ class RoutesConst {
|
|||||||
static const String deviceManagementPage = '/device-management-page';
|
static const String deviceManagementPage = '/device-management-page';
|
||||||
static const String spacesManagementPage = '/spaces_management-page';
|
static const String spacesManagementPage = '/spaces_management-page';
|
||||||
static const String rolesAndPermissions = '/roles_and_Permissions-page';
|
static const String rolesAndPermissions = '/roles_and_Permissions-page';
|
||||||
|
static const String analytics = '/syncrow_analytics';
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user