mirror of
https://github.com/SyncrowIOT/web.git
synced 2025-07-15 01:35:25 +00:00
Compare commits
12 Commits
SP-1513-FE
...
SP-1448-FE
Author | SHA1 | Date | |
---|---|---|---|
0d45a155e3 | |||
7f9d044f7e | |||
996a847a27 | |||
5645fb7826 | |||
e8f7c29652 | |||
36c5712c79 | |||
c7fef11aec | |||
ef29d78d70 | |||
cd9941f544 | |||
71aa64ba9e | |||
2262d3b2ba | |||
b7ef9da35d |
@ -10,6 +10,7 @@
|
||||
analyzer:
|
||||
errors:
|
||||
constant_identifier_names: ignore
|
||||
overridden_fields: ignore
|
||||
include: package:flutter_lints/flutter.yaml
|
||||
|
||||
linter:
|
||||
|
12
assets/icons/refresh_status_icon.svg
Normal file
12
assets/icons/refresh_status_icon.svg
Normal file
@ -0,0 +1,12 @@
|
||||
<svg width="22" height="22" viewBox="0 0 22 22" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0_7305_15779)">
|
||||
<path d="M17.0872 11.5142C17.0872 13.2025 16.427 14.8021 15.2211 15.9954C14.0278 17.2014 12.4283 17.8615 10.7399 17.8615C9.05141 17.8615 7.45185 17.2014 6.25856 15.9954C5.05262 14.8021 4.39249 13.2025 4.39249 11.5142C4.39249 9.82574 5.05266 8.22618 6.25856 7.03289C7.45185 5.8269 9.05141 5.16681 10.7399 5.16681C11.8063 5.16681 12.8471 5.43337 13.7866 5.95388L11.2984 8.97523H21.0861L18.6486 0L16.2113 2.97053C14.5737 1.91691 12.6948 1.35835 10.7398 1.35835C8.02314 1.35835 5.47142 2.41197 3.55459 4.32888C1.63765 6.24578 0.583984 8.79747 0.583984 11.5142C0.583984 14.2309 1.63765 16.7825 3.55459 18.6994C5.47146 20.6163 8.0231 21.67 10.7398 21.67C13.4565 21.67 16.0082 20.6163 17.925 18.6994C19.8419 16.7825 20.8956 14.2309 20.8956 11.5142V10.8794H17.0872V11.5142Z" fill="#77DD00"/>
|
||||
<path d="M17.0876 10.8799H20.8961V11.5146C20.8961 14.2313 19.8424 16.7829 17.9254 18.6998C16.0086 20.6168 13.4569 21.6704 10.7402 21.6704V17.862C12.4287 17.862 14.0282 17.2019 15.2215 15.9959C16.4275 14.8026 17.0876 13.203 17.0876 11.5147V10.8799H17.0876Z" fill="#66BB00"/>
|
||||
<path d="M13.787 5.95388C12.8475 5.43333 11.8066 5.16681 10.7402 5.16681V1.35835C12.6952 1.35835 14.5741 1.91691 16.2117 2.97057L18.6491 0L21.0866 8.97523H11.2989L13.787 5.95388Z" fill="#66BB00"/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_7305_15779">
|
||||
<rect width="21.67" height="21.67" fill="white"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
After Width: | Height: | Size: 1.5 KiB |
@ -20,7 +20,11 @@ class EnergyManagementDataLoadingStrategy implements AnalyticsDataLoadingStrateg
|
||||
spaces,
|
||||
),
|
||||
);
|
||||
FetchEnergyManagementDataHelper.loadEnergyManagementData(context);
|
||||
FetchEnergyManagementDataHelper.loadEnergyManagementData(
|
||||
context,
|
||||
communityId: community.uuid,
|
||||
spaceId: spaces.isNotEmpty ? spaces.first.uuid ?? '' : '',
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
@ -36,7 +40,12 @@ class EnergyManagementDataLoadingStrategy implements AnalyticsDataLoadingStrateg
|
||||
space.children,
|
||||
),
|
||||
);
|
||||
FetchEnergyManagementDataHelper.loadEnergyManagementData(context);
|
||||
|
||||
FetchEnergyManagementDataHelper.loadEnergyManagementData(
|
||||
context,
|
||||
communityId: community.uuid,
|
||||
spaceId: space.uuid ?? '',
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
@ -45,14 +54,7 @@ class EnergyManagementDataLoadingStrategy implements AnalyticsDataLoadingStrateg
|
||||
CommunityModel community,
|
||||
SpaceModel child,
|
||||
) {
|
||||
context.read<SpaceTreeBloc>().add(
|
||||
OnSpaceSelected(
|
||||
community,
|
||||
child.uuid ?? '',
|
||||
child.children,
|
||||
),
|
||||
);
|
||||
FetchEnergyManagementDataHelper.loadEnergyManagementData(context);
|
||||
// Do nothing
|
||||
}
|
||||
|
||||
@override
|
||||
|
@ -17,10 +17,14 @@ class OccupancyDataLoadingStrategy implements AnalyticsDataLoadingStrategy {
|
||||
context.read<SpaceTreeBloc>().add(
|
||||
OnCommunitySelected(
|
||||
community.uuid,
|
||||
spaces,
|
||||
spaces.isNotEmpty ? [spaces.first] : [],
|
||||
),
|
||||
);
|
||||
FetchOccupancyDataHelper.loadOccupancyData(context);
|
||||
FetchOccupancyDataHelper.loadOccupancyData(
|
||||
context,
|
||||
communityId: community.uuid,
|
||||
spaceId: spaces.isNotEmpty ? spaces.first.uuid ?? '' : '',
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
@ -29,14 +33,25 @@ class OccupancyDataLoadingStrategy implements AnalyticsDataLoadingStrategy {
|
||||
CommunityModel community,
|
||||
SpaceModel space,
|
||||
) {
|
||||
context.read<SpaceTreeBloc>().add(
|
||||
OnSpaceSelected(
|
||||
community,
|
||||
space.uuid ?? '',
|
||||
space.children,
|
||||
),
|
||||
);
|
||||
FetchOccupancyDataHelper.loadOccupancyData(context);
|
||||
final spaceTreeBloc = context.read<SpaceTreeBloc>();
|
||||
final selectedSpacesIds = spaceTreeBloc.state.selectedSpaces;
|
||||
final isSpaceSelected = selectedSpacesIds.contains(space.uuid);
|
||||
|
||||
if (selectedSpacesIds.isEmpty) {
|
||||
spaceTreeBloc.add(OnCommunitySelected(community.uuid, [space]));
|
||||
} else if (isSpaceSelected) {
|
||||
spaceTreeBloc.add(const SpaceTreeClearSelectionEvent());
|
||||
} else {
|
||||
spaceTreeBloc
|
||||
..add(const SpaceTreeClearSelectionEvent())
|
||||
..add(OnSpaceSelected(community, space.uuid ?? '', []));
|
||||
}
|
||||
|
||||
FetchOccupancyDataHelper.loadOccupancyData(
|
||||
context,
|
||||
communityId: community.uuid,
|
||||
spaceId: space.uuid ?? '',
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
@ -45,18 +60,12 @@ class OccupancyDataLoadingStrategy implements AnalyticsDataLoadingStrategy {
|
||||
CommunityModel community,
|
||||
SpaceModel child,
|
||||
) {
|
||||
context.read<SpaceTreeBloc>().add(
|
||||
OnSpaceSelected(
|
||||
community,
|
||||
child.uuid ?? '',
|
||||
child.children,
|
||||
),
|
||||
);
|
||||
FetchOccupancyDataHelper.loadOccupancyData(context);
|
||||
// Do nothing
|
||||
}
|
||||
|
||||
@override
|
||||
void clearData(BuildContext context) {
|
||||
context.read<SpaceTreeBloc>().add(const SpaceTreeClearSelectionEvent());
|
||||
// FetchOccupancyDataHelper.clearAllData(context);
|
||||
}
|
||||
}
|
||||
|
@ -9,29 +9,21 @@ class AnalyticsCommunitiesSidebar extends StatelessWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Builder(
|
||||
builder: (context) {
|
||||
final selectedTab = context.read<AnalyticsTabBloc>().state;
|
||||
final strategy =
|
||||
AnalyticsDataLoadingStrategyFactory.getStrategy(selectedTab);
|
||||
final selectedTab = context.watch<AnalyticsTabBloc>().state;
|
||||
final strategy = AnalyticsDataLoadingStrategyFactory.getStrategy(selectedTab);
|
||||
|
||||
// Clear data when tab changes
|
||||
strategy.clearData(context);
|
||||
|
||||
return Expanded(
|
||||
child: AnalyticsSpaceTreeView(
|
||||
onSelectCommunity: (community, spaces) {
|
||||
strategy.onCommunitySelected(context, community, spaces);
|
||||
},
|
||||
onSelectSpace: (community, space) {
|
||||
strategy.onSpaceSelected(context, community, space);
|
||||
},
|
||||
onSelectChildSpace: (community, child) {
|
||||
strategy.onChildSpaceSelected(context, community, child);
|
||||
},
|
||||
),
|
||||
);
|
||||
},
|
||||
return Expanded(
|
||||
child: AnalyticsSpaceTreeView(
|
||||
onSelectCommunity: (community, spaces) {
|
||||
strategy.onCommunitySelected(context, community, spaces);
|
||||
},
|
||||
onSelectSpace: (community, space) {
|
||||
strategy.onSpaceSelected(context, community, space);
|
||||
},
|
||||
onSelectChildSpace: (community, child) {
|
||||
strategy.onChildSpaceSelected(context, community, child);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -6,6 +6,7 @@ import 'package:syncrow_web/pages/analytics/modules/analytics/enums/analytics_pa
|
||||
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/pages/analytics/modules/energy_management/helpers/fetch_energy_management_data_helper.dart';
|
||||
import 'package:syncrow_web/pages/space_tree/bloc/space_tree_bloc.dart';
|
||||
import 'package:syncrow_web/utils/style.dart';
|
||||
|
||||
class AnalyticsPageTabsAndChildren extends StatelessWidget {
|
||||
@ -13,6 +14,7 @@ class AnalyticsPageTabsAndChildren extends StatelessWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final spaceTreeState = context.read<SpaceTreeBloc>().state;
|
||||
return BlocBuilder<AnalyticsTabBloc, AnalyticsPageTab>(
|
||||
buildWhen: (previous, current) => previous != current,
|
||||
builder: (context, selectedTab) => Column(
|
||||
@ -53,32 +55,33 @@ class AnalyticsPageTabsAndChildren extends StatelessWidget {
|
||||
),
|
||||
),
|
||||
const Spacer(),
|
||||
_buildAnimation(
|
||||
child: Visibility(
|
||||
key: ValueKey(selectedTab),
|
||||
visible: selectedTab == AnalyticsPageTab.energyManagement,
|
||||
child: Expanded(
|
||||
flex: 2,
|
||||
child: FittedBox(
|
||||
fit: BoxFit.scaleDown,
|
||||
alignment: AlignmentDirectional.centerEnd,
|
||||
child: AnalyticsDateFilterButton(
|
||||
onDateSelected: (DateTime value) {
|
||||
context.read<AnalyticsDatePickerBloc>().add(
|
||||
UpdateAnalyticsDatePickerEvent(
|
||||
montlyDate: value),
|
||||
);
|
||||
FetchEnergyManagementDataHelper
|
||||
.fetchEnergyManagementData(
|
||||
context,
|
||||
selectedDate: value,
|
||||
);
|
||||
},
|
||||
selectedDate: context
|
||||
.watch<AnalyticsDatePickerBloc>()
|
||||
.state
|
||||
.monthlyDate,
|
||||
),
|
||||
Visibility(
|
||||
key: ValueKey(selectedTab),
|
||||
visible: selectedTab == AnalyticsPageTab.energyManagement,
|
||||
child: Expanded(
|
||||
flex: 2,
|
||||
child: FittedBox(
|
||||
fit: BoxFit.scaleDown,
|
||||
alignment: AlignmentDirectional.centerEnd,
|
||||
child: AnalyticsDateFilterButton(
|
||||
onDateSelected: (DateTime value) {
|
||||
context.read<AnalyticsDatePickerBloc>().add(
|
||||
UpdateAnalyticsDatePickerEvent(montlyDate: value),
|
||||
);
|
||||
FetchEnergyManagementDataHelper.loadEnergyManagementData(
|
||||
context,
|
||||
selectedDate: value,
|
||||
communityId:
|
||||
spaceTreeState.selectedCommunities.firstOrNull ??
|
||||
'',
|
||||
spaceId:
|
||||
spaceTreeState.selectedSpaces.firstOrNull ?? '',
|
||||
);
|
||||
},
|
||||
selectedDate: context
|
||||
.watch<AnalyticsDatePickerBloc>()
|
||||
.state
|
||||
.monthlyDate,
|
||||
),
|
||||
),
|
||||
),
|
||||
|
@ -9,58 +9,38 @@ import 'package:syncrow_web/pages/analytics/modules/energy_management/blocs/tota
|
||||
import 'package:syncrow_web/pages/analytics/params/get_energy_consumption_by_phases_param.dart';
|
||||
import 'package:syncrow_web/pages/analytics/params/get_energy_consumption_per_device_param.dart';
|
||||
import 'package:syncrow_web/pages/analytics/params/get_total_energy_consumption_param.dart';
|
||||
import 'package:syncrow_web/pages/space_tree/bloc/space_tree_bloc.dart';
|
||||
|
||||
abstract final class FetchEnergyManagementDataHelper {
|
||||
const FetchEnergyManagementDataHelper._();
|
||||
|
||||
static void fetchEnergyManagementData(
|
||||
static const String _powerClampId = 'cb71d6ad-6e29-4eaa-ae3e-1a0d1c5f60fa';
|
||||
|
||||
static void loadEnergyManagementData(
|
||||
BuildContext context, {
|
||||
required String communityId,
|
||||
required String spaceId,
|
||||
DateTime? selectedDate,
|
||||
}) {
|
||||
final (selectedCommunities, selectedSpaces) =
|
||||
getSelectedCommunitiesAndSpaces(context);
|
||||
|
||||
if (selectedCommunities.isEmpty && selectedSpaces.isEmpty) {
|
||||
if (communityId.isEmpty && spaceId.isEmpty) {
|
||||
clearAllData(context);
|
||||
return;
|
||||
}
|
||||
|
||||
final datePickerState = context.read<AnalyticsDatePickerBloc>().state;
|
||||
final selectedDate0 = selectedDate ?? datePickerState.monthlyDate;
|
||||
|
||||
loadTotalEnergyConsumption(context, selectedDate: datePickerState.monthlyDate);
|
||||
loadEnergyConsumptionByPhases(
|
||||
loadTotalEnergyConsumption(
|
||||
context,
|
||||
selectedDate: datePickerState.monthlyDate,
|
||||
selectedDate: selectedDate0,
|
||||
communityId: communityId,
|
||||
spaceId: spaceId,
|
||||
);
|
||||
|
||||
loadEnergyConsumptionByPhases(context, selectedDate: selectedDate);
|
||||
|
||||
loadEnergyConsumptionPerDevice(context);
|
||||
return;
|
||||
}
|
||||
|
||||
static void loadEnergyManagementData(BuildContext context) {
|
||||
final (selectedCommunities, selectedSpaces) =
|
||||
FetchEnergyManagementDataHelper.getSelectedCommunitiesAndSpaces(context);
|
||||
if (selectedCommunities.isEmpty && selectedSpaces.isEmpty) return;
|
||||
|
||||
FetchEnergyManagementDataHelper.fetchEnergyManagementData(context,
|
||||
selectedDate: DateTime.now());
|
||||
FetchEnergyManagementDataHelper.loadRealtimeDeviceChanges(context);
|
||||
context.read<PowerClampInfoBloc>().add(const ClearPowerClampInfoEvent());
|
||||
if (selectedCommunities.isEmpty && selectedSpaces.isEmpty) {
|
||||
context.read<PowerClampInfoBloc>().add(
|
||||
const ClearPowerClampInfoEvent(),
|
||||
);
|
||||
} else {
|
||||
FetchEnergyManagementDataHelper.loadPowerClampInfo(context);
|
||||
}
|
||||
}
|
||||
|
||||
static (List<String> selectedCommunities, List<String> selectedSpaces)
|
||||
getSelectedCommunitiesAndSpaces(BuildContext context) {
|
||||
final spaceTreeState = context.read<SpaceTreeBloc>().state;
|
||||
final selectedCommunities = spaceTreeState.selectedCommunities;
|
||||
final selectedSpaces = spaceTreeState.selectedSpaces;
|
||||
|
||||
return (selectedCommunities, selectedSpaces);
|
||||
loadRealtimeDeviceChanges(context);
|
||||
loadPowerClampInfo(context);
|
||||
}
|
||||
|
||||
static void loadEnergyConsumptionByPhases(
|
||||
@ -79,13 +59,12 @@ abstract final class FetchEnergyManagementDataHelper {
|
||||
static void loadTotalEnergyConsumption(
|
||||
BuildContext context, {
|
||||
DateTime? selectedDate,
|
||||
required String communityId,
|
||||
required String spaceId,
|
||||
}) {
|
||||
final (selectedCommunities, selectedSpaces) =
|
||||
getSelectedCommunitiesAndSpaces(context);
|
||||
|
||||
final param = GetTotalEnergyConsumptionParam(
|
||||
spaceId: selectedCommunities.firstOrNull,
|
||||
communityId: selectedCommunities.firstOrNull,
|
||||
spaceId: spaceId,
|
||||
communityId: communityId,
|
||||
monthDate: selectedDate,
|
||||
);
|
||||
context.read<TotalEnergyConsumptionBloc>().add(
|
||||
@ -102,13 +81,13 @@ abstract final class FetchEnergyManagementDataHelper {
|
||||
|
||||
static void loadPowerClampInfo(BuildContext context) {
|
||||
context.read<PowerClampInfoBloc>().add(
|
||||
const LoadPowerClampInfoEvent('cb71d6ad-6e29-4eaa-ae3e-1a0d1c5f60fa'),
|
||||
const LoadPowerClampInfoEvent(_powerClampId),
|
||||
);
|
||||
}
|
||||
|
||||
static void loadRealtimeDeviceChanges(BuildContext context) {
|
||||
context.read<RealtimeDeviceChangesBloc>().add(
|
||||
const RealtimeDeviceChangesStarted('cb71d6ad-6e29-4eaa-ae3e-1a0d1c5f60fa'),
|
||||
const RealtimeDeviceChangesStarted(_powerClampId),
|
||||
);
|
||||
}
|
||||
|
||||
@ -120,6 +99,7 @@ abstract final class FetchEnergyManagementDataHelper {
|
||||
context.read<PowerClampInfoBloc>().add(
|
||||
const ClearPowerClampInfoEvent(),
|
||||
);
|
||||
|
||||
context.read<EnergyConsumptionPerDeviceBloc>().add(
|
||||
const ClearEnergyConsumptionPerDeviceEvent(),
|
||||
);
|
||||
|
@ -1,8 +1,10 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:syncrow_web/pages/analytics/modules/energy_management/helpers/fetch_energy_management_data_helper.dart';
|
||||
import 'package:syncrow_web/pages/analytics/modules/energy_management/widgets/energy_consumption_per_device_chart_box.dart';
|
||||
import 'package:syncrow_web/pages/analytics/modules/energy_management/widgets/power_clamp_energy_data_widget.dart';
|
||||
import 'package:syncrow_web/pages/analytics/modules/energy_management/widgets/total_energy_consumption_chart_box.dart';
|
||||
import 'package:syncrow_web/pages/space_tree/bloc/space_tree_bloc.dart';
|
||||
|
||||
class AnalyticsEnergyManagementView extends StatefulWidget {
|
||||
const AnalyticsEnergyManagementView({super.key});
|
||||
@ -16,7 +18,14 @@ class _AnalyticsEnergyManagementViewState
|
||||
extends State<AnalyticsEnergyManagementView> {
|
||||
@override
|
||||
void initState() {
|
||||
FetchEnergyManagementDataHelper.loadEnergyManagementData(context);
|
||||
final spaceTreeBloc = context.read<SpaceTreeBloc>();
|
||||
final communityId = spaceTreeBloc.state.selectedCommunities.firstOrNull;
|
||||
final spaceId = spaceTreeBloc.state.selectedSpaces.firstOrNull;
|
||||
FetchEnergyManagementDataHelper.loadEnergyManagementData(
|
||||
context,
|
||||
communityId: communityId ?? '',
|
||||
spaceId: spaceId ?? '',
|
||||
);
|
||||
super.initState();
|
||||
}
|
||||
|
||||
|
@ -2,7 +2,6 @@ import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:syncrow_web/pages/analytics/modules/analytics/blocs/analytics_date_picker_bloc/analytics_date_picker_bloc.dart';
|
||||
import 'package:syncrow_web/pages/analytics/modules/energy_management/blocs/realtime_device_changes/realtime_device_changes_bloc.dart';
|
||||
import 'package:syncrow_web/pages/analytics/modules/energy_management/helpers/fetch_energy_management_data_helper.dart';
|
||||
import 'package:syncrow_web/pages/analytics/modules/occupancy/blocs/occupancy/occupancy_bloc.dart';
|
||||
import 'package:syncrow_web/pages/analytics/modules/occupancy/blocs/occupancy_heat_map/occupancy_heat_map_bloc.dart';
|
||||
import 'package:syncrow_web/pages/analytics/params/get_occupancy_heat_map_param.dart';
|
||||
@ -11,17 +10,13 @@ import 'package:syncrow_web/pages/analytics/params/get_occupancy_param.dart';
|
||||
abstract final class FetchOccupancyDataHelper {
|
||||
const FetchOccupancyDataHelper._();
|
||||
|
||||
static void loadOccupancyData(BuildContext context) {
|
||||
final (selectedCommunities, selectedSpaces) =
|
||||
FetchEnergyManagementDataHelper.getSelectedCommunitiesAndSpaces(context);
|
||||
if (selectedCommunities.isEmpty && selectedSpaces.isEmpty) {
|
||||
context.read<OccupancyBloc>().add(
|
||||
const ClearOccupancyEvent(),
|
||||
);
|
||||
context.read<OccupancyHeatMapBloc>().add(
|
||||
const ClearOccupancyHeatMapEvent(),
|
||||
);
|
||||
return;
|
||||
static void loadOccupancyData(
|
||||
BuildContext context, {
|
||||
required String communityId,
|
||||
required String spaceId,
|
||||
}) {
|
||||
if (communityId.isEmpty && spaceId.isEmpty) {
|
||||
clearAllData(context);
|
||||
}
|
||||
|
||||
final datePickerState = context.read<AnalyticsDatePickerBloc>().state;
|
||||
@ -31,8 +26,8 @@ abstract final class FetchOccupancyDataHelper {
|
||||
GetOccupancyParam(
|
||||
monthDate:
|
||||
'${datePickerState.monthlyDate.year}-${datePickerState.monthlyDate.month}',
|
||||
spaceUuid: selectedSpaces.firstOrNull,
|
||||
communityUuid: selectedCommunities.first,
|
||||
spaceUuid: spaceId,
|
||||
communityUuid: communityId,
|
||||
),
|
||||
),
|
||||
);
|
||||
@ -40,9 +35,8 @@ abstract final class FetchOccupancyDataHelper {
|
||||
context.read<OccupancyHeatMapBloc>().add(
|
||||
LoadOccupancyHeatMapEvent(
|
||||
GetOccupancyHeatMapParam(
|
||||
spaceId: selectedSpaces.isNotEmpty ? selectedSpaces.first : '',
|
||||
communityId:
|
||||
selectedCommunities.isNotEmpty ? selectedCommunities.first : '',
|
||||
spaceId: spaceId,
|
||||
communityId: communityId,
|
||||
year: datePickerState.yearlyDate,
|
||||
),
|
||||
),
|
||||
@ -54,4 +48,16 @@ abstract final class FetchOccupancyDataHelper {
|
||||
const RealtimeDeviceChangesStarted('14fe6e7e-47af-4a07-ae0a-7c4a26ef8135'),
|
||||
);
|
||||
}
|
||||
|
||||
static void clearAllData(BuildContext context) {
|
||||
context.read<OccupancyBloc>().add(
|
||||
const ClearOccupancyEvent(),
|
||||
);
|
||||
context.read<OccupancyHeatMapBloc>().add(
|
||||
const ClearOccupancyHeatMapEvent(),
|
||||
);
|
||||
context.read<RealtimeDeviceChangesBloc>().add(
|
||||
const RealtimeDeviceChangesClosed(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -1,25 +1,13 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:syncrow_web/pages/analytics/modules/occupancy/helpers/fetch_occupancy_data_helper.dart';
|
||||
import 'package:syncrow_web/pages/analytics/modules/occupancy/widgets/occupancy_chart_box.dart';
|
||||
import 'package:syncrow_web/pages/analytics/modules/occupancy/widgets/occupancy_end_side_bar.dart';
|
||||
import 'package:syncrow_web/pages/analytics/modules/occupancy/widgets/occupancy_heat_map_box.dart';
|
||||
|
||||
class AnalyticsOccupancyView extends StatefulWidget {
|
||||
class AnalyticsOccupancyView extends StatelessWidget {
|
||||
const AnalyticsOccupancyView({super.key});
|
||||
|
||||
static const _padding = EdgeInsetsDirectional.all(32);
|
||||
|
||||
@override
|
||||
State<AnalyticsOccupancyView> createState() => _AnalyticsOccupancyViewState();
|
||||
}
|
||||
|
||||
class _AnalyticsOccupancyViewState extends State<AnalyticsOccupancyView> {
|
||||
@override
|
||||
void initState() {
|
||||
FetchOccupancyDataHelper.loadOccupancyData(context);
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final height = MediaQuery.sizeOf(context).height;
|
||||
@ -28,7 +16,7 @@ class _AnalyticsOccupancyViewState extends State<AnalyticsOccupancyView> {
|
||||
final isMediumOrLess = constraints.maxWidth <= 900;
|
||||
if (isMediumOrLess) {
|
||||
return SingleChildScrollView(
|
||||
padding: AnalyticsOccupancyView._padding,
|
||||
padding: _padding,
|
||||
child: Column(
|
||||
spacing: 32,
|
||||
children: [
|
||||
@ -42,7 +30,7 @@ class _AnalyticsOccupancyViewState extends State<AnalyticsOccupancyView> {
|
||||
|
||||
return SingleChildScrollView(
|
||||
child: Container(
|
||||
padding: AnalyticsOccupancyView._padding,
|
||||
padding: _padding,
|
||||
height: height * 0.9,
|
||||
child: const Row(
|
||||
spacing: 32,
|
||||
|
@ -7,6 +7,7 @@ import 'package:syncrow_web/pages/analytics/modules/occupancy/blocs/occupancy/oc
|
||||
import 'package:syncrow_web/pages/analytics/modules/occupancy/helpers/fetch_occupancy_data_helper.dart';
|
||||
import 'package:syncrow_web/pages/analytics/modules/occupancy/widgets/occupancy_chart.dart';
|
||||
import 'package:syncrow_web/pages/analytics/widgets/analytics_error_widget.dart';
|
||||
import 'package:syncrow_web/pages/space_tree/bloc/space_tree_bloc.dart';
|
||||
import 'package:syncrow_web/utils/style.dart';
|
||||
|
||||
class OccupancyChartBox extends StatelessWidget {
|
||||
@ -14,6 +15,7 @@ class OccupancyChartBox extends StatelessWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final spaceTreeState = context.watch<SpaceTreeBloc>().state;
|
||||
return BlocBuilder<OccupancyBloc, OccupancyState>(
|
||||
builder: (context, state) {
|
||||
return Container(
|
||||
@ -45,7 +47,11 @@ class OccupancyChartBox extends StatelessWidget {
|
||||
context.read<AnalyticsDatePickerBloc>().add(
|
||||
UpdateAnalyticsDatePickerEvent(montlyDate: value),
|
||||
);
|
||||
FetchOccupancyDataHelper.loadOccupancyData(context);
|
||||
FetchOccupancyDataHelper.loadOccupancyData(
|
||||
context,
|
||||
communityId: spaceTreeState.selectedCommunities.firstOrNull ?? '',
|
||||
spaceId: spaceTreeState.selectedSpaces.firstOrNull ?? '',
|
||||
);
|
||||
},
|
||||
selectedDate: context
|
||||
.watch<AnalyticsDatePickerBloc>()
|
||||
|
@ -7,6 +7,7 @@ import 'package:syncrow_web/pages/analytics/modules/occupancy/blocs/occupancy_he
|
||||
import 'package:syncrow_web/pages/analytics/modules/occupancy/helpers/fetch_occupancy_data_helper.dart';
|
||||
import 'package:syncrow_web/pages/analytics/modules/occupancy/widgets/occupancy_heat_map.dart';
|
||||
import 'package:syncrow_web/pages/analytics/widgets/analytics_error_widget.dart';
|
||||
import 'package:syncrow_web/pages/space_tree/bloc/space_tree_bloc.dart';
|
||||
import 'package:syncrow_web/utils/style.dart';
|
||||
|
||||
class OccupancyHeatMapBox extends StatelessWidget {
|
||||
@ -14,6 +15,7 @@ class OccupancyHeatMapBox extends StatelessWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final spaceTreeState = context.watch<SpaceTreeBloc>().state;
|
||||
return BlocBuilder<OccupancyHeatMapBloc, OccupancyHeatMapState>(
|
||||
builder: (context, state) {
|
||||
return Container(
|
||||
@ -45,7 +47,12 @@ class OccupancyHeatMapBox extends StatelessWidget {
|
||||
context.read<AnalyticsDatePickerBloc>().add(
|
||||
UpdateAnalyticsDatePickerEvent(yearlyDate: value),
|
||||
);
|
||||
FetchOccupancyDataHelper.loadOccupancyData(context);
|
||||
FetchOccupancyDataHelper.loadOccupancyData(
|
||||
context,
|
||||
communityId:
|
||||
spaceTreeState.selectedCommunities.firstOrNull ?? '',
|
||||
spaceId: spaceTreeState.selectedSpaces.firstOrNull ?? '',
|
||||
);
|
||||
},
|
||||
datePickerType: DatePickerType.year,
|
||||
selectedDate: context
|
||||
|
@ -12,6 +12,7 @@ import 'package:syncrow_web/pages/routines/models/gang_switches/one_gang_switch/
|
||||
import 'package:syncrow_web/pages/routines/models/gang_switches/three_gang_switch/three_gang_switch.dart';
|
||||
import 'package:syncrow_web/pages/routines/models/gang_switches/two_gang_switch/two_gang_switch.dart';
|
||||
import 'package:syncrow_web/pages/routines/models/gateway.dart';
|
||||
import 'package:syncrow_web/pages/routines/models/water_heater/water_heater_functions.dart';
|
||||
import 'package:syncrow_web/pages/routines/models/wps/wps_functions.dart';
|
||||
import 'package:syncrow_web/pages/routines/widgets/routine_dialogs/ceiling_sensor/ceiling_sensor_helper.dart';
|
||||
import 'package:syncrow_web/utils/constants/assets.dart';
|
||||
@ -358,7 +359,10 @@ SOS
|
||||
case 'NCPS':
|
||||
return [
|
||||
FlushPresenceDelayFunction(
|
||||
deviceId: uuid ?? '', deviceName: name ?? '', type: 'IF',),
|
||||
deviceId: uuid ?? '',
|
||||
deviceName: name ?? '',
|
||||
type: 'IF',
|
||||
),
|
||||
|
||||
FlushIlluminanceFunction(
|
||||
deviceId: uuid ?? '', deviceName: name ?? '', type: 'IF'),
|
||||
@ -378,6 +382,17 @@ SOS
|
||||
FlushTriggerLevelFunction(
|
||||
deviceId: uuid ?? '', deviceName: name ?? '', type: 'THEN'),
|
||||
];
|
||||
case 'WH':
|
||||
return [
|
||||
WHRestartStatusFunction(
|
||||
deviceId: uuid ?? '', deviceName: name ?? '', type: 'IF'),
|
||||
WHSwitchFunction(
|
||||
deviceId: uuid ?? '', deviceName: name ?? '', type: 'BOTH'),
|
||||
TimerConfirmTimeFunction(
|
||||
deviceId: uuid ?? '', deviceName: name ?? '', type: 'BOTH'),
|
||||
BacklightFunction(
|
||||
deviceId: uuid ?? '', deviceName: name ?? '', type: 'IF'),
|
||||
];
|
||||
|
||||
default:
|
||||
return [];
|
||||
|
@ -26,8 +26,10 @@ class FunctionBloc extends Bloc<FunctionBlocEvent, FunctionBlocState> {
|
||||
functionCode: event.functionData.functionCode,
|
||||
operationName: event.functionData.operationName,
|
||||
value: event.functionData.value ?? existingData.value,
|
||||
valueDescription: event.functionData.valueDescription ?? existingData.valueDescription,
|
||||
valueDescription: event.functionData.valueDescription ??
|
||||
existingData.valueDescription,
|
||||
condition: event.functionData.condition ?? existingData.condition,
|
||||
step: event.functionData.step ?? existingData.step,
|
||||
);
|
||||
} else {
|
||||
functions.clear();
|
||||
@ -59,8 +61,10 @@ class FunctionBloc extends Bloc<FunctionBlocEvent, FunctionBlocState> {
|
||||
);
|
||||
}
|
||||
|
||||
FutureOr<void> _onSelectFunction(SelectFunction event, Emitter<FunctionBlocState> emit) {
|
||||
FutureOr<void> _onSelectFunction(
|
||||
SelectFunction event, Emitter<FunctionBlocState> emit) {
|
||||
emit(state.copyWith(
|
||||
selectedFunction: event.functionCode, selectedOperationName: event.operationName));
|
||||
selectedFunction: event.functionCode,
|
||||
selectedOperationName: event.operationName));
|
||||
}
|
||||
}
|
||||
|
@ -536,7 +536,7 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
|
||||
// 'entityId': 'tab_to_run',
|
||||
// 'uniqueCustomId': const Uuid().v4(),
|
||||
// 'deviceId': 'tab_to_run',
|
||||
// 'title': 'Tab to run',
|
||||
// 'title': 'Tap to run',
|
||||
// 'productType': 'tab_to_run',
|
||||
// 'imagePath': Assets.tabToRun,
|
||||
// }
|
||||
@ -771,7 +771,7 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
|
||||
'entityId': 'tab_to_run',
|
||||
'uniqueCustomId': const Uuid().v4(),
|
||||
'deviceId': 'tab_to_run',
|
||||
'title': 'Tab to run',
|
||||
'title': 'Tap to run',
|
||||
'productType': 'tab_to_run',
|
||||
'imagePath': Assets.tabToRun,
|
||||
}
|
||||
|
@ -10,6 +10,7 @@ import 'package:syncrow_web/pages/routines/widgets/routine_dialogs/one_gang_swit
|
||||
import 'package:syncrow_web/pages/routines/widgets/routine_dialogs/three_gang_switch_dialog.dart';
|
||||
import 'package:syncrow_web/pages/routines/widgets/routine_dialogs/two_gang_switch_dialog.dart';
|
||||
import 'package:syncrow_web/pages/routines/widgets/routine_dialogs/wall_sensor/wall_presence_sensor.dart';
|
||||
import 'package:syncrow_web/pages/routines/widgets/routine_dialogs/water_heater/water_heater_presence_sensor.dart';
|
||||
|
||||
class DeviceDialogHelper {
|
||||
static Future<Map<String, dynamic>?> showDeviceDialog({
|
||||
@ -126,6 +127,15 @@ class DeviceDialogHelper {
|
||||
dialogType: dialogType,
|
||||
device: data['device'],
|
||||
);
|
||||
case 'WH':
|
||||
return WaterHeaterDialogRoutines.showWHFunctionsDialog(
|
||||
context: context,
|
||||
functions: functions,
|
||||
uniqueCustomId: data['uniqueCustomId'],
|
||||
deviceSelectedFunctions: deviceSelectedFunctions,
|
||||
dialogType: dialogType,
|
||||
device: data['device'],
|
||||
);
|
||||
|
||||
default:
|
||||
return null;
|
||||
|
@ -162,7 +162,7 @@ class SaveRoutineHelper {
|
||||
width: 24,
|
||||
height: 24,
|
||||
),
|
||||
title: const Text('Tab to run'),
|
||||
title: const Text('Tap to run'),
|
||||
),
|
||||
if (state.isAutomation)
|
||||
...state.ifItems.map((item) {
|
||||
|
@ -14,6 +14,10 @@ abstract class ACFunction extends DeviceFunction<AcStatusModel> {
|
||||
required super.operationName,
|
||||
required super.icon,
|
||||
required this.type,
|
||||
super.step,
|
||||
super.unit,
|
||||
super.max,
|
||||
super.min,
|
||||
});
|
||||
|
||||
List<ACOperationalValue> getOperationalValues();
|
||||
@ -75,26 +79,24 @@ class ModeFunction extends ACFunction {
|
||||
}
|
||||
|
||||
class TempSetFunction extends ACFunction {
|
||||
final int min;
|
||||
final int max;
|
||||
final int step;
|
||||
|
||||
TempSetFunction(
|
||||
{required super.deviceId, required super.deviceName, required type})
|
||||
: min = 160,
|
||||
max = 300,
|
||||
step = 1,
|
||||
super(
|
||||
TempSetFunction({
|
||||
required super.deviceId,
|
||||
required super.deviceName,
|
||||
required super.type,
|
||||
}) : super(
|
||||
code: 'temp_set',
|
||||
operationName: 'Set Temperature',
|
||||
icon: Assets.assetsTempreture,
|
||||
type: type,
|
||||
min: 200,
|
||||
max: 300,
|
||||
step: 1,
|
||||
unit: "°C",
|
||||
);
|
||||
|
||||
@override
|
||||
List<ACOperationalValue> getOperationalValues() {
|
||||
List<ACOperationalValue> values = [];
|
||||
for (int temp = min; temp <= max; temp += step) {
|
||||
for (int temp = min!.toInt(); temp <= max!; temp += step!.toInt()) {
|
||||
values.add(ACOperationalValue(
|
||||
icon: Assets.assetsTempreture,
|
||||
description: "${temp / 10}°C",
|
||||
@ -104,7 +106,6 @@ class TempSetFunction extends ACFunction {
|
||||
return values;
|
||||
}
|
||||
}
|
||||
|
||||
class LevelFunction extends ACFunction {
|
||||
LevelFunction(
|
||||
{required super.deviceId, required super.deviceName, required type})
|
||||
@ -166,9 +167,10 @@ class ChildLockFunction extends ACFunction {
|
||||
}
|
||||
|
||||
class CurrentTempFunction extends ACFunction {
|
||||
final int min;
|
||||
final int max;
|
||||
final int step;
|
||||
final double min;
|
||||
final double max;
|
||||
final double step;
|
||||
final String unit = "°C";
|
||||
|
||||
CurrentTempFunction(
|
||||
{required super.deviceId, required super.deviceName, required type})
|
||||
@ -185,7 +187,7 @@ class CurrentTempFunction extends ACFunction {
|
||||
@override
|
||||
List<ACOperationalValue> getOperationalValues() {
|
||||
List<ACOperationalValue> values = [];
|
||||
for (int temp = min; temp <= max; temp += step) {
|
||||
for (int temp = min.toInt(); temp <= max; temp += step.toInt()) {
|
||||
values.add(ACOperationalValue(
|
||||
icon: Assets.currentTemp,
|
||||
description: "${temp / 10}°C",
|
||||
|
@ -6,10 +6,12 @@ class CpsOperationalValue {
|
||||
final String description;
|
||||
final dynamic value;
|
||||
|
||||
|
||||
CpsOperationalValue({
|
||||
required this.icon,
|
||||
required this.description,
|
||||
required this.value,
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
@ -94,9 +96,9 @@ final class CpsSensitivityFunction extends CpsFunctions {
|
||||
icon: Assets.sensitivity,
|
||||
);
|
||||
|
||||
final int min;
|
||||
final int max;
|
||||
final int step;
|
||||
final double min;
|
||||
final double max;
|
||||
final double step;
|
||||
|
||||
static const _images = <String>[
|
||||
Assets.sensitivityFeature1,
|
||||
@ -115,10 +117,10 @@ final class CpsSensitivityFunction extends CpsFunctions {
|
||||
@override
|
||||
List<CpsOperationalValue> getOperationalValues() {
|
||||
final values = <CpsOperationalValue>[];
|
||||
for (var value = min; value <= max; value += step) {
|
||||
for (var value = min; value <= max; value += step.toInt()) {
|
||||
values.add(
|
||||
CpsOperationalValue(
|
||||
icon: _images[value],
|
||||
icon: _images[value.toInt()],
|
||||
description: '$value',
|
||||
value: value,
|
||||
),
|
||||
@ -142,9 +144,9 @@ final class CpsMovingSpeedFunction extends CpsFunctions {
|
||||
icon: Assets.speedoMeter,
|
||||
);
|
||||
|
||||
final int min;
|
||||
final int max;
|
||||
final int step;
|
||||
final double min;
|
||||
final double max;
|
||||
final double step;
|
||||
|
||||
@override
|
||||
List<CpsOperationalValue> getOperationalValues() {
|
||||
@ -173,9 +175,9 @@ final class CpsSpatialStaticValueFunction extends CpsFunctions {
|
||||
icon: Assets.spatialStaticValue,
|
||||
);
|
||||
|
||||
final int min;
|
||||
final int max;
|
||||
final int step;
|
||||
final double min;
|
||||
final double max;
|
||||
final double step;
|
||||
|
||||
@override
|
||||
List<CpsOperationalValue> getOperationalValues() {
|
||||
@ -204,9 +206,9 @@ final class CpsSpatialMotionValueFunction extends CpsFunctions {
|
||||
icon: Assets.spatialMotionValue,
|
||||
);
|
||||
|
||||
final int min;
|
||||
final int max;
|
||||
final int step;
|
||||
final double min;
|
||||
final double max;
|
||||
final double step;
|
||||
|
||||
@override
|
||||
List<CpsOperationalValue> getOperationalValues() {
|
||||
@ -375,9 +377,9 @@ final class CpsPresenceJudgementThrsholdFunction extends CpsFunctions {
|
||||
icon: Assets.presenceJudgementThrshold,
|
||||
);
|
||||
|
||||
final int min;
|
||||
final int max;
|
||||
final int step;
|
||||
final double min;
|
||||
final double max;
|
||||
final double step;
|
||||
|
||||
@override
|
||||
List<CpsOperationalValue> getOperationalValues() {
|
||||
@ -406,9 +408,9 @@ final class CpsMotionAmplitudeTriggerThresholdFunction extends CpsFunctions {
|
||||
icon: Assets.presenceJudgementThrshold,
|
||||
);
|
||||
|
||||
final int min;
|
||||
final int max;
|
||||
final int step;
|
||||
final double min;
|
||||
final double max;
|
||||
final double step;
|
||||
|
||||
@override
|
||||
List<CpsOperationalValue> getOperationalValues() {
|
||||
|
@ -4,6 +4,11 @@ abstract class DeviceFunction<T> {
|
||||
final String code;
|
||||
final String operationName;
|
||||
final String icon;
|
||||
final double? step;
|
||||
final String? unit;
|
||||
final double? max;
|
||||
final double? min;
|
||||
|
||||
|
||||
DeviceFunction({
|
||||
required this.deviceId,
|
||||
@ -11,6 +16,10 @@ abstract class DeviceFunction<T> {
|
||||
required this.code,
|
||||
required this.operationName,
|
||||
required this.icon,
|
||||
this.step,
|
||||
this.unit,
|
||||
this.max,
|
||||
this.min,
|
||||
});
|
||||
}
|
||||
|
||||
@ -22,6 +31,10 @@ class DeviceFunctionData {
|
||||
final dynamic value;
|
||||
final String? condition;
|
||||
final String? valueDescription;
|
||||
final double? step;
|
||||
final String? unit;
|
||||
final double? max;
|
||||
final double? min;
|
||||
|
||||
DeviceFunctionData({
|
||||
required this.entityId,
|
||||
@ -31,6 +44,10 @@ class DeviceFunctionData {
|
||||
required this.value,
|
||||
this.condition,
|
||||
this.valueDescription,
|
||||
this.step,
|
||||
this.unit,
|
||||
this.max,
|
||||
this.min,
|
||||
});
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
@ -42,6 +59,10 @@ class DeviceFunctionData {
|
||||
'value': value,
|
||||
if (condition != null) 'condition': condition,
|
||||
if (valueDescription != null) 'valueDescription': valueDescription,
|
||||
if (step != null) 'step': step,
|
||||
if (unit != null) 'unit': unit,
|
||||
if (max != null) 'max': max,
|
||||
if (min != null) 'min': min,
|
||||
};
|
||||
}
|
||||
|
||||
@ -54,6 +75,10 @@ class DeviceFunctionData {
|
||||
value: json['value'],
|
||||
condition: json['condition'],
|
||||
valueDescription: json['valueDescription'],
|
||||
step: json['step']?.toDouble(),
|
||||
unit: json['unit'],
|
||||
max: json['max']?.toDouble(),
|
||||
min: json['min']?.toDouble(),
|
||||
);
|
||||
}
|
||||
|
||||
@ -68,7 +93,11 @@ class DeviceFunctionData {
|
||||
other.operationName == operationName &&
|
||||
other.value == value &&
|
||||
other.condition == condition &&
|
||||
other.valueDescription == valueDescription;
|
||||
other.valueDescription == valueDescription &&
|
||||
other.step == step &&
|
||||
other.unit == unit &&
|
||||
other.max == max &&
|
||||
other.min == min;
|
||||
}
|
||||
|
||||
@override
|
||||
@ -79,6 +108,10 @@ class DeviceFunctionData {
|
||||
operationName.hashCode ^
|
||||
value.hashCode ^
|
||||
condition.hashCode ^
|
||||
valueDescription.hashCode;
|
||||
valueDescription.hashCode ^
|
||||
step.hashCode ^
|
||||
unit.hashCode ^
|
||||
max.hashCode ^
|
||||
min.hashCode;
|
||||
}
|
||||
}
|
||||
|
@ -20,12 +20,11 @@ abstract class FlushFunctions
|
||||
}
|
||||
|
||||
class FlushPresenceDelayFunction extends FlushFunctions {
|
||||
final int min;
|
||||
FlushPresenceDelayFunction({
|
||||
required super.deviceId,
|
||||
required super.deviceName,
|
||||
required super.type,
|
||||
}) : min = 0,
|
||||
}) :
|
||||
super(
|
||||
code: FlushMountedPresenceSensorModel.codePresenceState,
|
||||
operationName: 'Presence State',
|
||||
@ -50,9 +49,9 @@ class FlushPresenceDelayFunction extends FlushFunctions {
|
||||
}
|
||||
|
||||
class FlushSensiReduceFunction extends FlushFunctions {
|
||||
final int min;
|
||||
final int max;
|
||||
final int step;
|
||||
final double min;
|
||||
final double max;
|
||||
final double step;
|
||||
|
||||
FlushSensiReduceFunction({
|
||||
required super.deviceId,
|
||||
@ -80,8 +79,8 @@ class FlushSensiReduceFunction extends FlushFunctions {
|
||||
}
|
||||
|
||||
class FlushNoneDelayFunction extends FlushFunctions {
|
||||
final int min;
|
||||
final int max;
|
||||
final double min;
|
||||
final double max;
|
||||
final String unit;
|
||||
|
||||
FlushNoneDelayFunction({
|
||||
@ -110,9 +109,9 @@ class FlushNoneDelayFunction extends FlushFunctions {
|
||||
}
|
||||
|
||||
class FlushIlluminanceFunction extends FlushFunctions {
|
||||
final int min;
|
||||
final int max;
|
||||
final int step;
|
||||
final double min;
|
||||
final double max;
|
||||
final double step;
|
||||
|
||||
FlushIlluminanceFunction({
|
||||
required super.deviceId,
|
||||
@ -130,7 +129,7 @@ class FlushIlluminanceFunction extends FlushFunctions {
|
||||
@override
|
||||
List<FlushOperationalValue> getOperationalValues() {
|
||||
List<FlushOperationalValue> values = [];
|
||||
for (int lux = min; lux <= max; lux += step) {
|
||||
for (int lux = min.toInt(); lux <= max; lux += step.toInt()) {
|
||||
values.add(FlushOperationalValue(
|
||||
icon: Assets.IlluminanceIcon,
|
||||
description: "$lux Lux",
|
||||
@ -142,9 +141,9 @@ class FlushIlluminanceFunction extends FlushFunctions {
|
||||
}
|
||||
|
||||
class FlushOccurDistReduceFunction extends FlushFunctions {
|
||||
final int min;
|
||||
final int max;
|
||||
final int step;
|
||||
final double min;
|
||||
final double max;
|
||||
final double step;
|
||||
|
||||
FlushOccurDistReduceFunction({
|
||||
required super.deviceId,
|
||||
@ -173,9 +172,9 @@ class FlushOccurDistReduceFunction extends FlushFunctions {
|
||||
|
||||
// ==== then functions ====
|
||||
class FlushSensitivityFunction extends FlushFunctions {
|
||||
final int min;
|
||||
final int max;
|
||||
final int step;
|
||||
final double min;
|
||||
final double max;
|
||||
final double step;
|
||||
|
||||
FlushSensitivityFunction({
|
||||
required super.deviceId,
|
||||
@ -203,9 +202,9 @@ class FlushSensitivityFunction extends FlushFunctions {
|
||||
}
|
||||
|
||||
class FlushNearDetectionFunction extends FlushFunctions {
|
||||
final int min;
|
||||
final double min;
|
||||
final double max;
|
||||
final int step;
|
||||
final double step;
|
||||
final String unit;
|
||||
|
||||
FlushNearDetectionFunction({
|
||||
@ -225,7 +224,7 @@ class FlushNearDetectionFunction extends FlushFunctions {
|
||||
@override
|
||||
List<FlushOperationalValue> getOperationalValues() {
|
||||
final values = <FlushOperationalValue>[];
|
||||
for (var value = min; value <= max; value += step) {
|
||||
for (var value = min.toDouble(); value <= max; value += step) {
|
||||
values.add(FlushOperationalValue(
|
||||
icon: Assets.nobodyTime,
|
||||
description: '$value $unit',
|
||||
@ -237,9 +236,9 @@ class FlushNearDetectionFunction extends FlushFunctions {
|
||||
}
|
||||
|
||||
class FlushMaxDetectDistFunction extends FlushFunctions {
|
||||
final int min;
|
||||
final int max;
|
||||
final int step;
|
||||
final double min;
|
||||
final double max;
|
||||
final double step;
|
||||
final String unit;
|
||||
|
||||
FlushMaxDetectDistFunction({
|
||||
@ -259,7 +258,7 @@ class FlushMaxDetectDistFunction extends FlushFunctions {
|
||||
@override
|
||||
List<FlushOperationalValue> getOperationalValues() {
|
||||
final values = <FlushOperationalValue>[];
|
||||
for (var value = min; value <= max; value += step) {
|
||||
for (var value = min; value <= max; value += step.toInt()) {
|
||||
values.add(FlushOperationalValue(
|
||||
icon: Assets.nobodyTime,
|
||||
description: '$value $unit',
|
||||
@ -271,9 +270,9 @@ class FlushMaxDetectDistFunction extends FlushFunctions {
|
||||
}
|
||||
|
||||
class FlushTargetConfirmTimeFunction extends FlushFunctions {
|
||||
final int min;
|
||||
final int max;
|
||||
final int step;
|
||||
final double min;
|
||||
final double max;
|
||||
final double step;
|
||||
final String unit;
|
||||
|
||||
FlushTargetConfirmTimeFunction({
|
||||
@ -293,7 +292,7 @@ class FlushTargetConfirmTimeFunction extends FlushFunctions {
|
||||
@override
|
||||
List<FlushOperationalValue> getOperationalValues() {
|
||||
final values = <FlushOperationalValue>[];
|
||||
for (var value = min; value <= max; value += step) {
|
||||
for (var value = min.toDouble(); value <= max; value += step) {
|
||||
values.add(FlushOperationalValue(
|
||||
icon: Assets.nobodyTime,
|
||||
description: '$value $unit',
|
||||
@ -305,9 +304,9 @@ class FlushTargetConfirmTimeFunction extends FlushFunctions {
|
||||
}
|
||||
|
||||
class FlushDisappeDelayFunction extends FlushFunctions {
|
||||
final int min;
|
||||
final int max;
|
||||
final int step;
|
||||
final double min;
|
||||
final double max;
|
||||
final double step;
|
||||
final String unit;
|
||||
|
||||
FlushDisappeDelayFunction({
|
||||
@ -327,7 +326,7 @@ class FlushDisappeDelayFunction extends FlushFunctions {
|
||||
@override
|
||||
List<FlushOperationalValue> getOperationalValues() {
|
||||
final values = <FlushOperationalValue>[];
|
||||
for (var value = min; value <= max; value += step) {
|
||||
for (var value = min.toDouble(); value <= max; value += step) {
|
||||
values.add(FlushOperationalValue(
|
||||
icon: Assets.nobodyTime,
|
||||
description: '$value $unit',
|
||||
@ -339,9 +338,9 @@ class FlushDisappeDelayFunction extends FlushFunctions {
|
||||
}
|
||||
|
||||
class FlushIndentLevelFunction extends FlushFunctions {
|
||||
final int min;
|
||||
final int max;
|
||||
final int step;
|
||||
final double min;
|
||||
final double max;
|
||||
final double step;
|
||||
final String unit;
|
||||
|
||||
FlushIndentLevelFunction({
|
||||
@ -361,7 +360,7 @@ class FlushIndentLevelFunction extends FlushFunctions {
|
||||
@override
|
||||
List<FlushOperationalValue> getOperationalValues() {
|
||||
final values = <FlushOperationalValue>[];
|
||||
for (var value = min; value <= max; value += step) {
|
||||
for (var value = min.toDouble(); value <= max; value += step) {
|
||||
values.add(FlushOperationalValue(
|
||||
icon: Assets.nobodyTime,
|
||||
description: '$value $unit',
|
||||
@ -373,9 +372,9 @@ class FlushIndentLevelFunction extends FlushFunctions {
|
||||
}
|
||||
|
||||
class FlushTriggerLevelFunction extends FlushFunctions {
|
||||
final int min;
|
||||
final int max;
|
||||
final int step;
|
||||
final double min;
|
||||
final double max;
|
||||
final double step;
|
||||
final String unit;
|
||||
|
||||
FlushTriggerLevelFunction({
|
||||
@ -395,7 +394,7 @@ class FlushTriggerLevelFunction extends FlushFunctions {
|
||||
@override
|
||||
List<FlushOperationalValue> getOperationalValues() {
|
||||
final values = <FlushOperationalValue>[];
|
||||
for (var value = min; value <= max; value += step) {
|
||||
for (var value = min.toDouble(); value <= max; value += step) {
|
||||
values.add(FlushOperationalValue(
|
||||
icon: Assets.nobodyTime,
|
||||
description: '$value $unit',
|
||||
|
@ -0,0 +1,130 @@
|
||||
import 'package:syncrow_web/pages/device_managment/water_heater/models/water_heater_status_model.dart';
|
||||
import 'package:syncrow_web/pages/routines/models/device_functions.dart';
|
||||
import 'package:syncrow_web/pages/routines/models/water_heater/water_heater_operational_value.dart';
|
||||
import 'package:syncrow_web/utils/constants/assets.dart';
|
||||
|
||||
abstract class WaterHeaterFunctions
|
||||
extends DeviceFunction<WaterHeaterStatusModel> {
|
||||
final String type;
|
||||
|
||||
WaterHeaterFunctions({
|
||||
required super.deviceId,
|
||||
required super.deviceName,
|
||||
required super.code,
|
||||
required super.operationName,
|
||||
required super.icon,
|
||||
required this.type,
|
||||
});
|
||||
|
||||
List<WaterHeaterOperationalValue> getOperationalValues();
|
||||
}
|
||||
|
||||
class WHRestartStatusFunction extends WaterHeaterFunctions {
|
||||
WHRestartStatusFunction({
|
||||
required super.deviceId,
|
||||
required super.deviceName,
|
||||
required super.type,
|
||||
}) : super(
|
||||
code: 'relay_status',
|
||||
operationName: 'Restart Status',
|
||||
icon: Assets.refreshStatusIcon,
|
||||
);
|
||||
|
||||
|
||||
@override
|
||||
List<WaterHeaterOperationalValue> getOperationalValues() {
|
||||
return [
|
||||
WaterHeaterOperationalValue(
|
||||
icon: Assets.assetsAcPowerOFF,
|
||||
description: 'Power OFF',
|
||||
value: "off",
|
||||
),
|
||||
WaterHeaterOperationalValue(
|
||||
icon: Assets.assetsAcPower,
|
||||
description: 'Power ON',
|
||||
value: 'on',
|
||||
),
|
||||
WaterHeaterOperationalValue(
|
||||
icon: Assets.refreshStatusIcon,
|
||||
description: "Restart Memory",
|
||||
value: 'memory',
|
||||
),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
class WHSwitchFunction extends WaterHeaterFunctions {
|
||||
WHSwitchFunction({
|
||||
required super.deviceId,
|
||||
required super.deviceName,
|
||||
required super.type,
|
||||
}) : super(
|
||||
code: 'switch_1',
|
||||
operationName: 'Switch',
|
||||
icon: Assets.assetsAcPower,
|
||||
);
|
||||
|
||||
@override
|
||||
List<WaterHeaterOperationalValue> getOperationalValues() {
|
||||
return [
|
||||
WaterHeaterOperationalValue(
|
||||
icon: Assets.assetsAcPower,
|
||||
description: 'ON',
|
||||
value: true,
|
||||
),
|
||||
WaterHeaterOperationalValue(
|
||||
icon: Assets.assetsAcPowerOFF,
|
||||
description: 'OFF',
|
||||
value: false,
|
||||
),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
class TimerConfirmTimeFunction extends WaterHeaterFunctions {
|
||||
TimerConfirmTimeFunction({
|
||||
required super.deviceId,
|
||||
required super.deviceName,
|
||||
required super.type,
|
||||
}) : super(
|
||||
code: 'countdown_1',
|
||||
operationName: 'Timer',
|
||||
icon: Assets.targetConfirmTimeIcon,
|
||||
);
|
||||
|
||||
@override
|
||||
List<WaterHeaterOperationalValue> getOperationalValues() {
|
||||
final values = <WaterHeaterOperationalValue>[];
|
||||
|
||||
return values;
|
||||
}
|
||||
}
|
||||
|
||||
class BacklightFunction extends WaterHeaterFunctions {
|
||||
BacklightFunction({
|
||||
required super.deviceId,
|
||||
required super.deviceName,
|
||||
required super.type,
|
||||
}) :
|
||||
super(
|
||||
code: 'switch_backlight',
|
||||
operationName: 'Backlight',
|
||||
icon: Assets.indicator,
|
||||
);
|
||||
|
||||
@override
|
||||
List<WaterHeaterOperationalValue> getOperationalValues() {
|
||||
return [
|
||||
WaterHeaterOperationalValue(
|
||||
icon: Assets.assetsAcPower,
|
||||
description: 'ON',
|
||||
value: true,
|
||||
),
|
||||
WaterHeaterOperationalValue(
|
||||
icon: Assets.assetsAcPowerOFF,
|
||||
description: 'OFF',
|
||||
value: false,
|
||||
),
|
||||
];
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
class WaterHeaterOperationalValue {
|
||||
final String icon;
|
||||
final String description;
|
||||
final dynamic value;
|
||||
|
||||
WaterHeaterOperationalValue({
|
||||
required this.icon,
|
||||
required this.description,
|
||||
required this.value,
|
||||
});
|
||||
}
|
@ -4,7 +4,7 @@ import 'package:syncrow_web/pages/routines/models/wps/wps_operational_value.dart
|
||||
import 'package:syncrow_web/utils/constants/assets.dart';
|
||||
|
||||
abstract class WpsFunctions extends DeviceFunction<WallSensorModel> {
|
||||
final String type;
|
||||
final String type;
|
||||
|
||||
WpsFunctions({
|
||||
required super.deviceId,
|
||||
@ -13,6 +13,10 @@ abstract class WpsFunctions extends DeviceFunction<WallSensorModel> {
|
||||
required super.operationName,
|
||||
required super.icon,
|
||||
required this.type,
|
||||
super.step,
|
||||
super.unit,
|
||||
super.max,
|
||||
super.min,
|
||||
});
|
||||
|
||||
List<WpsOperationalValue> getOperationalValues();
|
||||
@ -20,9 +24,13 @@ abstract class WpsFunctions extends DeviceFunction<WallSensorModel> {
|
||||
|
||||
// For far_detection (75-600cm in 75cm steps)
|
||||
class FarDetectionFunction extends WpsFunctions {
|
||||
final int min;
|
||||
final int max;
|
||||
final int step;
|
||||
|
||||
final double min;
|
||||
@override
|
||||
final double max;
|
||||
@override
|
||||
final double step;
|
||||
@override
|
||||
final String unit;
|
||||
|
||||
FarDetectionFunction(
|
||||
@ -41,7 +49,7 @@ class FarDetectionFunction extends WpsFunctions {
|
||||
@override
|
||||
List<WpsOperationalValue> getOperationalValues() {
|
||||
final values = <WpsOperationalValue>[];
|
||||
for (var value = min; value <= max; value += step) {
|
||||
for (var value = min; value <= max; value += step.toInt()) {
|
||||
values.add(WpsOperationalValue(
|
||||
icon: Assets.currentDistanceIcon,
|
||||
description: '$value $unit',
|
||||
@ -54,9 +62,9 @@ class FarDetectionFunction extends WpsFunctions {
|
||||
|
||||
// For presence_time (0-65535 minutes)
|
||||
class PresenceTimeFunction extends WpsFunctions {
|
||||
final int min;
|
||||
final int max;
|
||||
final int step;
|
||||
final double min;
|
||||
final double max;
|
||||
final double step;
|
||||
final String unit;
|
||||
|
||||
PresenceTimeFunction(
|
||||
@ -86,9 +94,9 @@ class PresenceTimeFunction extends WpsFunctions {
|
||||
|
||||
// For motion_sensitivity_value (1-5 levels)
|
||||
class MotionSensitivityFunction extends WpsFunctions {
|
||||
final int min;
|
||||
final int max;
|
||||
final int step;
|
||||
final double min;
|
||||
final double max;
|
||||
final double step;
|
||||
|
||||
MotionSensitivityFunction(
|
||||
{required super.deviceId, required super.deviceName, required type})
|
||||
@ -116,9 +124,9 @@ class MotionSensitivityFunction extends WpsFunctions {
|
||||
}
|
||||
|
||||
class MotionLessSensitivityFunction extends WpsFunctions {
|
||||
final int min;
|
||||
final int max;
|
||||
final int step;
|
||||
final double min;
|
||||
final double max;
|
||||
final double step;
|
||||
|
||||
MotionLessSensitivityFunction(
|
||||
{required super.deviceId, required super.deviceName, required type})
|
||||
@ -171,8 +179,8 @@ class IndicatorFunction extends WpsFunctions {
|
||||
}
|
||||
|
||||
class NoOneTimeFunction extends WpsFunctions {
|
||||
final int min;
|
||||
final int max;
|
||||
final double min;
|
||||
final double max;
|
||||
final String unit;
|
||||
|
||||
NoOneTimeFunction(
|
||||
@ -225,9 +233,9 @@ class PresenceStateFunction extends WpsFunctions {
|
||||
}
|
||||
|
||||
class CurrentDistanceFunction extends WpsFunctions {
|
||||
final int min;
|
||||
final int max;
|
||||
final int step;
|
||||
final double min;
|
||||
final double max;
|
||||
final double step;
|
||||
|
||||
CurrentDistanceFunction(
|
||||
{required super.deviceId, required super.deviceName, required type})
|
||||
@ -244,11 +252,10 @@ class CurrentDistanceFunction extends WpsFunctions {
|
||||
@override
|
||||
List<WpsOperationalValue> getOperationalValues() {
|
||||
List<WpsOperationalValue> values = [];
|
||||
for (int cm = min; cm <= max; cm += step) {
|
||||
for (int cm = min.toInt(); cm <= max; cm += step.toInt()) {
|
||||
values.add(WpsOperationalValue(
|
||||
icon: Assets.assetsTempreture,
|
||||
description: "${cm}CM",
|
||||
|
||||
value: cm,
|
||||
));
|
||||
}
|
||||
@ -257,9 +264,9 @@ class CurrentDistanceFunction extends WpsFunctions {
|
||||
}
|
||||
|
||||
class IlluminanceValueFunction extends WpsFunctions {
|
||||
final int min;
|
||||
final int max;
|
||||
final int step;
|
||||
final double min;
|
||||
final double max;
|
||||
final double step;
|
||||
|
||||
IlluminanceValueFunction({
|
||||
required super.deviceId,
|
||||
@ -277,7 +284,7 @@ class IlluminanceValueFunction extends WpsFunctions {
|
||||
@override
|
||||
List<WpsOperationalValue> getOperationalValues() {
|
||||
List<WpsOperationalValue> values = [];
|
||||
for (int lux = min; lux <= max; lux += step) {
|
||||
for (int lux = min.toInt(); lux <= max; lux += step.toInt()) {
|
||||
values.add(WpsOperationalValue(
|
||||
icon: Assets.IlluminanceIcon,
|
||||
description: "$lux Lux",
|
||||
|
@ -29,11 +29,11 @@ class ConditionsRoutinesDevicesView extends StatelessWidget {
|
||||
children: [
|
||||
DraggableCard(
|
||||
imagePath: Assets.tabToRun,
|
||||
title: 'Tab to run',
|
||||
title: 'Tap to run',
|
||||
deviceData: {
|
||||
'deviceId': 'tab_to_run',
|
||||
'type': 'trigger',
|
||||
'name': 'Tab to run',
|
||||
'name': 'Tap to run',
|
||||
},
|
||||
),
|
||||
DraggableCard(
|
||||
|
297
lib/pages/routines/widgets/custom_routines_textbox.dart
Normal file
297
lib/pages/routines/widgets/custom_routines_textbox.dart
Normal file
@ -0,0 +1,297 @@
|
||||
import 'dart:math';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:syncrow_web/pages/routines/widgets/condition_toggle.dart';
|
||||
import 'package:syncrow_web/pages/routines/widgets/slider_value_selector.dart';
|
||||
import 'package:syncrow_web/utils/color_manager.dart';
|
||||
import 'package:syncrow_web/utils/extension/build_context_x.dart';
|
||||
|
||||
class CustomRoutinesTextbox extends StatefulWidget {
|
||||
final String? currentCondition;
|
||||
final String dialogType;
|
||||
final (double, double) sliderRange;
|
||||
final dynamic displayedValue;
|
||||
final dynamic initialValue;
|
||||
final void Function(String condition) onConditionChanged;
|
||||
final void Function(double value) onTextChanged;
|
||||
final String unit;
|
||||
final double dividendOfRange;
|
||||
final double stepIncreaseAmount;
|
||||
final bool withSpecialChar;
|
||||
|
||||
const CustomRoutinesTextbox({
|
||||
required this.dialogType,
|
||||
required this.sliderRange,
|
||||
required this.displayedValue,
|
||||
required this.initialValue,
|
||||
required this.onConditionChanged,
|
||||
required this.onTextChanged,
|
||||
required this.currentCondition,
|
||||
required this.unit,
|
||||
required this.dividendOfRange,
|
||||
required this.stepIncreaseAmount,
|
||||
required this.withSpecialChar,
|
||||
super.key,
|
||||
});
|
||||
|
||||
@override
|
||||
State<CustomRoutinesTextbox> createState() => _CustomRoutinesTextboxState();
|
||||
}
|
||||
|
||||
class _CustomRoutinesTextboxState extends State<CustomRoutinesTextbox> {
|
||||
late final TextEditingController _controller;
|
||||
bool hasError = false;
|
||||
String? errorMessage;
|
||||
|
||||
int getDecimalPlaces(double step) {
|
||||
String stepStr = step.toString();
|
||||
if (stepStr.contains('.')) {
|
||||
List<String> parts = stepStr.split('.');
|
||||
String decimalPart = parts[1];
|
||||
decimalPart = decimalPart.replaceAll(RegExp(r'0+$'), '');
|
||||
return decimalPart.isEmpty ? 0 : decimalPart.length;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
int decimalPlaces = getDecimalPlaces(widget.stepIncreaseAmount);
|
||||
double initialValue;
|
||||
if (widget.initialValue != null &&
|
||||
widget.initialValue is num &&
|
||||
(widget.initialValue as num) == 0) {
|
||||
initialValue = 0.0;
|
||||
} else {
|
||||
initialValue = double.tryParse(widget.displayedValue) ?? 0.0;
|
||||
}
|
||||
_controller = TextEditingController(
|
||||
text: initialValue.toStringAsFixed(decimalPlaces),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_controller.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
void _validateInput(String value) {
|
||||
final doubleValue = double.tryParse(value);
|
||||
if (doubleValue == null) {
|
||||
setState(() {
|
||||
errorMessage = "Invalid number";
|
||||
hasError = true;
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
final min = widget.sliderRange.$1;
|
||||
final max = widget.sliderRange.$2;
|
||||
|
||||
if (doubleValue < min) {
|
||||
setState(() {
|
||||
errorMessage = "Value must be at least $min";
|
||||
hasError = true;
|
||||
});
|
||||
} else if (doubleValue > max) {
|
||||
setState(() {
|
||||
errorMessage = "Value must be at most $max";
|
||||
hasError = true;
|
||||
});
|
||||
} else {
|
||||
int decimalPlaces = getDecimalPlaces(widget.stepIncreaseAmount);
|
||||
int factor = pow(10, decimalPlaces).toInt();
|
||||
int scaledStep = (widget.stepIncreaseAmount * factor).round();
|
||||
int scaledValue = (doubleValue * factor).round();
|
||||
|
||||
if (scaledValue % scaledStep != 0) {
|
||||
setState(() {
|
||||
errorMessage = "must be a multiple of ${widget.stepIncreaseAmount}";
|
||||
hasError = true;
|
||||
});
|
||||
} else {
|
||||
setState(() {
|
||||
errorMessage = null;
|
||||
hasError = false;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void didUpdateWidget(CustomRoutinesTextbox oldWidget) {
|
||||
super.didUpdateWidget(oldWidget);
|
||||
if (widget.initialValue != oldWidget.initialValue) {
|
||||
if (widget.initialValue != null &&
|
||||
widget.initialValue is num &&
|
||||
(widget.initialValue as num) == 0) {
|
||||
int decimalPlaces = getDecimalPlaces(widget.stepIncreaseAmount);
|
||||
_controller.text = 0.0.toStringAsFixed(decimalPlaces);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void _correctAndUpdateValue(String value) {
|
||||
final doubleValue = double.tryParse(value) ?? 0.0;
|
||||
int decimalPlaces = getDecimalPlaces(widget.stepIncreaseAmount);
|
||||
double rounded = (doubleValue / widget.stepIncreaseAmount).round() *
|
||||
widget.stepIncreaseAmount;
|
||||
rounded = rounded.clamp(widget.sliderRange.$1, widget.sliderRange.$2);
|
||||
rounded = double.parse(rounded.toStringAsFixed(decimalPlaces));
|
||||
|
||||
setState(() {
|
||||
hasError = false;
|
||||
errorMessage = null;
|
||||
});
|
||||
|
||||
_controller.text = rounded.toStringAsFixed(decimalPlaces);
|
||||
_controller.selection = TextSelection.fromPosition(
|
||||
TextPosition(offset: _controller.text.length),
|
||||
);
|
||||
widget.onTextChanged(rounded);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
int decimalPlaces = getDecimalPlaces(widget.stepIncreaseAmount);
|
||||
|
||||
List<TextInputFormatter> formatters = [];
|
||||
if (decimalPlaces == 0) {
|
||||
formatters.add(FilteringTextInputFormatter.digitsOnly);
|
||||
} else {
|
||||
formatters.add(FilteringTextInputFormatter.allow(
|
||||
RegExp(r'^\d*\.?\d{0,' + decimalPlaces.toString() + r'}$'),
|
||||
));
|
||||
}
|
||||
formatters.add(RangeInputFormatter(
|
||||
min: widget.sliderRange.$1,
|
||||
max: widget.sliderRange.$2,
|
||||
));
|
||||
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
if (widget.dialogType == 'IF')
|
||||
ConditionToggle(
|
||||
currentCondition: widget.currentCondition,
|
||||
onChanged: widget.onConditionChanged,
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 35, vertical: 2),
|
||||
child: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.end,
|
||||
mainAxisAlignment: MainAxisAlignment.end,
|
||||
children: [
|
||||
Text(
|
||||
'Step: ${widget.stepIncreaseAmount}',
|
||||
style: context.textTheme.bodySmall?.copyWith(
|
||||
color: ColorsManager.grayColor,
|
||||
fontSize: 10,
|
||||
fontWeight: FontWeight.w400,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
Center(
|
||||
child: Container(
|
||||
width: 170,
|
||||
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 8),
|
||||
decoration: BoxDecoration(
|
||||
color: const Color(0xFFF8F8F8),
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
border: hasError
|
||||
? Border.all(color: Colors.red, width: 1)
|
||||
: Border.all(
|
||||
color: ColorsManager.lightGrayBorderColor, width: 1),
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: ColorsManager.blackColor.withOpacity(0.05),
|
||||
blurRadius: 8,
|
||||
offset: const Offset(0, 4),
|
||||
),
|
||||
],
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: TextFormField(
|
||||
controller: _controller,
|
||||
style: context.textTheme.bodyLarge?.copyWith(
|
||||
fontSize: 20,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: ColorsManager.blackColor,
|
||||
),
|
||||
keyboardType: TextInputType.number,
|
||||
inputFormatters: widget.withSpecialChar == true
|
||||
? [FilteringTextInputFormatter.digitsOnly]
|
||||
: null,
|
||||
decoration: const InputDecoration(
|
||||
border: InputBorder.none,
|
||||
isDense: true,
|
||||
contentPadding: EdgeInsets.zero,
|
||||
),
|
||||
onChanged: _validateInput,
|
||||
onFieldSubmitted: _correctAndUpdateValue,
|
||||
onTapOutside: (_) =>
|
||||
_correctAndUpdateValue(_controller.text),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
Text(
|
||||
widget.unit,
|
||||
style: context.textTheme.bodyMedium?.copyWith(
|
||||
fontSize: 20,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: ColorsManager.vividBlue,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
if (errorMessage != null)
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(top: 2.0),
|
||||
child: Text(
|
||||
errorMessage!,
|
||||
style: context.textTheme.bodySmall?.copyWith(
|
||||
color: Colors.red,
|
||||
fontSize: 10,
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 32),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(
|
||||
'Min. ${widget.sliderRange.$1.toInt()}${widget.unit}',
|
||||
style: context.textTheme.bodySmall?.copyWith(
|
||||
color: ColorsManager.grayColor,
|
||||
fontSize: 10,
|
||||
fontWeight: FontWeight.w400,
|
||||
),
|
||||
),
|
||||
Text(
|
||||
'Max. ${widget.sliderRange.$2.toInt()}${widget.unit}',
|
||||
style: context.textTheme.bodySmall?.copyWith(
|
||||
color: ColorsManager.grayColor,
|
||||
fontSize: 10,
|
||||
fontWeight: FontWeight.w400,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
@ -43,7 +43,7 @@ class IfContainer extends StatelessWidget {
|
||||
children: [
|
||||
DraggableCard(
|
||||
imagePath: Assets.tabToRun,
|
||||
title: 'Tab to run',
|
||||
title: 'Tap to run',
|
||||
deviceData: {},
|
||||
),
|
||||
],
|
||||
@ -76,7 +76,8 @@ class IfContainer extends StatelessWidget {
|
||||
'WPS',
|
||||
'GW',
|
||||
'CPS',
|
||||
'NCPS'
|
||||
'NCPS',
|
||||
'WH',
|
||||
].contains(state.ifItems[index]
|
||||
['productType'])) {
|
||||
|
||||
@ -136,7 +137,7 @@ class IfContainer extends StatelessWidget {
|
||||
context
|
||||
.read<RoutineBloc>()
|
||||
.add(AddToIfContainer(mutableData, false));
|
||||
} else if (!['AC', '1G', '2G', '3G', 'WPS', 'GW', 'CPS', 'NCPS']
|
||||
} else if (!['AC', '1G', '2G', '3G', 'WPS', 'GW', 'CPS', 'NCPS','WH']
|
||||
.contains(mutableData['productType'])) {
|
||||
context
|
||||
.read<RoutineBloc>()
|
||||
|
@ -26,7 +26,7 @@ class FetchRoutineScenesAutomation extends StatelessWidget
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
_buildListTitle(context, "Scenes (Tab to Run)"),
|
||||
_buildListTitle(context, "Scenes (Tap to Run)"),
|
||||
const SizedBox(height: 10),
|
||||
Visibility(
|
||||
visible: state.scenes.isNotEmpty,
|
||||
|
@ -25,7 +25,8 @@ class _RoutineDevicesState extends State<RoutineDevices> {
|
||||
'WPS',
|
||||
'GW',
|
||||
'CPS',
|
||||
'NCPS'
|
||||
'NCPS',
|
||||
'WH',
|
||||
};
|
||||
|
||||
@override
|
||||
|
@ -7,6 +7,7 @@ import 'package:syncrow_web/pages/routines/bloc/routine_bloc/routine_bloc.dart';
|
||||
import 'package:syncrow_web/pages/routines/models/ac/ac_function.dart';
|
||||
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/widgets/custom_routines_textbox.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/routine_dialogs/helpers/routine_tap_function_helper.dart';
|
||||
@ -76,24 +77,20 @@ class ACHelper {
|
||||
context: context,
|
||||
acFunctions: acFunctions,
|
||||
device: device,
|
||||
onFunctionSelected: (functionCode, operationName) {
|
||||
onFunctionSelected:
|
||||
(functionCode, operationName) {
|
||||
RoutineTapFunctionHelper.onTapFunction(
|
||||
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,
|
||||
);
|
||||
context,
|
||||
functionCode: functionCode,
|
||||
functionOperationName: operationName,
|
||||
functionValueDescription:
|
||||
selectedFunctionData.valueDescription,
|
||||
deviceUuid: device?.uuid,
|
||||
codesToAddIntoFunctionsWithDefaultValue: [
|
||||
'temp_set',
|
||||
'temp_current',
|
||||
],
|
||||
defaultValue: 0);
|
||||
},
|
||||
),
|
||||
),
|
||||
@ -206,27 +203,61 @@ class ACHelper {
|
||||
required String operationName,
|
||||
bool? removeComparators,
|
||||
}) {
|
||||
final initialVal = selectedFunction == 'temp_set' ? 200 : -100;
|
||||
final selectedFn =
|
||||
acFunctions.firstWhere((f) => f.code == selectedFunction);
|
||||
|
||||
if (selectedFunction == 'temp_set' || selectedFunction == 'temp_current') {
|
||||
final initialValue = selectedFunctionData?.value ?? initialVal;
|
||||
return _buildTemperatureSelector(
|
||||
context: context,
|
||||
initialValue: initialValue,
|
||||
selectCode: selectedFunction,
|
||||
// Convert stored integer value to display value
|
||||
final displayValue =
|
||||
(selectedFunctionData?.value ?? selectedFn.min ?? 0) / 10;
|
||||
final minValue = selectedFn.min! / 10;
|
||||
final maxValue = selectedFn.max! / 10;
|
||||
return CustomRoutinesTextbox(
|
||||
withSpecialChar: true,
|
||||
dividendOfRange: maxValue,
|
||||
currentCondition: selectedFunctionData?.condition,
|
||||
device: device,
|
||||
operationName: operationName,
|
||||
selectedFunctionData: selectedFunctionData,
|
||||
removeComparators: removeComparators,
|
||||
dialogType: selectedFn.type,
|
||||
sliderRange: (minValue, maxValue),
|
||||
displayedValue: displayValue.toStringAsFixed(1),
|
||||
initialValue: displayValue.toDouble(),
|
||||
unit: selectedFn.unit!,
|
||||
onConditionChanged: (condition) => context.read<FunctionBloc>().add(
|
||||
AddFunction(
|
||||
functionData: DeviceFunctionData(
|
||||
entityId: device?.uuid ?? '',
|
||||
functionCode: selectedFunction,
|
||||
operationName: selectedFn.operationName,
|
||||
condition: condition,
|
||||
value: 0,
|
||||
step: selectedFn.step,
|
||||
unit: selectedFn.unit,
|
||||
max: selectedFn.max,
|
||||
min: selectedFn.min,
|
||||
),
|
||||
),
|
||||
),
|
||||
onTextChanged: (value) => context.read<FunctionBloc>().add(
|
||||
AddFunction(
|
||||
functionData: DeviceFunctionData(
|
||||
entityId: device?.uuid ?? '',
|
||||
functionCode: selectedFunction,
|
||||
operationName: selectedFn.operationName,
|
||||
value: (value * 10).round(), // Store as integer
|
||||
condition: selectedFunctionData?.condition,
|
||||
step: selectedFn.step,
|
||||
unit: selectedFn.unit,
|
||||
max: selectedFn.max,
|
||||
min: selectedFn.min,
|
||||
),
|
||||
),
|
||||
),
|
||||
stepIncreaseAmount: selectedFn.step! / 10, // Convert step for display
|
||||
);
|
||||
}
|
||||
|
||||
final selectedFn = acFunctions.firstWhere((f) => f.code == selectedFunction);
|
||||
final values = selectedFn.getOperationalValues();
|
||||
|
||||
return _buildOperationalValuesList(
|
||||
context: context,
|
||||
values: values,
|
||||
values: selectedFn.getOperationalValues(),
|
||||
selectedValue: selectedFunctionData?.value,
|
||||
device: device,
|
||||
operationName: operationName,
|
||||
@ -235,150 +266,151 @@ class ACHelper {
|
||||
);
|
||||
}
|
||||
|
||||
/// Build temperature selector for AC functions dialog
|
||||
static Widget _buildTemperatureSelector({
|
||||
required BuildContext context,
|
||||
required dynamic initialValue,
|
||||
required String? currentCondition,
|
||||
required String selectCode,
|
||||
AllDevicesModel? device,
|
||||
required String operationName,
|
||||
DeviceFunctionData? selectedFunctionData,
|
||||
bool? removeComparators,
|
||||
}) {
|
||||
return Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
if (removeComparators != true)
|
||||
_buildConditionToggle(
|
||||
context,
|
||||
currentCondition,
|
||||
selectCode,
|
||||
device,
|
||||
operationName,
|
||||
selectedFunctionData,
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
_buildTemperatureDisplay(
|
||||
context,
|
||||
initialValue,
|
||||
device,
|
||||
operationName,
|
||||
selectedFunctionData,
|
||||
selectCode,
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
_buildTemperatureSlider(
|
||||
context,
|
||||
initialValue,
|
||||
device,
|
||||
operationName,
|
||||
selectedFunctionData,
|
||||
selectCode,
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
// /// Build temperature selector for AC functions dialog
|
||||
// static Widget _buildTemperatureSelector({
|
||||
// required BuildContext context,
|
||||
// required dynamic initialValue,
|
||||
// required String? currentCondition,
|
||||
// required String selectCode,
|
||||
// AllDevicesModel? device,
|
||||
// required String operationName,
|
||||
// DeviceFunctionData? selectedFunctionData,
|
||||
// bool? removeComparators,
|
||||
// }) {
|
||||
// return Column(
|
||||
// mainAxisAlignment: MainAxisAlignment.center,
|
||||
// children: [
|
||||
// if (removeComparators != true)
|
||||
// _buildConditionToggle(
|
||||
// context,
|
||||
// currentCondition,
|
||||
// selectCode,
|
||||
// device,
|
||||
// operationName,
|
||||
// selectedFunctionData,
|
||||
// ),
|
||||
// const SizedBox(height: 20),
|
||||
// _buildTemperatureDisplay(
|
||||
// context,
|
||||
// initialValue,
|
||||
// device,
|
||||
// operationName,
|
||||
// selectedFunctionData,
|
||||
// selectCode,
|
||||
// ),
|
||||
// const SizedBox(height: 20),
|
||||
// _buildTemperatureSlider(
|
||||
// context,
|
||||
// initialValue,
|
||||
// device,
|
||||
// operationName,
|
||||
// selectedFunctionData,
|
||||
// selectCode,
|
||||
// ),
|
||||
// ],
|
||||
// );
|
||||
// }
|
||||
|
||||
/// Build condition toggle for AC functions dialog
|
||||
static Widget _buildConditionToggle(
|
||||
BuildContext context,
|
||||
String? currentCondition,
|
||||
String selectCode,
|
||||
AllDevicesModel? device,
|
||||
String operationName,
|
||||
DeviceFunctionData? selectedFunctionData,
|
||||
// /// Build condition toggle for AC functions dialog
|
||||
// static Widget _buildConditionToggle(
|
||||
// BuildContext context,
|
||||
// String? currentCondition,
|
||||
// String selectCode,
|
||||
// AllDevicesModel? device,
|
||||
// String operationName,
|
||||
// DeviceFunctionData? selectedFunctionData,
|
||||
|
||||
// Function(String) onConditionChanged,
|
||||
) {
|
||||
final conditions = ["<", "==", ">"];
|
||||
// // Function(String) onConditionChanged,
|
||||
// ) {
|
||||
// final conditions = ["<", "==", ">"];
|
||||
|
||||
return ToggleButtons(
|
||||
onPressed: (int index) {
|
||||
context.read<FunctionBloc>().add(
|
||||
AddFunction(
|
||||
functionData: DeviceFunctionData(
|
||||
entityId: device?.uuid ?? '',
|
||||
functionCode: selectCode,
|
||||
operationName: operationName,
|
||||
condition: conditions[index],
|
||||
value: selectedFunctionData?.value ?? selectCode == 'temp_set'
|
||||
? 200
|
||||
: -100,
|
||||
valueDescription: selectedFunctionData?.valueDescription,
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
borderRadius: const BorderRadius.all(Radius.circular(8)),
|
||||
selectedBorderColor: ColorsManager.primaryColorWithOpacity,
|
||||
selectedColor: Colors.white,
|
||||
fillColor: ColorsManager.primaryColorWithOpacity,
|
||||
color: ColorsManager.primaryColorWithOpacity,
|
||||
constraints: const BoxConstraints(
|
||||
minHeight: 40.0,
|
||||
minWidth: 40.0,
|
||||
),
|
||||
isSelected: conditions.map((c) => c == (currentCondition ?? "==")).toList(),
|
||||
children: conditions.map((c) => Text(c)).toList(),
|
||||
);
|
||||
}
|
||||
// return ToggleButtons(
|
||||
// onPressed: (int index) {
|
||||
// context.read<FunctionBloc>().add(
|
||||
// AddFunction(
|
||||
// functionData: DeviceFunctionData(
|
||||
// entityId: device?.uuid ?? '',
|
||||
// functionCode: selectCode,
|
||||
// operationName: operationName,
|
||||
// condition: conditions[index],
|
||||
// value: selectedFunctionData?.value ?? selectCode == 'temp_set'
|
||||
// ? 200
|
||||
// : -100,
|
||||
// valueDescription: selectedFunctionData?.valueDescription,
|
||||
// ),
|
||||
// ),
|
||||
// );
|
||||
// },
|
||||
// borderRadius: const BorderRadius.all(Radius.circular(8)),
|
||||
// selectedBorderColor: ColorsManager.primaryColorWithOpacity,
|
||||
// selectedColor: Colors.white,
|
||||
// fillColor: ColorsManager.primaryColorWithOpacity,
|
||||
// color: ColorsManager.primaryColorWithOpacity,
|
||||
// constraints: const BoxConstraints(
|
||||
// minHeight: 40.0,
|
||||
// minWidth: 40.0,
|
||||
// ),
|
||||
// isSelected:
|
||||
// conditions.map((c) => c == (currentCondition ?? "==")).toList(),
|
||||
// children: conditions.map((c) => Text(c)).toList(),
|
||||
// );
|
||||
// }
|
||||
|
||||
/// Build temperature display for AC functions dialog
|
||||
static Widget _buildTemperatureDisplay(
|
||||
BuildContext context,
|
||||
dynamic initialValue,
|
||||
AllDevicesModel? device,
|
||||
String operationName,
|
||||
DeviceFunctionData? selectedFunctionData,
|
||||
String selectCode,
|
||||
) {
|
||||
final initialVal = selectCode == 'temp_set' ? 200 : -100;
|
||||
return Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 10),
|
||||
decoration: BoxDecoration(
|
||||
color: ColorsManager.primaryColorWithOpacity.withOpacity(0.1),
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
),
|
||||
child: Text(
|
||||
'${(initialValue ?? initialVal) / 10}°C',
|
||||
style: context.textTheme.headlineMedium!.copyWith(
|
||||
color: ColorsManager.primaryColorWithOpacity,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
// /// Build temperature display for AC functions dialog
|
||||
// static Widget _buildTemperatureDisplay(
|
||||
// BuildContext context,
|
||||
// dynamic initialValue,
|
||||
// AllDevicesModel? device,
|
||||
// String operationName,
|
||||
// DeviceFunctionData? selectedFunctionData,
|
||||
// String selectCode,
|
||||
// ) {
|
||||
// final initialVal = selectCode == 'temp_set' ? 200 : -100;
|
||||
// return Container(
|
||||
// padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 10),
|
||||
// decoration: BoxDecoration(
|
||||
// color: ColorsManager.primaryColorWithOpacity.withOpacity(0.1),
|
||||
// borderRadius: BorderRadius.circular(10),
|
||||
// ),
|
||||
// child: Text(
|
||||
// '${(initialValue ?? initialVal) / 10}°C',
|
||||
// style: context.textTheme.headlineMedium!.copyWith(
|
||||
// color: ColorsManager.primaryColorWithOpacity,
|
||||
// ),
|
||||
// ),
|
||||
// );
|
||||
// }
|
||||
|
||||
static Widget _buildTemperatureSlider(
|
||||
BuildContext context,
|
||||
dynamic initialValue,
|
||||
AllDevicesModel? device,
|
||||
String operationName,
|
||||
DeviceFunctionData? selectedFunctionData,
|
||||
String selectCode,
|
||||
) {
|
||||
return Slider(
|
||||
value: initialValue is int ? initialValue.toDouble() : 200.0,
|
||||
min: selectCode == 'temp_current' ? -100 : 200,
|
||||
max: selectCode == 'temp_current' ? 900 : 300,
|
||||
divisions: 10,
|
||||
label: '${((initialValue ?? 160) / 10).toInt()}°C',
|
||||
onChanged: (value) {
|
||||
context.read<FunctionBloc>().add(
|
||||
AddFunction(
|
||||
functionData: DeviceFunctionData(
|
||||
entityId: device?.uuid ?? '',
|
||||
functionCode: selectCode,
|
||||
operationName: operationName,
|
||||
value: value,
|
||||
condition: selectedFunctionData?.condition,
|
||||
valueDescription: selectedFunctionData?.valueDescription,
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
// static Widget _buildTemperatureSlider(
|
||||
// BuildContext context,
|
||||
// dynamic initialValue,
|
||||
// AllDevicesModel? device,
|
||||
// String operationName,
|
||||
// DeviceFunctionData? selectedFunctionData,
|
||||
// String selectCode,
|
||||
// ) {
|
||||
// return Slider(
|
||||
// value: initialValue is int ? initialValue.toDouble() : 200.0,
|
||||
// min: selectCode == 'temp_current' ? -100 : 200,
|
||||
// max: selectCode == 'temp_current' ? 900 : 300,
|
||||
// divisions: 10,
|
||||
// label: '${((initialValue ?? 160) / 10).toInt()}°C',
|
||||
// onChanged: (value) {
|
||||
// context.read<FunctionBloc>().add(
|
||||
// AddFunction(
|
||||
// functionData: DeviceFunctionData(
|
||||
// entityId: device?.uuid ?? '',
|
||||
// functionCode: selectCode,
|
||||
// operationName: operationName,
|
||||
// value: value,
|
||||
// condition: selectedFunctionData?.condition,
|
||||
// valueDescription: selectedFunctionData?.valueDescription,
|
||||
// ),
|
||||
// ),
|
||||
// );
|
||||
// },
|
||||
// );
|
||||
// }
|
||||
|
||||
static Widget _buildOperationalValuesList({
|
||||
required BuildContext context,
|
||||
@ -414,7 +446,9 @@ class ACHelper {
|
||||
style: context.textTheme.bodyMedium,
|
||||
),
|
||||
trailing: Icon(
|
||||
isSelected ? Icons.radio_button_checked : Icons.radio_button_unchecked,
|
||||
isSelected
|
||||
? Icons.radio_button_checked
|
||||
: Icons.radio_button_unchecked,
|
||||
size: 24,
|
||||
color: isSelected
|
||||
? ColorsManager.primaryColorWithOpacity
|
||||
@ -430,7 +464,8 @@ class ACHelper {
|
||||
operationName: operationName,
|
||||
value: value.value,
|
||||
condition: selectedFunctionData?.condition,
|
||||
valueDescription: selectedFunctionData?.valueDescription,
|
||||
valueDescription:
|
||||
selectedFunctionData?.valueDescription,
|
||||
),
|
||||
),
|
||||
);
|
||||
|
@ -41,7 +41,8 @@ class _CeilingSensorDialogState extends State<CeilingSensorDialog> {
|
||||
void initState() {
|
||||
super.initState();
|
||||
|
||||
_cpsFunctions = widget.functions.whereType<CpsFunctions>().where((function) {
|
||||
_cpsFunctions =
|
||||
widget.functions.whereType<CpsFunctions>().where((function) {
|
||||
if (widget.dialogType == 'THEN') {
|
||||
return function.type == 'THEN' || function.type == 'BOTH';
|
||||
}
|
||||
@ -149,6 +150,7 @@ class _CeilingSensorDialogState extends State<CeilingSensorDialog> {
|
||||
device: widget.device,
|
||||
)
|
||||
: CpsDialogSliderSelector(
|
||||
step: selectedCpsFunctions.step!,
|
||||
operations: operations,
|
||||
selectedFunction: selectedFunction ?? '',
|
||||
selectedFunctionData: selectedFunctionData,
|
||||
|
@ -4,6 +4,7 @@ import 'package:syncrow_web/pages/device_managment/all_devices/models/devices_mo
|
||||
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/device_functions.dart';
|
||||
import 'package:syncrow_web/pages/routines/widgets/custom_routines_textbox.dart';
|
||||
import 'package:syncrow_web/pages/routines/widgets/routine_dialogs/ceiling_sensor/cps_slider_helpers.dart';
|
||||
import 'package:syncrow_web/pages/routines/widgets/slider_value_selector.dart';
|
||||
|
||||
@ -16,6 +17,7 @@ class CpsDialogSliderSelector extends StatelessWidget {
|
||||
required this.device,
|
||||
required this.operationName,
|
||||
required this.dialogType,
|
||||
required this.step,
|
||||
super.key,
|
||||
});
|
||||
|
||||
@ -26,13 +28,16 @@ class CpsDialogSliderSelector extends StatelessWidget {
|
||||
final AllDevicesModel? device;
|
||||
final String operationName;
|
||||
final String dialogType;
|
||||
final double step;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return SliderValueSelector(
|
||||
return CustomRoutinesTextbox(
|
||||
withSpecialChar: false,
|
||||
currentCondition: selectedFunctionData.condition,
|
||||
dialogType: dialogType,
|
||||
sliderRange: CpsSliderHelpers.sliderRange(selectedFunctionData.functionCode),
|
||||
sliderRange:
|
||||
CpsSliderHelpers.sliderRange(selectedFunctionData.functionCode),
|
||||
displayedValue: CpsSliderHelpers.displayText(
|
||||
value: selectedFunctionData.value,
|
||||
functionCode: selectedFunctionData.functionCode,
|
||||
@ -50,7 +55,7 @@ class CpsDialogSliderSelector extends StatelessWidget {
|
||||
),
|
||||
),
|
||||
),
|
||||
onSliderChanged: (value) => context.read<FunctionBloc>().add(
|
||||
onTextChanged: (value) => context.read<FunctionBloc>().add(
|
||||
AddFunction(
|
||||
functionData: DeviceFunctionData(
|
||||
entityId: device?.uuid ?? '',
|
||||
@ -64,6 +69,7 @@ class CpsDialogSliderSelector extends StatelessWidget {
|
||||
dividendOfRange: CpsSliderHelpers.dividendOfRange(
|
||||
selectedFunctionData.functionCode,
|
||||
),
|
||||
stepIncreaseAmount: step,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -34,30 +34,33 @@ class CpsFunctionsList extends StatelessWidget {
|
||||
itemBuilder: (context, index) {
|
||||
final function = cpsFunctions[index];
|
||||
return RoutineDialogFunctionListTile(
|
||||
iconPath: function.icon,
|
||||
operationName: function.operationName,
|
||||
onTap: () => RoutineTapFunctionHelper.onTapFunction(
|
||||
context,
|
||||
functionCode: function.code,
|
||||
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',
|
||||
],
|
||||
),
|
||||
);
|
||||
iconPath: function.icon,
|
||||
operationName: function.operationName,
|
||||
onTap: () {
|
||||
RoutineTapFunctionHelper.onTapFunction(
|
||||
context,
|
||||
step: function.step,
|
||||
functionCode: function.code,
|
||||
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',
|
||||
],
|
||||
);
|
||||
});
|
||||
},
|
||||
),
|
||||
);
|
||||
|
@ -1,11 +1,7 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:flutter_svg/flutter_svg.dart';
|
||||
import 'package:flutter_svg/svg.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/device_functions.dart';
|
||||
import 'package:syncrow_web/pages/routines/models/flush/flush_operational_value.dart';
|
||||
import 'package:syncrow_web/pages/routines/widgets/routine_dialogs/wall_sensor/time_wheel.dart';
|
||||
|
||||
class FlushOperationalValuesList extends StatelessWidget {
|
||||
final List<FlushOperationalValue> values;
|
||||
@ -26,22 +22,20 @@ class FlushOperationalValuesList extends StatelessWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return ListView.builder(
|
||||
padding: const EdgeInsets.all(20),
|
||||
itemCount: values.length,
|
||||
itemBuilder: (context, index) =>
|
||||
_buildValueItem(context, values[index]),
|
||||
);
|
||||
return ListView.builder(
|
||||
padding: const EdgeInsets.all(20),
|
||||
itemCount: values.length,
|
||||
itemBuilder: (context, index) => _buildValueItem(context, values[index]),
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
|
||||
Widget _buildValueItem(BuildContext context, FlushOperationalValue value) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
SvgPicture.asset(value.icon, width: 25, height: 25),
|
||||
Expanded(child: _buildValueDescription(value)),
|
||||
_buildValueRadio(context, value),
|
||||
],
|
||||
@ -49,9 +43,6 @@ class FlushOperationalValuesList extends StatelessWidget {
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
Widget _buildValueDescription(FlushOperationalValue value) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 8.0),
|
||||
@ -65,6 +56,4 @@ class FlushOperationalValuesList extends StatelessWidget {
|
||||
groupValue: selectedValue,
|
||||
onChanged: (_) => onSelect(value));
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
@ -5,6 +5,7 @@ import 'package:syncrow_web/pages/device_managment/flush_mounted_presence_sensor
|
||||
import 'package:syncrow_web/pages/routines/bloc/functions_bloc/functions_bloc_bloc.dart';
|
||||
import 'package:syncrow_web/pages/routines/models/device_functions.dart';
|
||||
import 'package:syncrow_web/pages/routines/models/flush/flush_functions.dart';
|
||||
import 'package:syncrow_web/pages/routines/widgets/custom_routines_textbox.dart';
|
||||
import 'package:syncrow_web/pages/routines/widgets/routine_dialogs/flush_presence_sensor/flush_operational_values_list.dart';
|
||||
import 'package:syncrow_web/pages/routines/widgets/slider_value_selector.dart';
|
||||
|
||||
@ -66,7 +67,8 @@ class FlushValueSelectorWidget extends StatelessWidget {
|
||||
if (isDistanceDetection) {
|
||||
initialValue = initialValue / 100;
|
||||
}
|
||||
return SliderValueSelector(
|
||||
return CustomRoutinesTextbox(
|
||||
withSpecialChar: true,
|
||||
currentCondition: functionData.condition,
|
||||
dialogType: dialogType,
|
||||
sliderRange: sliderRange,
|
||||
@ -83,7 +85,7 @@ class FlushValueSelectorWidget extends StatelessWidget {
|
||||
),
|
||||
),
|
||||
),
|
||||
onSliderChanged: (value) {
|
||||
onTextChanged: (value) {
|
||||
final roundedValue = _roundToStep(value, stepSize);
|
||||
final finalValue =
|
||||
isDistanceDetection ? (roundedValue * 100).toInt() : roundedValue;
|
||||
@ -102,6 +104,7 @@ class FlushValueSelectorWidget extends StatelessWidget {
|
||||
},
|
||||
unit: _unit,
|
||||
dividendOfRange: stepSize,
|
||||
stepIncreaseAmount: stepSize,
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -8,6 +8,7 @@ abstract final class RoutineTapFunctionHelper {
|
||||
|
||||
static void onTapFunction(
|
||||
BuildContext context, {
|
||||
double? step,
|
||||
required String functionCode,
|
||||
required String functionOperationName,
|
||||
required String? functionValueDescription,
|
||||
|
@ -4,11 +4,11 @@ import 'package:flutter_svg/flutter_svg.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/bloc/routine_bloc/routine_bloc.dart';
|
||||
import 'package:syncrow_web/pages/routines/helper/duration_format_helper.dart';
|
||||
import 'package:syncrow_web/pages/routines/models/device_functions.dart';
|
||||
import 'package:syncrow_web/pages/routines/models/gang_switches/base_switch_function.dart';
|
||||
import 'package:syncrow_web/pages/routines/models/gang_switches/one_gang_switch/one_gang_switch.dart';
|
||||
import 'package:syncrow_web/pages/routines/models/gang_switches/switch_operational_value.dart';
|
||||
import 'package:syncrow_web/pages/routines/widgets/custom_routines_textbox.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/routine_dialogs/helpers/routine_tap_function_helper.dart';
|
||||
@ -87,14 +87,15 @@ class OneGangSwitchHelper {
|
||||
size: 16,
|
||||
color: ColorsManager.textGray,
|
||||
),
|
||||
onTap: () =>
|
||||
RoutineTapFunctionHelper.onTapFunction(
|
||||
onTap: () => RoutineTapFunctionHelper
|
||||
.onTapFunction(
|
||||
context,
|
||||
functionCode: function.code,
|
||||
functionOperationName:
|
||||
function.operationName,
|
||||
functionValueDescription:
|
||||
selectedFunctionData.valueDescription,
|
||||
selectedFunctionData
|
||||
.valueDescription,
|
||||
deviceUuid: device?.uuid,
|
||||
codesToAddIntoFunctionsWithDefaultValue: [
|
||||
'countdown_1',
|
||||
@ -108,14 +109,16 @@ class OneGangSwitchHelper {
|
||||
if (selectedFunction != null)
|
||||
Expanded(
|
||||
child: _buildValueSelector(
|
||||
context: context,
|
||||
selectedFunction: selectedFunction,
|
||||
selectedFunctionData: selectedFunctionData,
|
||||
acFunctions: oneGangFunctions,
|
||||
device: device,
|
||||
operationName: selectedOperationName ?? '',
|
||||
removeComparetors: removeComparetors,
|
||||
),
|
||||
context: context,
|
||||
selectedFunction: selectedFunction,
|
||||
selectedFunctionData:
|
||||
selectedFunctionData,
|
||||
acFunctions: oneGangFunctions,
|
||||
device: device,
|
||||
operationName:
|
||||
selectedOperationName ?? '',
|
||||
removeComparetors: removeComparetors,
|
||||
dialogType: dialogType),
|
||||
),
|
||||
],
|
||||
),
|
||||
@ -172,6 +175,7 @@ class OneGangSwitchHelper {
|
||||
AllDevicesModel? device,
|
||||
required String operationName,
|
||||
required bool removeComparetors,
|
||||
required String dialogType,
|
||||
}) {
|
||||
if (selectedFunction == 'countdown_1') {
|
||||
final initialValue = selectedFunctionData?.value ?? 0;
|
||||
@ -184,6 +188,7 @@ class OneGangSwitchHelper {
|
||||
operationName: operationName,
|
||||
selectedFunctionData: selectedFunctionData,
|
||||
removeComparetors: removeComparetors,
|
||||
dialogType: dialogType,
|
||||
);
|
||||
}
|
||||
final selectedFn = acFunctions.firstWhere(
|
||||
@ -216,93 +221,18 @@ class OneGangSwitchHelper {
|
||||
required String operationName,
|
||||
DeviceFunctionData? selectedFunctionData,
|
||||
required bool removeComparetors,
|
||||
String? dialogType,
|
||||
}) {
|
||||
return Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
if (removeComparetors != true)
|
||||
_buildConditionToggle(
|
||||
context,
|
||||
currentCondition,
|
||||
selectCode,
|
||||
device,
|
||||
operationName,
|
||||
selectedFunctionData,
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
_buildCountDownDisplay(context, initialValue, device, operationName,
|
||||
selectedFunctionData, selectCode),
|
||||
const SizedBox(height: 20),
|
||||
_buildCountDownSlider(context, initialValue, device, operationName,
|
||||
selectedFunctionData, selectCode),
|
||||
selectedFunctionData, selectCode, dialogType!),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
/// Build condition toggle for AC functions dialog
|
||||
static Widget _buildConditionToggle(
|
||||
BuildContext context,
|
||||
String? currentCondition,
|
||||
String selectCode,
|
||||
AllDevicesModel? device,
|
||||
String operationName,
|
||||
DeviceFunctionData? selectedFunctionData,
|
||||
// Function(String) onConditionChanged,
|
||||
) {
|
||||
final conditions = ["<", "==", ">"];
|
||||
|
||||
return ToggleButtons(
|
||||
onPressed: (int index) {
|
||||
context.read<FunctionBloc>().add(
|
||||
AddFunction(
|
||||
functionData: DeviceFunctionData(
|
||||
entityId: device?.uuid ?? '',
|
||||
functionCode: selectCode,
|
||||
operationName: operationName,
|
||||
condition: conditions[index],
|
||||
value: selectedFunctionData?.value ?? 0,
|
||||
valueDescription: selectedFunctionData?.valueDescription,
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
borderRadius: const BorderRadius.all(Radius.circular(8)),
|
||||
selectedBorderColor: ColorsManager.primaryColorWithOpacity,
|
||||
selectedColor: Colors.white,
|
||||
fillColor: ColorsManager.primaryColorWithOpacity,
|
||||
color: ColorsManager.primaryColorWithOpacity,
|
||||
constraints: const BoxConstraints(
|
||||
minHeight: 40.0,
|
||||
minWidth: 40.0,
|
||||
),
|
||||
isSelected: conditions.map((c) => c == (currentCondition ?? "==")).toList(),
|
||||
children: conditions.map((c) => Text(c)).toList(),
|
||||
);
|
||||
}
|
||||
|
||||
/// Build temperature display for AC functions dialog
|
||||
static Widget _buildCountDownDisplay(
|
||||
BuildContext context,
|
||||
dynamic initialValue,
|
||||
AllDevicesModel? device,
|
||||
String operationName,
|
||||
DeviceFunctionData? selectedFunctionData,
|
||||
String selectCode) {
|
||||
return Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 10),
|
||||
decoration: BoxDecoration(
|
||||
color: ColorsManager.primaryColorWithOpacity.withOpacity(0.1),
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
),
|
||||
child: Text(
|
||||
DurationFormatMixin.formatDuration(initialValue?.toInt() ?? 0),
|
||||
style: context.textTheme.headlineMedium!.copyWith(
|
||||
color: ColorsManager.primaryColorWithOpacity,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
static Widget _buildCountDownSlider(
|
||||
BuildContext context,
|
||||
dynamic initialValue,
|
||||
@ -310,38 +240,47 @@ class OneGangSwitchHelper {
|
||||
String operationName,
|
||||
DeviceFunctionData? selectedFunctionData,
|
||||
String selectCode,
|
||||
String dialogType,
|
||||
) {
|
||||
const twelveHoursInSeconds = 43200.0;
|
||||
final operationalValues = SwitchOperationalValue(
|
||||
icon: '',
|
||||
description: "sec",
|
||||
value: 0.0,
|
||||
minValue: 0,
|
||||
maxValue: twelveHoursInSeconds,
|
||||
stepValue: 1,
|
||||
);
|
||||
return Slider(
|
||||
value: (initialValue ?? 0).toDouble(),
|
||||
min: operationalValues.minValue?.toDouble() ?? 0.0,
|
||||
max: operationalValues.maxValue?.toDouble() ?? 0.0,
|
||||
divisions:
|
||||
(((operationalValues.maxValue ?? 0) - (operationalValues.minValue ?? 0)) /
|
||||
(operationalValues.stepValue ?? 1))
|
||||
.round(),
|
||||
onChanged: (value) {
|
||||
return CustomRoutinesTextbox(
|
||||
withSpecialChar: false,
|
||||
currentCondition: selectedFunctionData?.condition,
|
||||
dialogType: dialogType,
|
||||
sliderRange: (0, 43200),
|
||||
displayedValue: (initialValue ?? 0).toString(),
|
||||
initialValue: (initialValue ?? 0).toString(),
|
||||
onConditionChanged: (condition) {
|
||||
context.read<FunctionBloc>().add(
|
||||
AddFunction(
|
||||
functionData: DeviceFunctionData(
|
||||
entityId: device?.uuid ?? '',
|
||||
functionCode: selectCode,
|
||||
operationName: operationName,
|
||||
value: value,
|
||||
condition: condition,
|
||||
value: selectedFunctionData?.value ?? 0,
|
||||
valueDescription: selectedFunctionData?.valueDescription,
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
onTextChanged: (value) {
|
||||
final roundedValue = value.round();
|
||||
context.read<FunctionBloc>().add(
|
||||
AddFunction(
|
||||
functionData: DeviceFunctionData(
|
||||
entityId: device?.uuid ?? '',
|
||||
functionCode: selectCode,
|
||||
operationName: operationName,
|
||||
value: roundedValue,
|
||||
condition: selectedFunctionData?.condition,
|
||||
valueDescription: selectedFunctionData?.valueDescription,
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
unit: 'sec',
|
||||
dividendOfRange: 1,
|
||||
stepIncreaseAmount: 1,
|
||||
);
|
||||
}
|
||||
|
||||
@ -377,7 +316,9 @@ class OneGangSwitchHelper {
|
||||
style: context.textTheme.bodyMedium,
|
||||
),
|
||||
trailing: Icon(
|
||||
isSelected ? Icons.radio_button_checked : Icons.radio_button_unchecked,
|
||||
isSelected
|
||||
? Icons.radio_button_checked
|
||||
: Icons.radio_button_unchecked,
|
||||
size: 24,
|
||||
color: isSelected
|
||||
? ColorsManager.primaryColorWithOpacity
|
||||
@ -393,7 +334,8 @@ class OneGangSwitchHelper {
|
||||
operationName: operationName,
|
||||
value: value.value,
|
||||
condition: selectedFunctionData?.condition,
|
||||
valueDescription: selectedFunctionData?.valueDescription,
|
||||
valueDescription:
|
||||
selectedFunctionData?.valueDescription,
|
||||
),
|
||||
),
|
||||
);
|
||||
|
@ -4,10 +4,10 @@ import 'package:flutter_svg/flutter_svg.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/bloc/routine_bloc/routine_bloc.dart';
|
||||
import 'package:syncrow_web/pages/routines/helper/duration_format_helper.dart';
|
||||
import 'package:syncrow_web/pages/routines/models/device_functions.dart';
|
||||
import 'package:syncrow_web/pages/routines/models/gang_switches/base_switch_function.dart';
|
||||
import 'package:syncrow_web/pages/routines/models/gang_switches/switch_operational_value.dart';
|
||||
import 'package:syncrow_web/pages/routines/widgets/custom_routines_textbox.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/routine_dialogs/helpers/routine_tap_function_helper.dart';
|
||||
@ -86,20 +86,21 @@ class ThreeGangSwitchHelper {
|
||||
size: 16,
|
||||
color: ColorsManager.textGray,
|
||||
),
|
||||
onTap: () =>
|
||||
RoutineTapFunctionHelper.onTapFunction(
|
||||
onTap: () => RoutineTapFunctionHelper
|
||||
.onTapFunction(
|
||||
context,
|
||||
functionCode: function.code,
|
||||
functionOperationName:
|
||||
function.operationName,
|
||||
functionValueDescription:
|
||||
selectedFunctionData.valueDescription,
|
||||
selectedFunctionData
|
||||
.valueDescription,
|
||||
deviceUuid: device?.uuid,
|
||||
codesToAddIntoFunctionsWithDefaultValue: [
|
||||
'countdown_1',
|
||||
'countdown_2',
|
||||
'countdown_3',
|
||||
],
|
||||
codesToAddIntoFunctionsWithDefaultValue:
|
||||
function.code
|
||||
.startsWith('countdown')
|
||||
? [function.code]
|
||||
: [],
|
||||
),
|
||||
);
|
||||
},
|
||||
@ -109,14 +110,16 @@ class ThreeGangSwitchHelper {
|
||||
if (selectedFunction != null)
|
||||
Expanded(
|
||||
child: _buildValueSelector(
|
||||
context: context,
|
||||
selectedFunction: selectedFunction,
|
||||
selectedFunctionData: selectedFunctionData,
|
||||
switchFunctions: switchFunctions,
|
||||
device: device,
|
||||
operationName: selectedOperationName ?? '',
|
||||
removeComparetors: removeComparetors,
|
||||
),
|
||||
context: context,
|
||||
selectedFunction: selectedFunction,
|
||||
selectedFunctionData:
|
||||
selectedFunctionData,
|
||||
switchFunctions: switchFunctions,
|
||||
device: device,
|
||||
operationName:
|
||||
selectedOperationName ?? '',
|
||||
removeComparetors: removeComparetors,
|
||||
dialogType: dialogType),
|
||||
),
|
||||
],
|
||||
),
|
||||
@ -133,14 +136,6 @@ class ThreeGangSwitchHelper {
|
||||
onConfirm: state.addedFunctions.isNotEmpty
|
||||
? () {
|
||||
/// add the functions to the routine bloc
|
||||
// for (var function in state.addedFunctions) {
|
||||
// context.read<RoutineBloc>().add(
|
||||
// AddFunctionToRoutine(
|
||||
// function,
|
||||
// uniqueCustomId,
|
||||
// ),
|
||||
// );
|
||||
// }
|
||||
context.read<RoutineBloc>().add(
|
||||
AddFunctionToRoutine(
|
||||
state.addedFunctions,
|
||||
@ -173,24 +168,26 @@ class ThreeGangSwitchHelper {
|
||||
AllDevicesModel? device,
|
||||
required String operationName,
|
||||
required bool removeComparetors,
|
||||
required String dialogType,
|
||||
}) {
|
||||
if (selectedFunction == 'countdown_1' ||
|
||||
selectedFunction == 'countdown_2' ||
|
||||
selectedFunction == 'countdown_3') {
|
||||
final initialValue = selectedFunctionData?.value ?? 0;
|
||||
return _buildTemperatureSelector(
|
||||
context: context,
|
||||
initialValue: initialValue,
|
||||
selectCode: selectedFunction,
|
||||
currentCondition: selectedFunctionData?.condition,
|
||||
device: device,
|
||||
operationName: operationName,
|
||||
selectedFunctionData: selectedFunctionData,
|
||||
removeComparetors: removeComparetors,
|
||||
);
|
||||
context: context,
|
||||
initialValue: initialValue,
|
||||
selectCode: selectedFunction,
|
||||
currentCondition: selectedFunctionData?.condition,
|
||||
device: device,
|
||||
operationName: operationName,
|
||||
selectedFunctionData: selectedFunctionData,
|
||||
removeComparetors: removeComparetors,
|
||||
dialogType: dialogType);
|
||||
}
|
||||
|
||||
final selectedFn = switchFunctions.firstWhere((f) => f.code == selectedFunction);
|
||||
final selectedFn =
|
||||
switchFunctions.firstWhere((f) => f.code == selectedFunction);
|
||||
final values = selectedFn.getOperationalValues();
|
||||
|
||||
return _buildOperationalValuesList(
|
||||
@ -213,93 +210,18 @@ class ThreeGangSwitchHelper {
|
||||
required String operationName,
|
||||
DeviceFunctionData? selectedFunctionData,
|
||||
bool? removeComparetors,
|
||||
required String dialogType,
|
||||
}) {
|
||||
return Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
if (removeComparetors != true)
|
||||
_buildConditionToggle(
|
||||
context,
|
||||
currentCondition,
|
||||
selectCode,
|
||||
device,
|
||||
operationName,
|
||||
selectedFunctionData,
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
_buildCountDownDisplay(context, initialValue, device, operationName,
|
||||
selectedFunctionData, selectCode),
|
||||
const SizedBox(height: 20),
|
||||
_buildCountDownSlider(context, initialValue, device, operationName,
|
||||
selectedFunctionData, selectCode),
|
||||
selectedFunctionData, selectCode, dialogType),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
/// Build condition toggle for AC functions dialog
|
||||
static Widget _buildConditionToggle(
|
||||
BuildContext context,
|
||||
String? currentCondition,
|
||||
String selectCode,
|
||||
AllDevicesModel? device,
|
||||
String operationName,
|
||||
DeviceFunctionData? selectedFunctionData,
|
||||
// Function(String) onConditionChanged,
|
||||
) {
|
||||
final conditions = ["<", "==", ">"];
|
||||
|
||||
return ToggleButtons(
|
||||
onPressed: (int index) {
|
||||
context.read<FunctionBloc>().add(
|
||||
AddFunction(
|
||||
functionData: DeviceFunctionData(
|
||||
entityId: device?.uuid ?? '',
|
||||
functionCode: selectCode,
|
||||
operationName: operationName,
|
||||
condition: conditions[index],
|
||||
value: selectedFunctionData?.value ?? 0,
|
||||
valueDescription: selectedFunctionData?.valueDescription,
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
borderRadius: const BorderRadius.all(Radius.circular(8)),
|
||||
selectedBorderColor: ColorsManager.primaryColorWithOpacity,
|
||||
selectedColor: Colors.white,
|
||||
fillColor: ColorsManager.primaryColorWithOpacity,
|
||||
color: ColorsManager.primaryColorWithOpacity,
|
||||
constraints: const BoxConstraints(
|
||||
minHeight: 40.0,
|
||||
minWidth: 40.0,
|
||||
),
|
||||
isSelected: conditions.map((c) => c == (currentCondition ?? "==")).toList(),
|
||||
children: conditions.map((c) => Text(c)).toList(),
|
||||
);
|
||||
}
|
||||
|
||||
/// Build temperature display for AC functions dialog
|
||||
static Widget _buildCountDownDisplay(
|
||||
BuildContext context,
|
||||
dynamic initialValue,
|
||||
AllDevicesModel? device,
|
||||
String operationName,
|
||||
DeviceFunctionData? selectedFunctionData,
|
||||
String selectCode) {
|
||||
return Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 10),
|
||||
decoration: BoxDecoration(
|
||||
color: ColorsManager.primaryColorWithOpacity.withOpacity(0.1),
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
),
|
||||
child: Text(
|
||||
DurationFormatMixin.formatDuration(initialValue?.toInt() ?? 0),
|
||||
style: context.textTheme.headlineMedium!.copyWith(
|
||||
color: ColorsManager.primaryColorWithOpacity,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
static Widget _buildCountDownSlider(
|
||||
BuildContext context,
|
||||
dynamic initialValue,
|
||||
@ -307,38 +229,47 @@ class ThreeGangSwitchHelper {
|
||||
String operationName,
|
||||
DeviceFunctionData? selectedFunctionData,
|
||||
String selectCode,
|
||||
String dialogType,
|
||||
) {
|
||||
const twelveHoursInSeconds = 43200.0;
|
||||
final operationalValues = SwitchOperationalValue(
|
||||
icon: '',
|
||||
description: "sec",
|
||||
value: 0.0,
|
||||
minValue: 0,
|
||||
maxValue: twelveHoursInSeconds,
|
||||
stepValue: 1,
|
||||
);
|
||||
return Slider(
|
||||
value: (initialValue ?? 0).toDouble(),
|
||||
min: operationalValues.minValue?.toDouble() ?? 0.0,
|
||||
max: operationalValues.maxValue?.toDouble() ?? 0.0,
|
||||
divisions:
|
||||
(((operationalValues.maxValue ?? 0) - (operationalValues.minValue ?? 0)) /
|
||||
(operationalValues.stepValue ?? 1))
|
||||
.round(),
|
||||
onChanged: (value) {
|
||||
return CustomRoutinesTextbox(
|
||||
withSpecialChar: true,
|
||||
currentCondition: selectedFunctionData?.condition,
|
||||
dialogType: dialogType,
|
||||
sliderRange: (0, 43200),
|
||||
displayedValue: (initialValue ?? 0).toString(),
|
||||
initialValue: (initialValue ?? 0).toString(),
|
||||
onConditionChanged: (condition) {
|
||||
context.read<FunctionBloc>().add(
|
||||
AddFunction(
|
||||
functionData: DeviceFunctionData(
|
||||
entityId: device?.uuid ?? '',
|
||||
functionCode: selectCode,
|
||||
operationName: operationName,
|
||||
value: value,
|
||||
condition: condition,
|
||||
value: selectedFunctionData?.value ?? 0,
|
||||
valueDescription: selectedFunctionData?.valueDescription,
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
onTextChanged: (value) {
|
||||
final roundedValue = value.round();
|
||||
context.read<FunctionBloc>().add(
|
||||
AddFunction(
|
||||
functionData: DeviceFunctionData(
|
||||
entityId: device?.uuid ?? '',
|
||||
functionCode: selectCode,
|
||||
operationName: operationName,
|
||||
value: roundedValue,
|
||||
condition: selectedFunctionData?.condition,
|
||||
valueDescription: selectedFunctionData?.valueDescription,
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
unit: 'sec',
|
||||
dividendOfRange: 1,
|
||||
stepIncreaseAmount: 1,
|
||||
);
|
||||
}
|
||||
|
||||
@ -374,7 +305,9 @@ class ThreeGangSwitchHelper {
|
||||
style: context.textTheme.bodyMedium,
|
||||
),
|
||||
trailing: Icon(
|
||||
isSelected ? Icons.radio_button_checked : Icons.radio_button_unchecked,
|
||||
isSelected
|
||||
? Icons.radio_button_checked
|
||||
: Icons.radio_button_unchecked,
|
||||
size: 24,
|
||||
color: isSelected
|
||||
? ColorsManager.primaryColorWithOpacity
|
||||
@ -390,7 +323,8 @@ class ThreeGangSwitchHelper {
|
||||
operationName: operationName,
|
||||
value: value.value,
|
||||
condition: selectedFunctionData?.condition,
|
||||
valueDescription: selectedFunctionData?.valueDescription,
|
||||
valueDescription:
|
||||
selectedFunctionData?.valueDescription,
|
||||
),
|
||||
),
|
||||
);
|
||||
|
@ -8,6 +8,7 @@ import 'package:syncrow_web/pages/routines/helper/duration_format_helper.dart';
|
||||
import 'package:syncrow_web/pages/routines/models/device_functions.dart';
|
||||
import 'package:syncrow_web/pages/routines/models/gang_switches/base_switch_function.dart';
|
||||
import 'package:syncrow_web/pages/routines/models/gang_switches/switch_operational_value.dart';
|
||||
import 'package:syncrow_web/pages/routines/widgets/custom_routines_textbox.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/routine_dialogs/helpers/routine_tap_function_helper.dart';
|
||||
@ -86,14 +87,15 @@ class TwoGangSwitchHelper {
|
||||
size: 16,
|
||||
color: ColorsManager.textGray,
|
||||
),
|
||||
onTap: () =>
|
||||
RoutineTapFunctionHelper.onTapFunction(
|
||||
onTap: () => RoutineTapFunctionHelper
|
||||
.onTapFunction(
|
||||
context,
|
||||
functionCode: function.code,
|
||||
functionOperationName:
|
||||
function.operationName,
|
||||
functionValueDescription:
|
||||
selectedFunctionData.valueDescription,
|
||||
selectedFunctionData
|
||||
.valueDescription,
|
||||
deviceUuid: device?.uuid,
|
||||
codesToAddIntoFunctionsWithDefaultValue: [
|
||||
'countdown_1',
|
||||
@ -115,6 +117,7 @@ class TwoGangSwitchHelper {
|
||||
device: device,
|
||||
operationName: selectedOperationName ?? '',
|
||||
removeComparetors: removeComparetors,
|
||||
dialogType: dialogType,
|
||||
),
|
||||
),
|
||||
],
|
||||
@ -172,22 +175,25 @@ class TwoGangSwitchHelper {
|
||||
AllDevicesModel? device,
|
||||
required String operationName,
|
||||
required bool removeComparetors,
|
||||
required String dialogType,
|
||||
}) {
|
||||
if (selectedFunction == 'countdown_1' || selectedFunction == 'countdown_2') {
|
||||
if (selectedFunction == 'countdown_1' ||
|
||||
selectedFunction == 'countdown_2') {
|
||||
final initialValue = selectedFunctionData?.value ?? 0;
|
||||
return _buildTemperatureSelector(
|
||||
context: context,
|
||||
initialValue: initialValue,
|
||||
selectCode: selectedFunction,
|
||||
currentCondition: selectedFunctionData?.condition,
|
||||
device: device,
|
||||
operationName: operationName,
|
||||
selectedFunctionData: selectedFunctionData,
|
||||
removeComparetors: removeComparetors,
|
||||
);
|
||||
context: context,
|
||||
initialValue: initialValue,
|
||||
selectCode: selectedFunction,
|
||||
currentCondition: selectedFunctionData?.condition,
|
||||
device: device,
|
||||
operationName: operationName,
|
||||
selectedFunctionData: selectedFunctionData,
|
||||
removeComparetors: removeComparetors,
|
||||
dialogType: dialogType);
|
||||
}
|
||||
|
||||
final selectedFn = switchFunctions.firstWhere((f) => f.code == selectedFunction);
|
||||
final selectedFn =
|
||||
switchFunctions.firstWhere((f) => f.code == selectedFunction);
|
||||
final values = selectedFn.getOperationalValues();
|
||||
|
||||
return _buildOperationalValuesList(
|
||||
@ -210,25 +216,13 @@ class TwoGangSwitchHelper {
|
||||
required String operationName,
|
||||
DeviceFunctionData? selectedFunctionData,
|
||||
bool? removeComparetors,
|
||||
String? dialogType,
|
||||
}) {
|
||||
return Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
if (removeComparetors != true)
|
||||
_buildConditionToggle(
|
||||
context,
|
||||
currentCondition,
|
||||
selectCode,
|
||||
device,
|
||||
operationName,
|
||||
selectedFunctionData,
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
_buildCountDownDisplay(context, initialValue, device, operationName,
|
||||
selectedFunctionData, selectCode),
|
||||
const SizedBox(height: 20),
|
||||
_buildCountDownSlider(context, initialValue, device, operationName,
|
||||
selectedFunctionData, selectCode),
|
||||
selectedFunctionData, selectCode, dialogType!),
|
||||
],
|
||||
);
|
||||
}
|
||||
@ -269,7 +263,8 @@ class TwoGangSwitchHelper {
|
||||
minHeight: 40.0,
|
||||
minWidth: 40.0,
|
||||
),
|
||||
isSelected: conditions.map((c) => c == (currentCondition ?? "==")).toList(),
|
||||
isSelected:
|
||||
conditions.map((c) => c == (currentCondition ?? "==")).toList(),
|
||||
children: conditions.map((c) => Text(c)).toList(),
|
||||
);
|
||||
}
|
||||
@ -304,38 +299,48 @@ class TwoGangSwitchHelper {
|
||||
String operationName,
|
||||
DeviceFunctionData? selectedFunctionData,
|
||||
String selectCode,
|
||||
String dialogType,
|
||||
) {
|
||||
const twelveHoursInSeconds = 43200.0;
|
||||
final operationalValues = SwitchOperationalValue(
|
||||
icon: '',
|
||||
description: "sec",
|
||||
value: 0.0,
|
||||
minValue: 0,
|
||||
maxValue: twelveHoursInSeconds,
|
||||
stepValue: 1,
|
||||
);
|
||||
return Slider(
|
||||
value: (initialValue ?? 0).toDouble(),
|
||||
min: operationalValues.minValue?.toDouble() ?? 0.0,
|
||||
max: operationalValues.maxValue?.toDouble() ?? 0.0,
|
||||
divisions:
|
||||
(((operationalValues.maxValue ?? 0) - (operationalValues.minValue ?? 0)) /
|
||||
(operationalValues.stepValue ?? 1))
|
||||
.round(),
|
||||
onChanged: (value) {
|
||||
return CustomRoutinesTextbox(
|
||||
withSpecialChar: true,
|
||||
currentCondition: selectedFunctionData?.condition,
|
||||
dialogType: dialogType,
|
||||
sliderRange: (0, 43200),
|
||||
displayedValue: (initialValue ?? 0).toString(),
|
||||
initialValue: (initialValue ?? 0).toString(),
|
||||
onConditionChanged: (condition) {
|
||||
context.read<FunctionBloc>().add(
|
||||
AddFunction(
|
||||
functionData: DeviceFunctionData(
|
||||
entityId: device?.uuid ?? '',
|
||||
functionCode: selectCode,
|
||||
operationName: operationName,
|
||||
value: value,
|
||||
condition: condition,
|
||||
value: selectedFunctionData?.value ?? 0,
|
||||
valueDescription: selectedFunctionData?.valueDescription,
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
onTextChanged: (value) {
|
||||
final roundedValue =
|
||||
value.round(); // Round to nearest integer (stepSize 1)
|
||||
context.read<FunctionBloc>().add(
|
||||
AddFunction(
|
||||
functionData: DeviceFunctionData(
|
||||
entityId: device?.uuid ?? '',
|
||||
functionCode: selectCode,
|
||||
operationName: operationName,
|
||||
value: roundedValue,
|
||||
condition: selectedFunctionData?.condition,
|
||||
valueDescription: selectedFunctionData?.valueDescription,
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
unit: 'sec',
|
||||
dividendOfRange: 1,
|
||||
stepIncreaseAmount: 1,
|
||||
);
|
||||
}
|
||||
|
||||
@ -371,7 +376,9 @@ class TwoGangSwitchHelper {
|
||||
style: context.textTheme.bodyMedium,
|
||||
),
|
||||
trailing: Icon(
|
||||
isSelected ? Icons.radio_button_checked : Icons.radio_button_unchecked,
|
||||
isSelected
|
||||
? Icons.radio_button_checked
|
||||
: Icons.radio_button_unchecked,
|
||||
size: 24,
|
||||
color: isSelected
|
||||
? ColorsManager.primaryColorWithOpacity
|
||||
@ -387,7 +394,8 @@ class TwoGangSwitchHelper {
|
||||
operationName: operationName,
|
||||
value: value.value,
|
||||
condition: selectedFunctionData?.condition,
|
||||
valueDescription: selectedFunctionData?.valueDescription,
|
||||
valueDescription:
|
||||
selectedFunctionData?.valueDescription,
|
||||
),
|
||||
),
|
||||
);
|
||||
|
@ -4,8 +4,8 @@ import 'package:syncrow_web/pages/device_managment/all_devices/models/devices_mo
|
||||
import 'package:syncrow_web/pages/routines/bloc/functions_bloc/functions_bloc_bloc.dart';
|
||||
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/widgets/custom_routines_textbox.dart';
|
||||
import 'package:syncrow_web/pages/routines/widgets/routine_dialogs/wall_sensor/wps_operational_values_list.dart';
|
||||
import 'package:syncrow_web/pages/routines/widgets/slider_value_selector.dart';
|
||||
|
||||
class WpsValueSelectorWidget extends StatelessWidget {
|
||||
final String selectedFunction;
|
||||
@ -27,11 +27,13 @@ class WpsValueSelectorWidget extends StatelessWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final selectedFn = wpsFunctions.firstWhere((f) => f.code == selectedFunction);
|
||||
final selectedFn =
|
||||
wpsFunctions.firstWhere((f) => f.code == selectedFunction);
|
||||
final values = selectedFn.getOperationalValues();
|
||||
|
||||
if (_isSliderFunction(selectedFunction)) {
|
||||
return SliderValueSelector(
|
||||
return CustomRoutinesTextbox(
|
||||
withSpecialChar: false,
|
||||
currentCondition: functionData.condition,
|
||||
dialogType: dialogType,
|
||||
sliderRange: sliderRange,
|
||||
@ -48,7 +50,7 @@ class WpsValueSelectorWidget extends StatelessWidget {
|
||||
),
|
||||
),
|
||||
),
|
||||
onSliderChanged: (value) => context.read<FunctionBloc>().add(
|
||||
onTextChanged: (value) => context.read<FunctionBloc>().add(
|
||||
AddFunction(
|
||||
functionData: DeviceFunctionData(
|
||||
entityId: device?.uuid ?? '',
|
||||
@ -61,6 +63,7 @@ class WpsValueSelectorWidget extends StatelessWidget {
|
||||
),
|
||||
unit: _unit,
|
||||
dividendOfRange: 1,
|
||||
stepIncreaseAmount: _steps,
|
||||
);
|
||||
}
|
||||
|
||||
@ -99,4 +102,10 @@ class WpsValueSelectorWidget extends StatelessWidget {
|
||||
'illuminance_value' => 'Lux',
|
||||
_ => '',
|
||||
};
|
||||
double get _steps => switch (functionData.functionCode) {
|
||||
'presence_time' => 1,
|
||||
'dis_current' => 1,
|
||||
'illuminance_value' => 1,
|
||||
_ => 1,
|
||||
};
|
||||
}
|
||||
|
@ -0,0 +1,65 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_svg/svg.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/all_devices/models/devices_model.dart';
|
||||
import 'package:syncrow_web/pages/routines/models/water_heater/water_heater_operational_value.dart';
|
||||
|
||||
class WaterHeaterOperationalValuesList extends StatelessWidget {
|
||||
final List<WaterHeaterOperationalValue> values;
|
||||
final dynamic selectedValue;
|
||||
final AllDevicesModel? device;
|
||||
final String operationName;
|
||||
final String selectCode;
|
||||
final ValueChanged<WaterHeaterOperationalValue> onSelect;
|
||||
const WaterHeaterOperationalValuesList({
|
||||
required this.values,
|
||||
required this.selectedValue,
|
||||
required this.device,
|
||||
required this.operationName,
|
||||
required this.selectCode,
|
||||
required this.onSelect,
|
||||
super.key,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return ListView.builder(
|
||||
padding: const EdgeInsets.all(20),
|
||||
itemCount: values.length,
|
||||
itemBuilder: (context, index) => _buildValueItem(context, values[index]),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildValueItem(
|
||||
BuildContext context, WaterHeaterOperationalValue value) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
SvgPicture.asset(
|
||||
value.icon,
|
||||
width: 24,
|
||||
height: 24,
|
||||
),
|
||||
Expanded(child: _buildValueDescription(value)),
|
||||
_buildValueRadio(context, value),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildValueDescription(WaterHeaterOperationalValue value) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 8.0),
|
||||
child: Text(value.description),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildValueRadio(context, WaterHeaterOperationalValue value) {
|
||||
return Radio<dynamic>(
|
||||
value: value.value,
|
||||
groupValue: selectedValue,
|
||||
onChanged: (_) => onSelect(value));
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,204 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:flutter_svg/flutter_svg.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/bloc/routine_bloc/routine_bloc.dart';
|
||||
import 'package:syncrow_web/pages/routines/models/device_functions.dart';
|
||||
import 'package:syncrow_web/pages/routines/models/water_heater/water_heater_functions.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/routine_dialogs/water_heater/water_heater_value_selector_widget.dart';
|
||||
import 'package:syncrow_web/utils/color_manager.dart';
|
||||
import 'package:syncrow_web/utils/extension/build_context_x.dart';
|
||||
|
||||
class WaterHeaterDialogRoutines extends StatefulWidget {
|
||||
final List<DeviceFunction> functions;
|
||||
final AllDevicesModel? device;
|
||||
final List<DeviceFunctionData>? deviceSelectedFunctions;
|
||||
final String? uniqueCustomId;
|
||||
final String dialogType;
|
||||
|
||||
const WaterHeaterDialogRoutines({
|
||||
super.key,
|
||||
required this.functions,
|
||||
this.device,
|
||||
this.deviceSelectedFunctions,
|
||||
this.uniqueCustomId,
|
||||
required this.dialogType,
|
||||
});
|
||||
|
||||
static Future<Map<String, dynamic>?> showWHFunctionsDialog({
|
||||
required BuildContext context,
|
||||
required List<DeviceFunction> functions,
|
||||
AllDevicesModel? device,
|
||||
List<DeviceFunctionData>? deviceSelectedFunctions,
|
||||
String? uniqueCustomId,
|
||||
required String dialogType,
|
||||
}) async {
|
||||
return showDialog<Map<String, dynamic>?>(
|
||||
context: context,
|
||||
builder: (context) => WaterHeaterDialogRoutines(
|
||||
functions: functions,
|
||||
device: device,
|
||||
deviceSelectedFunctions: deviceSelectedFunctions,
|
||||
uniqueCustomId: uniqueCustomId,
|
||||
dialogType: dialogType,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
State<WaterHeaterDialogRoutines> createState() =>
|
||||
_WaterHeaterDialogRoutinesState();
|
||||
}
|
||||
|
||||
class _WaterHeaterDialogRoutinesState extends State<WaterHeaterDialogRoutines> {
|
||||
late final List<WaterHeaterFunctions> _waterHeaterFunctions;
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_waterHeaterFunctions =
|
||||
widget.functions.whereType<WaterHeaterFunctions>().where((function) {
|
||||
if (widget.dialogType == 'THEN') {
|
||||
return function.type == 'THEN' || function.type == 'BOTH';
|
||||
}
|
||||
return function.type == 'IF' || function.type == 'BOTH';
|
||||
}).toList();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return BlocProvider(
|
||||
create: (_) => FunctionBloc()
|
||||
..add(InitializeFunctions(widget.deviceSelectedFunctions ?? [])),
|
||||
child: _buildDialogContent(),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildDialogContent() {
|
||||
return AlertDialog(
|
||||
contentPadding: EdgeInsets.zero,
|
||||
content: BlocBuilder<FunctionBloc, FunctionBlocState>(
|
||||
builder: (context, state) {
|
||||
final selectedFunction = state.selectedFunction;
|
||||
return Container(
|
||||
width: selectedFunction != null ? 600 : 360,
|
||||
height: 450,
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
),
|
||||
padding: const EdgeInsets.only(top: 20),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
const DialogHeader('Water Heater Condition'),
|
||||
Expanded(child: _buildMainContent(context, state)),
|
||||
_buildDialogFooter(context, state),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildMainContent(BuildContext context, FunctionBlocState state) {
|
||||
return Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: [
|
||||
_buildFunctionList(context),
|
||||
if (state.selectedFunction != null) _buildValueSelector(context, state),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildFunctionList(BuildContext context) {
|
||||
return SizedBox(
|
||||
width: 360,
|
||||
child: ListView.separated(
|
||||
shrinkWrap: false,
|
||||
physics: const AlwaysScrollableScrollPhysics(),
|
||||
itemCount: _waterHeaterFunctions.length,
|
||||
separatorBuilder: (context, index) => const Padding(
|
||||
padding: EdgeInsets.symmetric(horizontal: 40.0),
|
||||
child: Divider(color: ColorsManager.dividerColor),
|
||||
),
|
||||
itemBuilder: (context, index) {
|
||||
final function = _waterHeaterFunctions[index];
|
||||
return ListTile(
|
||||
leading: SvgPicture.asset(
|
||||
function.icon,
|
||||
width: 24,
|
||||
height: 24,
|
||||
placeholderBuilder: (context) => const SizedBox(
|
||||
width: 24,
|
||||
height: 24,
|
||||
),
|
||||
),
|
||||
title: Text(
|
||||
function.operationName,
|
||||
style: context.textTheme.bodyMedium,
|
||||
),
|
||||
trailing: const Icon(
|
||||
Icons.arrow_forward_ios,
|
||||
size: 16,
|
||||
color: ColorsManager.textGray,
|
||||
),
|
||||
onTap: () => context.read<FunctionBloc>().add(
|
||||
SelectFunction(
|
||||
functionCode: function.code,
|
||||
operationName: function.operationName,
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildValueSelector(BuildContext context, FunctionBlocState state) {
|
||||
final selectedFunction = state.selectedFunction ?? '';
|
||||
final functionData = state.addedFunctions.firstWhere(
|
||||
(f) => f.functionCode == selectedFunction,
|
||||
orElse: () => DeviceFunctionData(
|
||||
entityId: '',
|
||||
functionCode: selectedFunction,
|
||||
operationName: state.selectedOperationName ?? '',
|
||||
value: null,
|
||||
),
|
||||
);
|
||||
|
||||
return Expanded(
|
||||
child: WaterHeaterValueSelectorWidget(
|
||||
selectedFunction: selectedFunction,
|
||||
functionData: functionData,
|
||||
whFunctions: _waterHeaterFunctions,
|
||||
device: widget.device,
|
||||
dialogType: widget.dialogType,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildDialogFooter(BuildContext context, FunctionBlocState state) {
|
||||
return DialogFooter(
|
||||
onCancel: () => Navigator.pop(context),
|
||||
onConfirm: state.addedFunctions.isNotEmpty
|
||||
? () {
|
||||
context.read<RoutineBloc>().add(
|
||||
AddFunctionToRoutine(
|
||||
state.addedFunctions,
|
||||
widget.uniqueCustomId!,
|
||||
),
|
||||
);
|
||||
Navigator.pop(
|
||||
context,
|
||||
{'deviceId': widget.functions.first.deviceId},
|
||||
);
|
||||
}
|
||||
: null,
|
||||
isConfirmEnabled: state.selectedFunction != null,
|
||||
);
|
||||
}
|
||||
}
|
@ -0,0 +1,127 @@
|
||||
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/device_functions.dart';
|
||||
import 'package:syncrow_web/pages/routines/models/water_heater/water_heater_functions.dart';
|
||||
import 'package:syncrow_web/pages/routines/widgets/custom_routines_textbox.dart';
|
||||
import 'package:syncrow_web/pages/routines/widgets/routine_dialogs/water_heater/water_heater_operational_values_list.dart';
|
||||
|
||||
class WaterHeaterValueSelectorWidget extends StatelessWidget {
|
||||
final String selectedFunction;
|
||||
final DeviceFunctionData functionData;
|
||||
final List<WaterHeaterFunctions> whFunctions;
|
||||
final AllDevicesModel? device;
|
||||
final String dialogType;
|
||||
|
||||
const WaterHeaterValueSelectorWidget({
|
||||
required this.selectedFunction,
|
||||
required this.functionData,
|
||||
required this.whFunctions,
|
||||
required this.device,
|
||||
required this.dialogType,
|
||||
super.key,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final selectedFn = whFunctions.firstWhere(
|
||||
(f) => f.code == selectedFunction,
|
||||
orElse: () => WHSwitchFunction(
|
||||
deviceId: '',
|
||||
deviceName: '',
|
||||
type: '',
|
||||
),
|
||||
);
|
||||
if (selectedFunction == 'countdown_1') {
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
_buildCountDownSlider(
|
||||
context,
|
||||
functionData.value,
|
||||
device,
|
||||
selectedFn.operationName,
|
||||
functionData,
|
||||
selectedFunction,
|
||||
dialogType
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
return WaterHeaterOperationalValuesList(
|
||||
values: selectedFn.getOperationalValues(),
|
||||
selectedValue: functionData.value,
|
||||
device: device,
|
||||
operationName: selectedFn.operationName,
|
||||
selectCode: selectedFunction,
|
||||
onSelect: (selectedValue) async {
|
||||
context.read<FunctionBloc>().add(
|
||||
AddFunction(
|
||||
functionData: DeviceFunctionData(
|
||||
entityId: device?.uuid ?? '',
|
||||
functionCode: selectedFunction,
|
||||
operationName: functionData.operationName,
|
||||
value: selectedValue.value,
|
||||
condition: functionData.condition,
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
static Widget _buildCountDownSlider(
|
||||
BuildContext context,
|
||||
dynamic initialValue,
|
||||
AllDevicesModel? device,
|
||||
String operationName,
|
||||
DeviceFunctionData? selectedFunctionData,
|
||||
String selectCode,
|
||||
String dialogType,
|
||||
) {
|
||||
return CustomRoutinesTextbox(
|
||||
withSpecialChar: false,
|
||||
currentCondition: selectedFunctionData?.condition,
|
||||
dialogType: dialogType,
|
||||
sliderRange: (0, 43200),
|
||||
displayedValue: (initialValue ?? 0).toString(),
|
||||
initialValue: (initialValue ?? 0).toString(),
|
||||
onConditionChanged: (condition) {
|
||||
context.read<FunctionBloc>().add(
|
||||
AddFunction(
|
||||
functionData: DeviceFunctionData(
|
||||
entityId: device?.uuid ?? '',
|
||||
functionCode: selectCode,
|
||||
operationName: operationName,
|
||||
value: condition,
|
||||
condition: condition,
|
||||
valueDescription: selectedFunctionData?.valueDescription,
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
onTextChanged: (value) {
|
||||
final roundedValue = value.round();
|
||||
context.read<FunctionBloc>().add(
|
||||
AddFunction(
|
||||
functionData: DeviceFunctionData(
|
||||
entityId: device?.uuid ?? '',
|
||||
functionCode: selectCode,
|
||||
operationName: operationName,
|
||||
value: roundedValue,
|
||||
condition: selectedFunctionData?.condition,
|
||||
valueDescription: selectedFunctionData?.valueDescription,
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
unit: 'sec',
|
||||
dividendOfRange: 1,
|
||||
stepIncreaseAmount: 1,
|
||||
);
|
||||
}
|
||||
}
|
@ -116,7 +116,8 @@ class ThenContainer extends StatelessWidget {
|
||||
'WPS',
|
||||
'CPS',
|
||||
"GW",
|
||||
"NCPS"
|
||||
"NCPS",
|
||||
'WH',
|
||||
].contains(state.thenItems[index]
|
||||
['productType'])) {
|
||||
context.read<RoutineBloc>().add(
|
||||
@ -232,8 +233,17 @@ class ThenContainer extends StatelessWidget {
|
||||
dialogType: "THEN");
|
||||
if (result != null) {
|
||||
context.read<RoutineBloc>().add(AddToThenContainer(mutableData));
|
||||
} else if (!['AC', '1G', '2G', '3G', 'WPS', 'GW', 'CPS', "NCPS"]
|
||||
.contains(mutableData['productType'])) {
|
||||
} else if (![
|
||||
'AC',
|
||||
'1G',
|
||||
'2G',
|
||||
'3G',
|
||||
'WPS',
|
||||
'GW',
|
||||
'CPS',
|
||||
"NCPS",
|
||||
"WH"
|
||||
].contains(mutableData['productType'])) {
|
||||
context.read<RoutineBloc>().add(AddToThenContainer(mutableData));
|
||||
}
|
||||
},
|
||||
|
@ -481,4 +481,5 @@ class Assets {
|
||||
static const String indentLevelIcon = 'assets/icons/indent_level_icon.svg';
|
||||
static const String triggerLevelIcon = 'assets/icons/trigger_level_icon.svg';
|
||||
static const String blankCalendar = 'assets/icons/blank_calendar.svg';
|
||||
static const String refreshStatusIcon = 'assets/icons/refresh_status_icon.svg';
|
||||
}
|
||||
|
Reference in New Issue
Block a user