mirror of
https://github.com/SyncrowIOT/web.git
synced 2025-07-17 10:35:14 +00:00
Compare commits
28 Commits
SP-1513-FE
...
chore/add-
Author | SHA1 | Date | |
---|---|---|---|
4f98891902 | |||
7002bbfa04 | |||
f19120c754 | |||
6b3eca23af | |||
4f4f11c330 | |||
8a25fa798c | |||
6612e91430 | |||
56c613fb0c | |||
8d2d9dd0bb | |||
cfc68f1568 | |||
02e08ad92f | |||
d7899a24f5 | |||
800c0ba47f | |||
fe4e775902 | |||
5247856cb4 | |||
4a8b8a32ba | |||
2abce77eb5 | |||
7efd1c3c87 | |||
7a0d9aefb7 | |||
21cc25cfc4 | |||
e2ec4bbf31 | |||
51b46ae197 | |||
36ee22603a | |||
b0abd42b0c | |||
ba4da78846 | |||
dc20d69f20 | |||
cf6ec231dc | |||
d0530f7fc3 |
10
.github/.github/dependabot.yaml
vendored
Normal file
10
.github/.github/dependabot.yaml
vendored
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
version: 2
|
||||||
|
updates:
|
||||||
|
- package-ecosystem: "github-actions"
|
||||||
|
directory: "/"
|
||||||
|
schedule:
|
||||||
|
interval: "daily"
|
||||||
|
- package-ecosystem: "pub"
|
||||||
|
directory: "/"
|
||||||
|
schedule:
|
||||||
|
interval: "daily"
|
26
.github/pull_request_template.md
vendored
Normal file
26
.github/pull_request_template.md
vendored
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
<!--
|
||||||
|
Thanks for contributing!
|
||||||
|
|
||||||
|
Provide a description of your changes below and a general summary in the title
|
||||||
|
|
||||||
|
Please look at the following checklist to ensure that your PR can be accepted quickly:
|
||||||
|
-->
|
||||||
|
|
||||||
|
## Jira Ticket
|
||||||
|
[SP-0000](https://syncrow.atlassian.net/browse/SP-0000)
|
||||||
|
|
||||||
|
## Description
|
||||||
|
|
||||||
|
<!--- Describe your changes in detail -->
|
||||||
|
|
||||||
|
## Type of Change
|
||||||
|
|
||||||
|
<!--- Put an `x` in all the boxes that apply: -->
|
||||||
|
|
||||||
|
- [ ] ✨ New feature (non-breaking change which adds functionality)
|
||||||
|
- [ ] 🛠️ Bug fix (non-breaking change which fixes an issue)
|
||||||
|
- [ ] ❌ Breaking change (fix or feature that would cause existing functionality to change)
|
||||||
|
- [ ] 🧹 Code refactor
|
||||||
|
- [ ] ✅ Build configuration change
|
||||||
|
- [ ] 📝 Documentation
|
||||||
|
- [ ] 🗑️ Chore
|
@ -4,10 +4,6 @@ on:
|
|||||||
push:
|
push:
|
||||||
branches:
|
branches:
|
||||||
- main
|
- main
|
||||||
pull_request:
|
|
||||||
types: [opened, synchronize, reopened, closed]
|
|
||||||
branches:
|
|
||||||
- main
|
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build_and_deploy_job:
|
build_and_deploy_job:
|
||||||
|
@ -4,18 +4,12 @@ on:
|
|||||||
push:
|
push:
|
||||||
branches:
|
branches:
|
||||||
- dev
|
- dev
|
||||||
pull_request:
|
|
||||||
types: [opened, synchronize, reopened, closed]
|
|
||||||
branches:
|
|
||||||
- dev
|
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build_and_deploy_job:
|
build_and_deploy_job:
|
||||||
if: github.event_name == 'push' || (github.event_name == 'pull_request' && github.event.action != 'closed')
|
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
name: Build and Deploy Job
|
name: Build and Deploy Job
|
||||||
steps:
|
steps:
|
||||||
|
|
||||||
- name: Checkout Code
|
- name: Checkout Code
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v3
|
||||||
with:
|
with:
|
||||||
|
29
.github/workflows/pr-check.yml
vendored
Normal file
29
.github/workflows/pr-check.yml
vendored
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
name: Pull Request Check
|
||||||
|
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
branches:
|
||||||
|
- dev
|
||||||
|
- main
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
setup_flutter:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
name: Setup Flutter and Dependencies
|
||||||
|
steps:
|
||||||
|
- name: Checkout Code
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
with:
|
||||||
|
submodules: true
|
||||||
|
lfs: false
|
||||||
|
|
||||||
|
- name: Set up Flutter
|
||||||
|
uses: subosito/flutter-action@v2
|
||||||
|
with:
|
||||||
|
flutter-version: '3.27.3'
|
||||||
|
|
||||||
|
- name: Install dependencies
|
||||||
|
run: flutter pub get
|
||||||
|
|
||||||
|
- name: Run Flutter Build
|
||||||
|
run: flutter build web --web-renderer canvaskit -t lib/main_dev.dart
|
@ -1,56 +0,0 @@
|
|||||||
import 'dart:ui';
|
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
|
|
||||||
class DashedBorderPainter extends CustomPainter {
|
|
||||||
final double dashWidth;
|
|
||||||
final double dashSpace;
|
|
||||||
final Color color;
|
|
||||||
|
|
||||||
DashedBorderPainter({
|
|
||||||
this.dashWidth = 4.0,
|
|
||||||
this.dashSpace = 2.0,
|
|
||||||
this.color = Colors.black,
|
|
||||||
});
|
|
||||||
|
|
||||||
@override
|
|
||||||
void paint(Canvas canvas, Size size) {
|
|
||||||
final paint = Paint()
|
|
||||||
..color = color
|
|
||||||
..strokeWidth = 0.5
|
|
||||||
..style = PaintingStyle.stroke;
|
|
||||||
|
|
||||||
final Path topPath = Path()
|
|
||||||
..moveTo(0, 0)
|
|
||||||
..lineTo(size.width, 0);
|
|
||||||
|
|
||||||
final Path bottomPath = Path()
|
|
||||||
..moveTo(0, size.height)
|
|
||||||
..lineTo(size.width, size.height);
|
|
||||||
|
|
||||||
final dashedTopPath = _createDashedPath(topPath, dashWidth, dashSpace);
|
|
||||||
final dashedBottomPath = _createDashedPath(bottomPath, dashWidth, dashSpace);
|
|
||||||
|
|
||||||
canvas.drawPath(dashedTopPath, paint);
|
|
||||||
canvas.drawPath(dashedBottomPath, paint);
|
|
||||||
}
|
|
||||||
|
|
||||||
Path _createDashedPath(Path source, double dashWidth, double dashSpace) {
|
|
||||||
final Path dashedPath = Path();
|
|
||||||
for (PathMetric pathMetric in source.computeMetrics()) {
|
|
||||||
double distance = 0.0;
|
|
||||||
while (distance < pathMetric.length) {
|
|
||||||
final double nextDistance = distance + dashWidth;
|
|
||||||
dashedPath.addPath(
|
|
||||||
pathMetric.extractPath(distance, nextDistance),
|
|
||||||
Offset.zero,
|
|
||||||
);
|
|
||||||
distance = nextDistance + dashSpace;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return dashedPath;
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
bool shouldRepaint(covariant CustomPainter oldDelegate) => false;
|
|
||||||
}
|
|
@ -1,18 +0,0 @@
|
|||||||
import 'package:equatable/equatable.dart';
|
|
||||||
|
|
||||||
class Occupacy extends Equatable {
|
|
||||||
final String date;
|
|
||||||
final String occupancy;
|
|
||||||
|
|
||||||
const Occupacy({required this.date, required this.occupancy});
|
|
||||||
|
|
||||||
factory Occupacy.fromJson(Map<String, dynamic> json) {
|
|
||||||
return Occupacy(
|
|
||||||
date: json['date'] as String,
|
|
||||||
occupancy: json['occupancy'] as String,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
List<Object?> get props => [date, occupancy];
|
|
||||||
}
|
|
@ -1,22 +0,0 @@
|
|||||||
import 'package:equatable/equatable.dart';
|
|
||||||
|
|
||||||
class OccupancyHeatMapModel extends Equatable {
|
|
||||||
final DateTime date;
|
|
||||||
|
|
||||||
final int occupancy;
|
|
||||||
|
|
||||||
const OccupancyHeatMapModel({
|
|
||||||
required this.date,
|
|
||||||
required this.occupancy,
|
|
||||||
});
|
|
||||||
|
|
||||||
factory OccupancyHeatMapModel.fromJson(Map<String, dynamic> json) {
|
|
||||||
return OccupancyHeatMapModel(
|
|
||||||
date: DateTime.parse(json['date'] as String),
|
|
||||||
occupancy: json['occupancy'] as int,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
List<Object?> get props => [date, occupancy];
|
|
||||||
}
|
|
@ -2,23 +2,16 @@ import 'package:bloc/bloc.dart';
|
|||||||
import 'package:equatable/equatable.dart';
|
import 'package:equatable/equatable.dart';
|
||||||
|
|
||||||
part 'analytics_date_picker_event.dart';
|
part 'analytics_date_picker_event.dart';
|
||||||
part 'analytics_date_picker_state.dart';
|
|
||||||
|
|
||||||
class AnalyticsDatePickerBloc
|
class AnalyticsDatePickerBloc extends Bloc<AnalyticsDatePickerEvent, DateTime> {
|
||||||
extends Bloc<AnalyticsDatePickerEvent, AnalyticsDatePickerState> {
|
AnalyticsDatePickerBloc() : super(DateTime.now()) {
|
||||||
AnalyticsDatePickerBloc() : super(AnalyticsDatePickerState()) {
|
|
||||||
on<UpdateAnalyticsDatePickerEvent>(_onUpdateAnalyticsDatePickerEvent);
|
on<UpdateAnalyticsDatePickerEvent>(_onUpdateAnalyticsDatePickerEvent);
|
||||||
}
|
}
|
||||||
|
|
||||||
void _onUpdateAnalyticsDatePickerEvent(
|
void _onUpdateAnalyticsDatePickerEvent(
|
||||||
UpdateAnalyticsDatePickerEvent event,
|
UpdateAnalyticsDatePickerEvent event,
|
||||||
Emitter<AnalyticsDatePickerState> emit,
|
Emitter<DateTime> emit,
|
||||||
) {
|
) {
|
||||||
emit(
|
emit(event.date);
|
||||||
state.copyWith(
|
|
||||||
monthlyDate: event.montlyDate ?? state.monthlyDate,
|
|
||||||
yearlyDate: event.yearlyDate ?? state.yearlyDate,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,11 +8,10 @@ sealed class AnalyticsDatePickerEvent extends Equatable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
final class UpdateAnalyticsDatePickerEvent extends AnalyticsDatePickerEvent {
|
final class UpdateAnalyticsDatePickerEvent extends AnalyticsDatePickerEvent {
|
||||||
const UpdateAnalyticsDatePickerEvent({this.montlyDate, this.yearlyDate});
|
const UpdateAnalyticsDatePickerEvent(this.date);
|
||||||
|
|
||||||
final DateTime? montlyDate;
|
final DateTime date;
|
||||||
final DateTime? yearlyDate;
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
List<Object?> get props => [montlyDate, yearlyDate];
|
List<Object?> get props => [date];
|
||||||
}
|
}
|
||||||
|
@ -1,25 +0,0 @@
|
|||||||
part of 'analytics_date_picker_bloc.dart';
|
|
||||||
|
|
||||||
final class AnalyticsDatePickerState extends Equatable {
|
|
||||||
AnalyticsDatePickerState({
|
|
||||||
DateTime? monthlyDate,
|
|
||||||
DateTime? yearlyDate,
|
|
||||||
}) : monthlyDate = monthlyDate ?? DateTime.now(),
|
|
||||||
yearlyDate = yearlyDate ?? DateTime.now();
|
|
||||||
|
|
||||||
final DateTime monthlyDate;
|
|
||||||
final DateTime yearlyDate;
|
|
||||||
|
|
||||||
AnalyticsDatePickerState copyWith({
|
|
||||||
DateTime? monthlyDate,
|
|
||||||
DateTime? yearlyDate,
|
|
||||||
}) {
|
|
||||||
return AnalyticsDatePickerState(
|
|
||||||
monthlyDate: monthlyDate ?? this.monthlyDate,
|
|
||||||
yearlyDate: yearlyDate ?? this.yearlyDate,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
List<Object?> get props => [monthlyDate, yearlyDate];
|
|
||||||
}
|
|
@ -1,22 +0,0 @@
|
|||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/community_model.dart';
|
|
||||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/space_model.dart';
|
|
||||||
|
|
||||||
abstract class AnalyticsDataLoadingStrategy {
|
|
||||||
void onCommunitySelected(
|
|
||||||
BuildContext context,
|
|
||||||
CommunityModel community,
|
|
||||||
List<SpaceModel> spaces,
|
|
||||||
);
|
|
||||||
void onSpaceSelected(
|
|
||||||
BuildContext context,
|
|
||||||
CommunityModel community,
|
|
||||||
SpaceModel space,
|
|
||||||
);
|
|
||||||
void onChildSpaceSelected(
|
|
||||||
BuildContext context,
|
|
||||||
CommunityModel community,
|
|
||||||
SpaceModel child,
|
|
||||||
);
|
|
||||||
void clearData(BuildContext context);
|
|
||||||
}
|
|
@ -1,14 +0,0 @@
|
|||||||
import 'package:syncrow_web/pages/analytics/modules/analytics/enums/analytics_page_tab.dart';
|
|
||||||
import 'package:syncrow_web/pages/analytics/modules/analytics/strategies/analytics_data_loading_strategy.dart';
|
|
||||||
import 'package:syncrow_web/pages/analytics/modules/analytics/strategies/energy_management_data_loading_strategy.dart';
|
|
||||||
import 'package:syncrow_web/pages/analytics/modules/analytics/strategies/occupancy_data_loading_strategy.dart';
|
|
||||||
|
|
||||||
abstract final class AnalyticsDataLoadingStrategyFactory {
|
|
||||||
const AnalyticsDataLoadingStrategyFactory._();
|
|
||||||
static AnalyticsDataLoadingStrategy getStrategy(AnalyticsPageTab tab) {
|
|
||||||
return switch (tab) {
|
|
||||||
AnalyticsPageTab.energyManagement => EnergyManagementDataLoadingStrategy(),
|
|
||||||
AnalyticsPageTab.occupancy => OccupancyDataLoadingStrategy(),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,63 +0,0 @@
|
|||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
|
||||||
import 'package:syncrow_web/pages/analytics/modules/analytics/strategies/analytics_data_loading_strategy.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/pages/space_tree/bloc/space_tree_event.dart';
|
|
||||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/community_model.dart';
|
|
||||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/space_model.dart';
|
|
||||||
|
|
||||||
class EnergyManagementDataLoadingStrategy implements AnalyticsDataLoadingStrategy {
|
|
||||||
@override
|
|
||||||
void onCommunitySelected(
|
|
||||||
BuildContext context,
|
|
||||||
CommunityModel community,
|
|
||||||
List<SpaceModel> spaces,
|
|
||||||
) {
|
|
||||||
context.read<SpaceTreeBloc>().add(
|
|
||||||
OnCommunitySelected(
|
|
||||||
community.uuid,
|
|
||||||
spaces,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
FetchEnergyManagementDataHelper.loadEnergyManagementData(context);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
void onSpaceSelected(
|
|
||||||
BuildContext context,
|
|
||||||
CommunityModel community,
|
|
||||||
SpaceModel space,
|
|
||||||
) {
|
|
||||||
context.read<SpaceTreeBloc>().add(
|
|
||||||
OnSpaceSelected(
|
|
||||||
community,
|
|
||||||
space.uuid ?? '',
|
|
||||||
space.children,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
FetchEnergyManagementDataHelper.loadEnergyManagementData(context);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
void onChildSpaceSelected(
|
|
||||||
BuildContext context,
|
|
||||||
CommunityModel community,
|
|
||||||
SpaceModel child,
|
|
||||||
) {
|
|
||||||
context.read<SpaceTreeBloc>().add(
|
|
||||||
OnSpaceSelected(
|
|
||||||
community,
|
|
||||||
child.uuid ?? '',
|
|
||||||
child.children,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
FetchEnergyManagementDataHelper.loadEnergyManagementData(context);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
void clearData(BuildContext context) {
|
|
||||||
context.read<SpaceTreeBloc>().add(const SpaceTreeClearSelectionEvent());
|
|
||||||
FetchEnergyManagementDataHelper.clearAllData(context);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,62 +0,0 @@
|
|||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
|
||||||
import 'package:syncrow_web/pages/analytics/modules/analytics/strategies/analytics_data_loading_strategy.dart';
|
|
||||||
import 'package:syncrow_web/pages/analytics/modules/occupancy/helpers/fetch_occupancy_data_helper.dart';
|
|
||||||
import 'package:syncrow_web/pages/space_tree/bloc/space_tree_bloc.dart';
|
|
||||||
import 'package:syncrow_web/pages/space_tree/bloc/space_tree_event.dart';
|
|
||||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/community_model.dart';
|
|
||||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/space_model.dart';
|
|
||||||
|
|
||||||
class OccupancyDataLoadingStrategy implements AnalyticsDataLoadingStrategy {
|
|
||||||
@override
|
|
||||||
void onCommunitySelected(
|
|
||||||
BuildContext context,
|
|
||||||
CommunityModel community,
|
|
||||||
List<SpaceModel> spaces,
|
|
||||||
) {
|
|
||||||
context.read<SpaceTreeBloc>().add(
|
|
||||||
OnCommunitySelected(
|
|
||||||
community.uuid,
|
|
||||||
spaces,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
FetchOccupancyDataHelper.loadOccupancyData(context);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
void onSpaceSelected(
|
|
||||||
BuildContext context,
|
|
||||||
CommunityModel community,
|
|
||||||
SpaceModel space,
|
|
||||||
) {
|
|
||||||
context.read<SpaceTreeBloc>().add(
|
|
||||||
OnSpaceSelected(
|
|
||||||
community,
|
|
||||||
space.uuid ?? '',
|
|
||||||
space.children,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
FetchOccupancyDataHelper.loadOccupancyData(context);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
void onChildSpaceSelected(
|
|
||||||
BuildContext context,
|
|
||||||
CommunityModel community,
|
|
||||||
SpaceModel child,
|
|
||||||
) {
|
|
||||||
context.read<SpaceTreeBloc>().add(
|
|
||||||
OnSpaceSelected(
|
|
||||||
community,
|
|
||||||
child.uuid ?? '',
|
|
||||||
child.children,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
FetchOccupancyDataHelper.loadOccupancyData(context);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
void clearData(BuildContext context) {
|
|
||||||
context.read<SpaceTreeBloc>().add(const SpaceTreeClearSelectionEvent());
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,6 +1,5 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
import 'package:syncrow_web/pages/analytics/modules/analytics/blocs/analytics_date_picker_bloc/analytics_date_picker_bloc.dart';
|
|
||||||
import 'package:syncrow_web/pages/analytics/modules/analytics/blocs/analytics_tab/analytics_tab_bloc.dart';
|
import 'package:syncrow_web/pages/analytics/modules/analytics/blocs/analytics_tab/analytics_tab_bloc.dart';
|
||||||
import 'package:syncrow_web/pages/analytics/modules/analytics/widgets/analytics_communities_sidebar.dart';
|
import 'package:syncrow_web/pages/analytics/modules/analytics/widgets/analytics_communities_sidebar.dart';
|
||||||
import 'package:syncrow_web/pages/analytics/modules/analytics/widgets/analytics_page_tabs_and_children.dart';
|
import 'package:syncrow_web/pages/analytics/modules/analytics/widgets/analytics_page_tabs_and_children.dart';
|
||||||
@ -9,15 +8,11 @@ import 'package:syncrow_web/pages/analytics/modules/energy_management/blocs/ener
|
|||||||
import 'package:syncrow_web/pages/analytics/modules/energy_management/blocs/power_clamp_info/power_clamp_info_bloc.dart';
|
import 'package:syncrow_web/pages/analytics/modules/energy_management/blocs/power_clamp_info/power_clamp_info_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/blocs/realtime_device_changes/realtime_device_changes_bloc.dart';
|
||||||
import 'package:syncrow_web/pages/analytics/modules/energy_management/blocs/total_energy_consumption/total_energy_consumption_bloc.dart';
|
import 'package:syncrow_web/pages/analytics/modules/energy_management/blocs/total_energy_consumption/total_energy_consumption_bloc.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/services/energy_consumption_by_phases/fake_energy_consumption_by_phases_service.dart';
|
import 'package:syncrow_web/pages/analytics/services/energy_consumption_by_phases/fake_energy_consumption_by_phases_service.dart';
|
||||||
import 'package:syncrow_web/pages/analytics/services/energy_consumption_per_device/fake_energy_consumption_per_device_service.dart';
|
import 'package:syncrow_web/pages/analytics/services/energy_consumption_per_device/fake_energy_consumption_per_device_service.dart';
|
||||||
import 'package:syncrow_web/pages/analytics/services/occupacy/fake_occupacy_service.dart';
|
|
||||||
import 'package:syncrow_web/pages/analytics/services/occupancy_heat_map/fake_occupancy_heat_map_service.dart';
|
|
||||||
import 'package:syncrow_web/pages/analytics/services/power_clamp_info/remote_power_clamp_info_service.dart';
|
import 'package:syncrow_web/pages/analytics/services/power_clamp_info/remote_power_clamp_info_service.dart';
|
||||||
import 'package:syncrow_web/pages/analytics/services/realtime_device_service/firebase_realtime_device_service.dart';
|
import 'package:syncrow_web/pages/analytics/services/realtime_device_service/firebase_realtime_device_service.dart';
|
||||||
import 'package:syncrow_web/pages/analytics/services/total_energy_consumption/remote_total_energy_consumption_service.dart';
|
import 'package:syncrow_web/pages/analytics/services/total_energy_consumption/fake_total_energy_consumption_service.dart';
|
||||||
import 'package:syncrow_web/pages/device_managment/shared/navigate_home_grid_view.dart';
|
import 'package:syncrow_web/pages/device_managment/shared/navigate_home_grid_view.dart';
|
||||||
import 'package:syncrow_web/services/api/http_service.dart';
|
import 'package:syncrow_web/services/api/http_service.dart';
|
||||||
import 'package:syncrow_web/utils/theme/responsive_text_theme.dart';
|
import 'package:syncrow_web/utils/theme/responsive_text_theme.dart';
|
||||||
@ -35,7 +30,7 @@ class AnalyticsPage extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
BlocProvider(
|
BlocProvider(
|
||||||
create: (context) => TotalEnergyConsumptionBloc(
|
create: (context) => TotalEnergyConsumptionBloc(
|
||||||
RemoteTotalEnergyConsumptionService(HTTPService()),
|
FakeTotalEnergyConsumptionService(),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
BlocProvider(
|
BlocProvider(
|
||||||
@ -58,11 +53,6 @@ class AnalyticsPage extends StatelessWidget {
|
|||||||
FirebaseRealtimeDeviceService(),
|
FirebaseRealtimeDeviceService(),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
BlocProvider(create: (context) => OccupancyBloc(FakeOccupacyService())),
|
|
||||||
BlocProvider(
|
|
||||||
create: (context) => OccupancyHeatMapBloc(FakeOccupancyHeatMapService()),
|
|
||||||
),
|
|
||||||
BlocProvider(create: (context) => AnalyticsDatePickerBloc()),
|
|
||||||
],
|
],
|
||||||
child: const AnalyticsPageForm(),
|
child: const AnalyticsPageForm(),
|
||||||
);
|
);
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
import 'package:syncrow_web/pages/analytics/modules/analytics/blocs/analytics_tab/analytics_tab_bloc.dart';
|
import 'package:syncrow_web/pages/analytics/modules/energy_management/blocs/power_clamp_info/power_clamp_info_bloc.dart';
|
||||||
import 'package:syncrow_web/pages/analytics/modules/analytics/strategies/analytics_data_loading_strategy_factory.dart';
|
import 'package:syncrow_web/pages/analytics/modules/energy_management/helpers/fetch_energy_management_data_helper.dart';
|
||||||
import 'package:syncrow_web/pages/analytics/modules/analytics/widgets/sidebar/analytics_space_tree_view.dart';
|
import 'package:syncrow_web/pages/space_tree/view/space_tree_view.dart';
|
||||||
|
|
||||||
class AnalyticsCommunitiesSidebar extends StatelessWidget {
|
class AnalyticsCommunitiesSidebar extends StatelessWidget {
|
||||||
const AnalyticsCommunitiesSidebar({super.key});
|
const AnalyticsCommunitiesSidebar({super.key});
|
||||||
@ -11,24 +11,42 @@ class AnalyticsCommunitiesSidebar extends StatelessWidget {
|
|||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Builder(
|
return Builder(
|
||||||
builder: (context) {
|
builder: (context) {
|
||||||
final selectedTab = context.read<AnalyticsTabBloc>().state;
|
|
||||||
final strategy =
|
|
||||||
AnalyticsDataLoadingStrategyFactory.getStrategy(selectedTab);
|
|
||||||
|
|
||||||
// Clear data when tab changes
|
|
||||||
strategy.clearData(context);
|
|
||||||
|
|
||||||
return Expanded(
|
return Expanded(
|
||||||
child: AnalyticsSpaceTreeView(
|
child: SpaceTreeView(
|
||||||
onSelectCommunity: (community, spaces) {
|
title: const Text('Communities'),
|
||||||
strategy.onCommunitySelected(context, community, spaces);
|
shouldDisableDeselectingChildrenOfSelectedParent: true,
|
||||||
},
|
onSelect: () {
|
||||||
onSelectSpace: (community, space) {
|
/// Necessary to wait for the state to update before fethcing the data.
|
||||||
strategy.onSpaceSelected(context, community, space);
|
Future.delayed(
|
||||||
},
|
const Duration(milliseconds: 100),
|
||||||
onSelectChildSpace: (community, child) {
|
() {
|
||||||
strategy.onChildSpaceSelected(context, community, child);
|
if (context.mounted) {
|
||||||
|
FetchEnergyManagementDataHelper.fetchEnergyManagementData(
|
||||||
|
context,
|
||||||
|
);
|
||||||
|
FetchEnergyManagementDataHelper.loadRealtimeDeviceChanges(
|
||||||
|
context,
|
||||||
|
);
|
||||||
|
context.read<PowerClampInfoBloc>().add(
|
||||||
|
const ClearPowerClampInfoEvent(),
|
||||||
|
);
|
||||||
|
final (selectedCommunities, selectedSpaces) =
|
||||||
|
FetchEnergyManagementDataHelper
|
||||||
|
.getSelectedCommunitiesAndSpaces(context);
|
||||||
|
if (selectedCommunities.isEmpty && selectedSpaces.isEmpty) {
|
||||||
|
context.read<PowerClampInfoBloc>().add(
|
||||||
|
const ClearPowerClampInfoEvent(),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
FetchEnergyManagementDataHelper.loadPowerClampInfo(
|
||||||
|
context,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
},
|
},
|
||||||
|
isSide: false,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
@ -1,24 +1,15 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
import 'package:flutter_svg/svg.dart';
|
import 'package:flutter_svg/svg.dart';
|
||||||
import 'package:intl/intl.dart';
|
import 'package:intl/intl.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/analytics/widgets/month_picker_widget.dart';
|
import 'package:syncrow_web/pages/analytics/modules/analytics/widgets/month_picker_widget.dart';
|
||||||
import 'package:syncrow_web/pages/analytics/modules/analytics/widgets/year_picker_widget.dart';
|
import 'package:syncrow_web/pages/analytics/modules/energy_management/helpers/fetch_energy_management_data_helper.dart';
|
||||||
import 'package:syncrow_web/utils/color_manager.dart';
|
import 'package:syncrow_web/utils/color_manager.dart';
|
||||||
import 'package:syncrow_web/utils/constants/assets.dart';
|
import 'package:syncrow_web/utils/constants/assets.dart';
|
||||||
|
|
||||||
enum DatePickerType { month, year }
|
|
||||||
|
|
||||||
class AnalyticsDateFilterButton extends StatefulWidget {
|
class AnalyticsDateFilterButton extends StatefulWidget {
|
||||||
const AnalyticsDateFilterButton({
|
const AnalyticsDateFilterButton({super.key});
|
||||||
required this.selectedDate,
|
|
||||||
required this.onDateSelected,
|
|
||||||
this.datePickerType = DatePickerType.month,
|
|
||||||
super.key,
|
|
||||||
});
|
|
||||||
|
|
||||||
final DateTime selectedDate;
|
|
||||||
final void Function(DateTime)? onDateSelected;
|
|
||||||
final DatePickerType datePickerType;
|
|
||||||
|
|
||||||
static final _color = ColorsManager.blackColor.withValues(alpha: 0.8);
|
static final _color = ColorsManager.blackColor.withValues(alpha: 0.8);
|
||||||
|
|
||||||
@ -28,69 +19,79 @@ class AnalyticsDateFilterButton extends StatefulWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class _AnalyticsDateFilterButtonState extends State<AnalyticsDateFilterButton> {
|
class _AnalyticsDateFilterButtonState extends State<AnalyticsDateFilterButton> {
|
||||||
|
late final AnalyticsDatePickerBloc _analyticsDatePickerBloc;
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
_analyticsDatePickerBloc = AnalyticsDatePickerBloc();
|
||||||
|
super.initState();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
_analyticsDatePickerBloc.close();
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return TextButton.icon(
|
return BlocProvider.value(
|
||||||
style: TextButton.styleFrom(
|
value: _analyticsDatePickerBloc,
|
||||||
foregroundColor: AnalyticsDateFilterButton._color,
|
child: Builder(builder: (context) {
|
||||||
shape: RoundedRectangleBorder(
|
final selectedDate = context.watch<AnalyticsDatePickerBloc>().state;
|
||||||
borderRadius: BorderRadius.circular(16),
|
return TextButton.icon(
|
||||||
side: const BorderSide(
|
style: TextButton.styleFrom(
|
||||||
color: ColorsManager.greyColor,
|
foregroundColor: AnalyticsDateFilterButton._color,
|
||||||
width: 1,
|
shape: RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.circular(16),
|
||||||
|
side: const BorderSide(
|
||||||
|
color: ColorsManager.greyColor,
|
||||||
|
width: 1,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
backgroundColor: ColorsManager.transparentColor,
|
||||||
|
padding: const EdgeInsets.symmetric(
|
||||||
|
horizontal: 32,
|
||||||
|
vertical: 16,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
icon: SvgPicture.asset(
|
||||||
backgroundColor: ColorsManager.transparentColor,
|
Assets.blankCalendar,
|
||||||
padding: const EdgeInsets.symmetric(
|
height: 20,
|
||||||
horizontal: 32,
|
width: 20,
|
||||||
vertical: 16,
|
colorFilter:
|
||||||
),
|
ColorFilter.mode(AnalyticsDateFilterButton._color, BlendMode.srcIn),
|
||||||
),
|
),
|
||||||
icon: SvgPicture.asset(
|
label: Text(
|
||||||
Assets.blankCalendar,
|
_formatDate(selectedDate),
|
||||||
height: 20,
|
style: const TextStyle(
|
||||||
width: 20,
|
fontWeight: FontWeight.w700,
|
||||||
colorFilter:
|
),
|
||||||
ColorFilter.mode(AnalyticsDateFilterButton._color, BlendMode.srcIn),
|
),
|
||||||
),
|
onPressed: () {
|
||||||
label: Text(
|
showDialog(
|
||||||
_formatDate(widget.selectedDate),
|
context: context,
|
||||||
style: const TextStyle(
|
builder: (_) => MonthPickerWidget(
|
||||||
fontWeight: FontWeight.w700,
|
selectedDate: selectedDate,
|
||||||
),
|
onDateSelected: (value) {
|
||||||
),
|
_analyticsDatePickerBloc.add(
|
||||||
onPressed: () {
|
UpdateAnalyticsDatePickerEvent(value),
|
||||||
showDialog(
|
);
|
||||||
context: context,
|
FetchEnergyManagementDataHelper.fetchEnergyManagementData(
|
||||||
builder: (_) {
|
context,
|
||||||
return switch (widget.datePickerType) {
|
selectedDate: value,
|
||||||
DatePickerType.month => MonthPickerWidget(
|
);
|
||||||
selectedDate: widget.selectedDate,
|
},
|
||||||
onDateSelected: (value) {
|
),
|
||||||
widget.onDateSelected?.call(value);
|
);
|
||||||
},
|
|
||||||
),
|
|
||||||
DatePickerType.year => YearPickerWidget(
|
|
||||||
selectedDate: widget.selectedDate,
|
|
||||||
onDateSelected: (value) {
|
|
||||||
widget.onDateSelected?.call(value);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
};
|
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
},
|
}),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
String _formatDate(DateTime? date) {
|
String _formatDate(DateTime? date) {
|
||||||
final formatterBasedOnDatePickerType = switch (widget.datePickerType) {
|
final formatter = DateFormat('MMMM yyyy');
|
||||||
DatePickerType.month => DateFormat('MMMM yyyy'),
|
final formattedDate = formatter.format(date ?? DateTime.now());
|
||||||
DatePickerType.year => DateFormat('yyyy'),
|
|
||||||
};
|
|
||||||
final formattedDate = formatterBasedOnDatePickerType.format(
|
|
||||||
date ?? DateTime.now(),
|
|
||||||
);
|
|
||||||
return formattedDate;
|
return formattedDate;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,6 @@ import 'package:flutter/material.dart';
|
|||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
import 'package:syncrow_web/pages/analytics/modules/analytics/blocs/analytics_tab/analytics_tab_bloc.dart';
|
import 'package:syncrow_web/pages/analytics/modules/analytics/blocs/analytics_tab/analytics_tab_bloc.dart';
|
||||||
import 'package:syncrow_web/pages/analytics/modules/analytics/enums/analytics_page_tab.dart';
|
import 'package:syncrow_web/pages/analytics/modules/analytics/enums/analytics_page_tab.dart';
|
||||||
import 'package:syncrow_web/pages/analytics/modules/analytics/strategies/analytics_data_loading_strategy_factory.dart';
|
|
||||||
import 'package:syncrow_web/utils/color_manager.dart';
|
import 'package:syncrow_web/utils/color_manager.dart';
|
||||||
|
|
||||||
class AnalyticsPageTabButton extends StatelessWidget {
|
class AnalyticsPageTabButton extends StatelessWidget {
|
||||||
@ -18,12 +17,9 @@ class AnalyticsPageTabButton extends StatelessWidget {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return TextButton(
|
return TextButton(
|
||||||
onPressed: () {
|
onPressed: () => context.read<AnalyticsTabBloc>().add(
|
||||||
AnalyticsDataLoadingStrategyFactory.getStrategy(tab).clearData(context);
|
|
||||||
context.read<AnalyticsTabBloc>().add(
|
|
||||||
UpdateAnalyticsTabEvent(tab),
|
UpdateAnalyticsTabEvent(tab),
|
||||||
);
|
),
|
||||||
},
|
|
||||||
child: Text(
|
child: Text(
|
||||||
tab.title,
|
tab.title,
|
||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
|
@ -1,11 +1,9 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
import 'package:syncrow_web/pages/analytics/modules/analytics/blocs/analytics_date_picker_bloc/analytics_date_picker_bloc.dart';
|
|
||||||
import 'package:syncrow_web/pages/analytics/modules/analytics/blocs/analytics_tab/analytics_tab_bloc.dart';
|
import 'package:syncrow_web/pages/analytics/modules/analytics/blocs/analytics_tab/analytics_tab_bloc.dart';
|
||||||
import 'package:syncrow_web/pages/analytics/modules/analytics/enums/analytics_page_tab.dart';
|
import 'package:syncrow_web/pages/analytics/modules/analytics/enums/analytics_page_tab.dart';
|
||||||
import 'package:syncrow_web/pages/analytics/modules/analytics/widgets/analytics_date_filter_button.dart';
|
import 'package:syncrow_web/pages/analytics/modules/analytics/widgets/analytics_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/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/utils/style.dart';
|
import 'package:syncrow_web/utils/style.dart';
|
||||||
|
|
||||||
class AnalyticsPageTabsAndChildren extends StatelessWidget {
|
class AnalyticsPageTabsAndChildren extends StatelessWidget {
|
||||||
@ -40,7 +38,9 @@ class AnalyticsPageTabsAndChildren extends StatelessWidget {
|
|||||||
crossAxisAlignment: CrossAxisAlignment.center,
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
children: [
|
children: [
|
||||||
...AnalyticsPageTab.values.map(
|
...AnalyticsPageTab.values.map(
|
||||||
(tab) => _buildAnimation(
|
(tab) => AnimatedSwitcher(
|
||||||
|
switchInCurve: Curves.easeIn,
|
||||||
|
duration: const Duration(milliseconds: 200),
|
||||||
child: AnalyticsPageTabButton(
|
child: AnalyticsPageTabButton(
|
||||||
key: ValueKey(selectedTab),
|
key: ValueKey(selectedTab),
|
||||||
tab: tab,
|
tab: tab,
|
||||||
@ -53,34 +53,12 @@ class AnalyticsPageTabsAndChildren extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
const Spacer(),
|
const Spacer(),
|
||||||
_buildAnimation(
|
const Expanded(
|
||||||
child: Visibility(
|
flex: 2,
|
||||||
key: ValueKey(selectedTab),
|
child: FittedBox(
|
||||||
visible: selectedTab == AnalyticsPageTab.energyManagement,
|
fit: BoxFit.scaleDown,
|
||||||
child: Expanded(
|
alignment: AlignmentDirectional.centerEnd,
|
||||||
flex: 2,
|
child: AnalyticsDateFilterButton(),
|
||||||
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,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
@ -89,18 +67,14 @@ class AnalyticsPageTabsAndChildren extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
Expanded(
|
Expanded(
|
||||||
flex: 8,
|
flex: 8,
|
||||||
child: _buildAnimation(child: selectedTab.child),
|
child: AnimatedSwitcher(
|
||||||
|
switchInCurve: Curves.easeIn,
|
||||||
|
duration: const Duration(milliseconds: 200),
|
||||||
|
child: selectedTab.child,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildAnimation({required Widget child}) {
|
|
||||||
return AnimatedSwitcher(
|
|
||||||
switchInCurve: Curves.easeIn,
|
|
||||||
duration: const Duration(milliseconds: 200),
|
|
||||||
child: child,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -1,188 +0,0 @@
|
|||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
|
||||||
import 'package:syncrow_web/common/widgets/search_bar.dart';
|
|
||||||
import 'package:syncrow_web/common/widgets/sidebar_communities_list.dart';
|
|
||||||
import 'package:syncrow_web/pages/space_tree/bloc/space_tree_bloc.dart';
|
|
||||||
import 'package:syncrow_web/pages/space_tree/bloc/space_tree_event.dart';
|
|
||||||
import 'package:syncrow_web/pages/space_tree/bloc/space_tree_state.dart';
|
|
||||||
import 'package:syncrow_web/pages/space_tree/view/custom_expansion.dart';
|
|
||||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/community_model.dart';
|
|
||||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/space_model.dart';
|
|
||||||
import 'package:syncrow_web/utils/color_manager.dart';
|
|
||||||
import 'package:syncrow_web/utils/extension/build_context_x.dart';
|
|
||||||
|
|
||||||
class AnalyticsSpaceTreeView extends StatefulWidget {
|
|
||||||
const AnalyticsSpaceTreeView({
|
|
||||||
super.key,
|
|
||||||
this.onSelectCommunity,
|
|
||||||
this.onSelectSpace,
|
|
||||||
this.onSelectChildSpace,
|
|
||||||
});
|
|
||||||
|
|
||||||
final void Function(
|
|
||||||
CommunityModel community,
|
|
||||||
List<SpaceModel> spaces,
|
|
||||||
)? onSelectCommunity;
|
|
||||||
final void Function(
|
|
||||||
CommunityModel community,
|
|
||||||
SpaceModel space,
|
|
||||||
)? onSelectSpace;
|
|
||||||
final void Function(
|
|
||||||
CommunityModel community,
|
|
||||||
SpaceModel child,
|
|
||||||
)? onSelectChildSpace;
|
|
||||||
|
|
||||||
@override
|
|
||||||
State<AnalyticsSpaceTreeView> createState() => _AnalyticsSpaceTreeViewState();
|
|
||||||
}
|
|
||||||
|
|
||||||
class _AnalyticsSpaceTreeViewState extends State<AnalyticsSpaceTreeView> {
|
|
||||||
late final ScrollController _scrollController;
|
|
||||||
|
|
||||||
@override
|
|
||||||
void initState() {
|
|
||||||
_scrollController = ScrollController();
|
|
||||||
super.initState();
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
void dispose() {
|
|
||||||
_scrollController.dispose();
|
|
||||||
super.dispose();
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return BlocBuilder<SpaceTreeBloc, SpaceTreeState>(builder: (context, state) {
|
|
||||||
final communities = state.searchQuery.isNotEmpty
|
|
||||||
? state.filteredCommunity
|
|
||||||
: state.communityList;
|
|
||||||
return Container(
|
|
||||||
height: MediaQuery.sizeOf(context).height,
|
|
||||||
decoration: const BoxDecoration(color: ColorsManager.whiteColors),
|
|
||||||
child: state is SpaceTreeLoadingState
|
|
||||||
? const Center(child: CircularProgressIndicator())
|
|
||||||
: Column(
|
|
||||||
children: [
|
|
||||||
Container(
|
|
||||||
alignment: AlignmentDirectional.centerStart,
|
|
||||||
padding: const EdgeInsets.all(24),
|
|
||||||
child: DefaultTextStyle(
|
|
||||||
style: context.textTheme.titleMedium!.copyWith(
|
|
||||||
color: ColorsManager.blackColor,
|
|
||||||
fontSize: 20,
|
|
||||||
),
|
|
||||||
child: const Text('Communities'),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
CustomSearchBar(
|
|
||||||
onSearchChanged: (query) => context.read<SpaceTreeBloc>().add(
|
|
||||||
SearchQueryEvent(query),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(height: 16),
|
|
||||||
Expanded(
|
|
||||||
child: state.isSearching
|
|
||||||
? const Center(child: CircularProgressIndicator())
|
|
||||||
: SidebarCommunitiesList(
|
|
||||||
onScrollToEnd: () {
|
|
||||||
if (!state.paginationIsLoading) {
|
|
||||||
context.read<SpaceTreeBloc>().add(
|
|
||||||
PaginationEvent(
|
|
||||||
state.paginationModel,
|
|
||||||
state.communityList,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
scrollController: _scrollController,
|
|
||||||
communities: communities,
|
|
||||||
itemBuilder: (context, index) {
|
|
||||||
return CustomExpansionTileSpaceTree(
|
|
||||||
title: communities[index].name,
|
|
||||||
isSelected: state.selectedCommunities
|
|
||||||
.contains(communities[index].uuid),
|
|
||||||
isSoldCheck: state.selectedCommunities
|
|
||||||
.contains(communities[index].uuid),
|
|
||||||
onExpansionChanged: () =>
|
|
||||||
context.read<SpaceTreeBloc>().add(
|
|
||||||
OnCommunityExpanded(
|
|
||||||
communities[index].uuid,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
isExpanded: state.expandedCommunities.contains(
|
|
||||||
communities[index].uuid,
|
|
||||||
),
|
|
||||||
onItemSelected: () => widget.onSelectCommunity?.call(
|
|
||||||
communities[index],
|
|
||||||
communities[index].spaces,
|
|
||||||
),
|
|
||||||
children: communities[index].spaces.map(
|
|
||||||
(space) {
|
|
||||||
return CustomExpansionTileSpaceTree(
|
|
||||||
title: space.name,
|
|
||||||
isExpanded:
|
|
||||||
state.expandedSpaces.contains(space.uuid),
|
|
||||||
onItemSelected: () =>
|
|
||||||
widget.onSelectSpace?.call(
|
|
||||||
communities[index],
|
|
||||||
space,
|
|
||||||
),
|
|
||||||
onExpansionChanged: () =>
|
|
||||||
context.read<SpaceTreeBloc>().add(
|
|
||||||
OnSpaceExpanded(
|
|
||||||
communities[index].uuid,
|
|
||||||
space.uuid ?? '',
|
|
||||||
),
|
|
||||||
),
|
|
||||||
isSelected: state.selectedSpaces
|
|
||||||
.contains(space.uuid) ||
|
|
||||||
state.soldCheck.contains(space.uuid),
|
|
||||||
isSoldCheck:
|
|
||||||
state.soldCheck.contains(space.uuid),
|
|
||||||
children: _buildNestedSpaces(
|
|
||||||
context,
|
|
||||||
state,
|
|
||||||
space,
|
|
||||||
communities[index],
|
|
||||||
),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
).toList(),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
),
|
|
||||||
if (state.paginationIsLoading) const CircularProgressIndicator(),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
List<Widget> _buildNestedSpaces(
|
|
||||||
BuildContext context,
|
|
||||||
SpaceTreeState state,
|
|
||||||
SpaceModel space,
|
|
||||||
CommunityModel community,
|
|
||||||
) {
|
|
||||||
return space.children.map((child) {
|
|
||||||
return CustomExpansionTileSpaceTree(
|
|
||||||
isSelected: state.selectedSpaces.contains(child.uuid) ||
|
|
||||||
state.soldCheck.contains(child.uuid),
|
|
||||||
isSoldCheck: state.soldCheck.contains(child.uuid),
|
|
||||||
title: child.name,
|
|
||||||
isExpanded: state.expandedSpaces.contains(child.uuid),
|
|
||||||
onItemSelected: () {
|
|
||||||
widget.onSelectChildSpace?.call(community, child);
|
|
||||||
},
|
|
||||||
onExpansionChanged: () {
|
|
||||||
context.read<SpaceTreeBloc>().add(
|
|
||||||
OnSpaceExpanded(community.uuid, child.uuid ?? ''),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
children: _buildNestedSpaces(context, state, child, community),
|
|
||||||
);
|
|
||||||
}).toList();
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,147 +0,0 @@
|
|||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:syncrow_web/utils/color_manager.dart';
|
|
||||||
import 'package:syncrow_web/utils/extension/build_context_x.dart';
|
|
||||||
|
|
||||||
class YearPickerWidget extends StatefulWidget {
|
|
||||||
const YearPickerWidget({
|
|
||||||
super.key,
|
|
||||||
required this.selectedDate,
|
|
||||||
required this.onDateSelected,
|
|
||||||
});
|
|
||||||
|
|
||||||
final DateTime selectedDate;
|
|
||||||
final ValueChanged<DateTime>? onDateSelected;
|
|
||||||
|
|
||||||
@override
|
|
||||||
State<YearPickerWidget> createState() => _YearPickerWidgetState();
|
|
||||||
}
|
|
||||||
|
|
||||||
class _YearPickerWidgetState extends State<YearPickerWidget> {
|
|
||||||
late int _currentYear;
|
|
||||||
|
|
||||||
static final years = List.generate(
|
|
||||||
DateTime.now().year - 2020 + 1,
|
|
||||||
(index) => (2020 + index),
|
|
||||||
);
|
|
||||||
|
|
||||||
@override
|
|
||||||
void initState() {
|
|
||||||
super.initState();
|
|
||||||
_currentYear = widget.selectedDate.year;
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return Dialog(
|
|
||||||
backgroundColor: Theme.of(context).colorScheme.surface,
|
|
||||||
child: Container(
|
|
||||||
padding: const EdgeInsetsDirectional.all(20),
|
|
||||||
width: 320,
|
|
||||||
child: Column(
|
|
||||||
mainAxisSize: MainAxisSize.min,
|
|
||||||
children: [
|
|
||||||
_buildMonthsGrid(),
|
|
||||||
const SizedBox(height: 20),
|
|
||||||
Row(
|
|
||||||
spacing: 12,
|
|
||||||
mainAxisAlignment: MainAxisAlignment.end,
|
|
||||||
children: [
|
|
||||||
FilledButton(
|
|
||||||
onPressed: () => Navigator.pop(context),
|
|
||||||
style: FilledButton.styleFrom(
|
|
||||||
fixedSize: const Size(106, 40),
|
|
||||||
backgroundColor: const Color(0xFFEDF2F7),
|
|
||||||
padding: const EdgeInsetsDirectional.symmetric(
|
|
||||||
vertical: 10,
|
|
||||||
horizontal: 16,
|
|
||||||
),
|
|
||||||
shape: RoundedRectangleBorder(
|
|
||||||
borderRadius: BorderRadius.circular(20),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
child: Text(
|
|
||||||
'Cancel',
|
|
||||||
style: context.textTheme.titleSmall?.copyWith(
|
|
||||||
fontSize: 14,
|
|
||||||
fontWeight: FontWeight.w600,
|
|
||||||
color: ColorsManager.grey700,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
FilledButton(
|
|
||||||
onPressed: () {
|
|
||||||
Navigator.pop(context);
|
|
||||||
final date = DateTime(_currentYear);
|
|
||||||
widget.onDateSelected?.call(date);
|
|
||||||
},
|
|
||||||
style: FilledButton.styleFrom(
|
|
||||||
fixedSize: const Size(106, 40),
|
|
||||||
backgroundColor: ColorsManager.vividBlue.withValues(
|
|
||||||
alpha: 0.7,
|
|
||||||
),
|
|
||||||
padding: const EdgeInsetsDirectional.symmetric(
|
|
||||||
vertical: 10,
|
|
||||||
horizontal: 16,
|
|
||||||
),
|
|
||||||
shape: RoundedRectangleBorder(
|
|
||||||
borderRadius: BorderRadius.circular(20),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
child: Text(
|
|
||||||
'Done',
|
|
||||||
style: context.textTheme.titleSmall?.copyWith(
|
|
||||||
fontSize: 14,
|
|
||||||
fontWeight: FontWeight.w600,
|
|
||||||
color: ColorsManager.whiteColors,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Widget _buildMonthsGrid() {
|
|
||||||
return GridView.builder(
|
|
||||||
shrinkWrap: true,
|
|
||||||
itemCount: years.length,
|
|
||||||
physics: const NeverScrollableScrollPhysics(),
|
|
||||||
padding: const EdgeInsets.all(8),
|
|
||||||
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
|
|
||||||
crossAxisCount: 3,
|
|
||||||
childAspectRatio: 2.5,
|
|
||||||
mainAxisSpacing: 8,
|
|
||||||
mainAxisExtent: 30,
|
|
||||||
),
|
|
||||||
itemBuilder: (context, index) {
|
|
||||||
final isSelected = _currentYear == years[index];
|
|
||||||
return InkWell(
|
|
||||||
onTap: () => setState(() => _currentYear = years[index]),
|
|
||||||
child: Container(
|
|
||||||
alignment: Alignment.center,
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: isSelected
|
|
||||||
? ColorsManager.vividBlue.withValues(alpha: 0.7)
|
|
||||||
: const Color(0xFFEDF2F7),
|
|
||||||
borderRadius:
|
|
||||||
isSelected ? BorderRadius.circular(15) : BorderRadius.zero,
|
|
||||||
),
|
|
||||||
child: Text(
|
|
||||||
years[index].toString(),
|
|
||||||
style: context.textTheme.titleSmall?.copyWith(
|
|
||||||
fontSize: 12,
|
|
||||||
color: isSelected
|
|
||||||
? ColorsManager.whiteColors
|
|
||||||
: ColorsManager.blackColor.withValues(alpha: 0.8),
|
|
||||||
fontWeight: FontWeight.w500,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
@ -24,7 +24,7 @@ abstract final class EnergyManagementChartsHelper {
|
|||||||
getTitlesWidget: (value, meta) => Padding(
|
getTitlesWidget: (value, meta) => Padding(
|
||||||
padding: const EdgeInsetsDirectional.only(top: 20.0),
|
padding: const EdgeInsetsDirectional.only(top: 20.0),
|
||||||
child: Text(
|
child: Text(
|
||||||
(value + 1).toString(),
|
value.toString(),
|
||||||
style: context.textTheme.bodySmall?.copyWith(
|
style: context.textTheme.bodySmall?.copyWith(
|
||||||
color: ColorsManager.greyColor,
|
color: ColorsManager.greyColor,
|
||||||
fontSize: 12,
|
fontSize: 12,
|
||||||
@ -70,7 +70,7 @@ abstract final class EnergyManagementChartsHelper {
|
|||||||
static List<LineTooltipItem?> getTooltipItems(List<LineBarSpot> touchedSpots) {
|
static List<LineTooltipItem?> getTooltipItems(List<LineBarSpot> touchedSpots) {
|
||||||
return touchedSpots.map((spot) {
|
return touchedSpots.map((spot) {
|
||||||
return LineTooltipItem(
|
return LineTooltipItem(
|
||||||
getToolTipLabel(spot.x + 1, spot.y),
|
getToolTipLabel(spot.x, spot.y),
|
||||||
const TextStyle(
|
const TextStyle(
|
||||||
color: ColorsManager.textPrimaryColor,
|
color: ColorsManager.textPrimaryColor,
|
||||||
fontWeight: FontWeight.w600,
|
fontWeight: FontWeight.w600,
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
import 'package:syncrow_web/pages/analytics/modules/analytics/blocs/analytics_date_picker_bloc/analytics_date_picker_bloc.dart';
|
|
||||||
import 'package:syncrow_web/pages/analytics/modules/energy_management/blocs/energy_consumption_by_phases/energy_consumption_by_phases_bloc.dart';
|
import 'package:syncrow_web/pages/analytics/modules/energy_management/blocs/energy_consumption_by_phases/energy_consumption_by_phases_bloc.dart';
|
||||||
import 'package:syncrow_web/pages/analytics/modules/energy_management/blocs/energy_consumption_per_device/energy_consumption_per_device_bloc.dart';
|
import 'package:syncrow_web/pages/analytics/modules/energy_management/blocs/energy_consumption_per_device/energy_consumption_per_device_bloc.dart';
|
||||||
import 'package:syncrow_web/pages/analytics/modules/energy_management/blocs/power_clamp_info/power_clamp_info_bloc.dart';
|
import 'package:syncrow_web/pages/analytics/modules/energy_management/blocs/power_clamp_info/power_clamp_info_bloc.dart';
|
||||||
@ -25,35 +24,13 @@ abstract final class FetchEnergyManagementDataHelper {
|
|||||||
clearAllData(context);
|
clearAllData(context);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
final datePickerState = context.read<AnalyticsDatePickerBloc>().state;
|
|
||||||
|
|
||||||
loadTotalEnergyConsumption(context, selectedDate: datePickerState.monthlyDate);
|
loadTotalEnergyConsumption(context);
|
||||||
loadEnergyConsumptionByPhases(
|
loadEnergyConsumptionByPhases(context);
|
||||||
context,
|
|
||||||
selectedDate: datePickerState.monthlyDate,
|
|
||||||
);
|
|
||||||
loadEnergyConsumptionPerDevice(context);
|
loadEnergyConsumptionPerDevice(context);
|
||||||
return;
|
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)
|
static (List<String> selectedCommunities, List<String> selectedSpaces)
|
||||||
getSelectedCommunitiesAndSpaces(BuildContext context) {
|
getSelectedCommunitiesAndSpaces(BuildContext context) {
|
||||||
final spaceTreeState = context.read<SpaceTreeBloc>().state;
|
final spaceTreeState = context.read<SpaceTreeBloc>().state;
|
||||||
@ -85,8 +62,7 @@ abstract final class FetchEnergyManagementDataHelper {
|
|||||||
|
|
||||||
final param = GetTotalEnergyConsumptionParam(
|
final param = GetTotalEnergyConsumptionParam(
|
||||||
spaceId: selectedCommunities.firstOrNull,
|
spaceId: selectedCommunities.firstOrNull,
|
||||||
communityId: selectedCommunities.firstOrNull,
|
startDate: selectedDate,
|
||||||
monthDate: selectedDate,
|
|
||||||
);
|
);
|
||||||
context.read<TotalEnergyConsumptionBloc>().add(
|
context.read<TotalEnergyConsumptionBloc>().add(
|
||||||
TotalEnergyConsumptionLoadEvent(param: param),
|
TotalEnergyConsumptionLoadEvent(param: param),
|
||||||
|
@ -1,26 +1,13 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.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/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/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/analytics/modules/energy_management/widgets/total_energy_consumption_chart_box.dart';
|
||||||
|
|
||||||
class AnalyticsEnergyManagementView extends StatefulWidget {
|
class AnalyticsEnergyManagementView extends StatelessWidget {
|
||||||
const AnalyticsEnergyManagementView({super.key});
|
const AnalyticsEnergyManagementView({super.key});
|
||||||
|
|
||||||
@override
|
|
||||||
State<AnalyticsEnergyManagementView> createState() =>
|
|
||||||
_AnalyticsEnergyManagementViewState();
|
|
||||||
}
|
|
||||||
|
|
||||||
class _AnalyticsEnergyManagementViewState
|
|
||||||
extends State<AnalyticsEnergyManagementView> {
|
|
||||||
@override
|
|
||||||
void initState() {
|
|
||||||
FetchEnergyManagementDataHelper.loadEnergyManagementData(context);
|
|
||||||
super.initState();
|
|
||||||
}
|
|
||||||
|
|
||||||
static const _padding = EdgeInsetsDirectional.all(32);
|
static const _padding = EdgeInsetsDirectional.all(32);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return LayoutBuilder(
|
return LayoutBuilder(
|
||||||
|
@ -1,55 +1,36 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:syncrow_web/utils/color_manager.dart';
|
import 'package:syncrow_web/utils/color_manager.dart';
|
||||||
import 'package:syncrow_web/utils/extension/build_context_x.dart';
|
|
||||||
|
|
||||||
class PowerClampEnergyDataDeviceDropdown extends StatelessWidget {
|
class PowerClampEnergyDataDeviceDropdown extends StatelessWidget {
|
||||||
const PowerClampEnergyDataDeviceDropdown({super.key});
|
const PowerClampEnergyDataDeviceDropdown({super.key});
|
||||||
|
|
||||||
|
static final _color = ColorsManager.blackColor.withValues(alpha: 0.8);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Container(
|
return TextButton(
|
||||||
decoration: BoxDecoration(
|
style: TextButton.styleFrom(
|
||||||
borderRadius: BorderRadius.circular(16),
|
foregroundColor: _color,
|
||||||
border: Border.all(
|
shape: RoundedRectangleBorder(
|
||||||
color: ColorsManager.greyColor,
|
borderRadius: BorderRadius.circular(16),
|
||||||
width: 1,
|
side: const BorderSide(
|
||||||
|
color: ColorsManager.greyColor,
|
||||||
|
width: 1,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
backgroundColor: ColorsManager.transparentColor,
|
||||||
|
padding: const EdgeInsets.symmetric(
|
||||||
|
horizontal: 32,
|
||||||
|
vertical: 16,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
child: DropdownButton<String>(
|
child: const Text(
|
||||||
value: 'Device 1',
|
'Device 1',
|
||||||
isDense: true,
|
style: TextStyle(
|
||||||
borderRadius: BorderRadius.circular(16),
|
|
||||||
dropdownColor: ColorsManager.whiteColors,
|
|
||||||
underline: const SizedBox.shrink(),
|
|
||||||
icon: const RotatedBox(
|
|
||||||
quarterTurns: 1,
|
|
||||||
child: Icon(Icons.chevron_right, size: 16),
|
|
||||||
),
|
|
||||||
style: context.textTheme.labelSmall?.copyWith(
|
|
||||||
color: ColorsManager.textPrimaryColor,
|
|
||||||
fontWeight: FontWeight.w700,
|
fontWeight: FontWeight.w700,
|
||||||
fontSize: 14,
|
|
||||||
),
|
),
|
||||||
padding: const EdgeInsetsDirectional.symmetric(
|
|
||||||
horizontal: 20,
|
|
||||||
vertical: 2,
|
|
||||||
),
|
|
||||||
items: [
|
|
||||||
for (var i = 1; i < 10; i++)
|
|
||||||
DropdownMenuItem(
|
|
||||||
value: 'Device $i',
|
|
||||||
child: Text(
|
|
||||||
'Device $i',
|
|
||||||
style: context.textTheme.labelSmall?.copyWith(
|
|
||||||
color: ColorsManager.textPrimaryColor,
|
|
||||||
fontWeight: FontWeight.w700,
|
|
||||||
fontSize: 14,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
onChanged: (value) {},
|
|
||||||
),
|
),
|
||||||
|
onPressed: () {},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -23,7 +23,10 @@ class TotalEnergyConsumptionChart extends StatelessWidget {
|
|||||||
return Expanded(
|
return Expanded(
|
||||||
child: LineChart(
|
child: LineChart(
|
||||||
LineChartData(
|
LineChartData(
|
||||||
titlesData: EnergyManagementChartsHelper.titlesData(context),
|
titlesData: EnergyManagementChartsHelper.titlesData(
|
||||||
|
context,
|
||||||
|
leftTitlesInterval: 5000,
|
||||||
|
),
|
||||||
gridData: EnergyManagementChartsHelper.gridData(),
|
gridData: EnergyManagementChartsHelper.gridData(),
|
||||||
borderData: EnergyManagementChartsHelper.borderData(),
|
borderData: EnergyManagementChartsHelper.borderData(),
|
||||||
lineTouchData: EnergyManagementChartsHelper.lineTouchData(),
|
lineTouchData: EnergyManagementChartsHelper.lineTouchData(),
|
||||||
|
@ -1,37 +0,0 @@
|
|||||||
import 'package:bloc/bloc.dart';
|
|
||||||
import 'package:equatable/equatable.dart';
|
|
||||||
import 'package:syncrow_web/pages/analytics/models/occupacy.dart';
|
|
||||||
import 'package:syncrow_web/pages/analytics/params/get_occupancy_param.dart';
|
|
||||||
import 'package:syncrow_web/pages/analytics/services/occupacy/occupacy_service.dart';
|
|
||||||
|
|
||||||
part 'occupancy_event.dart';
|
|
||||||
part 'occupancy_state.dart';
|
|
||||||
|
|
||||||
class OccupancyBloc extends Bloc<OccupancyEvent, OccupancyState> {
|
|
||||||
OccupancyBloc(this._occupacyService) : super(const OccupancyState()) {
|
|
||||||
on<LoadOccupancyEvent>(_onLoadOccupancyEvent);
|
|
||||||
on<ClearOccupancyEvent>(_onClearOccupancyEvent);
|
|
||||||
}
|
|
||||||
|
|
||||||
final OccupacyService _occupacyService;
|
|
||||||
|
|
||||||
Future<void> _onLoadOccupancyEvent(
|
|
||||||
LoadOccupancyEvent event,
|
|
||||||
Emitter<OccupancyState> emit,
|
|
||||||
) async {
|
|
||||||
emit(state.copyWith(status: OccupancyStatus.loading));
|
|
||||||
try {
|
|
||||||
final chartData = await _occupacyService.load(event.param);
|
|
||||||
emit(state.copyWith(chartData: chartData, status: OccupancyStatus.loaded));
|
|
||||||
} catch (e) {
|
|
||||||
emit(state.copyWith(status: OccupancyStatus.failure, errorMessage: '$e'));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void _onClearOccupancyEvent(
|
|
||||||
ClearOccupancyEvent event,
|
|
||||||
Emitter<OccupancyState> emit,
|
|
||||||
) {
|
|
||||||
emit(const OccupancyState());
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,21 +0,0 @@
|
|||||||
part of 'occupancy_bloc.dart';
|
|
||||||
|
|
||||||
sealed class OccupancyEvent extends Equatable {
|
|
||||||
const OccupancyEvent();
|
|
||||||
|
|
||||||
@override
|
|
||||||
List<Object> get props => [];
|
|
||||||
}
|
|
||||||
|
|
||||||
final class LoadOccupancyEvent extends OccupancyEvent {
|
|
||||||
const LoadOccupancyEvent(this.param);
|
|
||||||
|
|
||||||
final GetOccupancyParam param;
|
|
||||||
|
|
||||||
@override
|
|
||||||
List<Object> get props => [param];
|
|
||||||
}
|
|
||||||
|
|
||||||
final class ClearOccupancyEvent extends OccupancyEvent {
|
|
||||||
const ClearOccupancyEvent();
|
|
||||||
}
|
|
@ -1,30 +0,0 @@
|
|||||||
part of 'occupancy_bloc.dart';
|
|
||||||
|
|
||||||
enum OccupancyStatus { initial, loading, loaded, failure }
|
|
||||||
|
|
||||||
final class OccupancyState extends Equatable {
|
|
||||||
const OccupancyState({
|
|
||||||
this.chartData = const [],
|
|
||||||
this.status = OccupancyStatus.initial,
|
|
||||||
this.errorMessage,
|
|
||||||
});
|
|
||||||
|
|
||||||
final List<Occupacy> chartData;
|
|
||||||
final OccupancyStatus status;
|
|
||||||
final String? errorMessage;
|
|
||||||
|
|
||||||
OccupancyState copyWith({
|
|
||||||
List<Occupacy>? chartData,
|
|
||||||
OccupancyStatus? status,
|
|
||||||
String? errorMessage,
|
|
||||||
}) {
|
|
||||||
return OccupancyState(
|
|
||||||
chartData: chartData ?? this.chartData,
|
|
||||||
status: status ?? this.status,
|
|
||||||
errorMessage: errorMessage ?? this.errorMessage,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
List<Object?> get props => [chartData, status, errorMessage];
|
|
||||||
}
|
|
@ -1,49 +0,0 @@
|
|||||||
import 'package:bloc/bloc.dart';
|
|
||||||
import 'package:equatable/equatable.dart';
|
|
||||||
import 'package:syncrow_web/pages/analytics/models/occupancy_heat_map_model.dart';
|
|
||||||
import 'package:syncrow_web/pages/analytics/params/get_occupancy_heat_map_param.dart';
|
|
||||||
import 'package:syncrow_web/pages/analytics/services/occupancy_heat_map/occupancy_heat_map_service.dart';
|
|
||||||
|
|
||||||
part 'occupancy_heat_map_event.dart';
|
|
||||||
part 'occupancy_heat_map_state.dart';
|
|
||||||
|
|
||||||
class OccupancyHeatMapBloc
|
|
||||||
extends Bloc<OccupancyHeatMapEvent, OccupancyHeatMapState> {
|
|
||||||
OccupancyHeatMapBloc(
|
|
||||||
this._occupancyHeatMapService,
|
|
||||||
) : super(const OccupancyHeatMapState()) {
|
|
||||||
on<LoadOccupancyHeatMapEvent>(_onLoadOccupancyHeatMapEvent);
|
|
||||||
on<ClearOccupancyHeatMapEvent>(_onClearOccupancyHeatMapEvent);
|
|
||||||
}
|
|
||||||
final OccupancyHeatMapService _occupancyHeatMapService;
|
|
||||||
|
|
||||||
Future<void> _onLoadOccupancyHeatMapEvent(
|
|
||||||
LoadOccupancyHeatMapEvent event,
|
|
||||||
Emitter<OccupancyHeatMapState> emit,
|
|
||||||
) async {
|
|
||||||
emit(state.copyWith(status: OccupancyHeatMapStatus.loading));
|
|
||||||
try {
|
|
||||||
final occupancyHeatMap = await _occupancyHeatMapService.load(event.param);
|
|
||||||
emit(
|
|
||||||
state.copyWith(
|
|
||||||
status: OccupancyHeatMapStatus.loaded,
|
|
||||||
heatMapData: occupancyHeatMap,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
} catch (e) {
|
|
||||||
emit(
|
|
||||||
state.copyWith(
|
|
||||||
status: OccupancyHeatMapStatus.failure,
|
|
||||||
errorMessage: e.toString(),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void _onClearOccupancyHeatMapEvent(
|
|
||||||
ClearOccupancyHeatMapEvent event,
|
|
||||||
Emitter<OccupancyHeatMapState> emit,
|
|
||||||
) {
|
|
||||||
emit(const OccupancyHeatMapState());
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,21 +0,0 @@
|
|||||||
part of 'occupancy_heat_map_bloc.dart';
|
|
||||||
|
|
||||||
sealed class OccupancyHeatMapEvent extends Equatable {
|
|
||||||
const OccupancyHeatMapEvent();
|
|
||||||
|
|
||||||
@override
|
|
||||||
List<Object> get props => [];
|
|
||||||
}
|
|
||||||
|
|
||||||
final class LoadOccupancyHeatMapEvent extends OccupancyHeatMapEvent {
|
|
||||||
const LoadOccupancyHeatMapEvent(this.param);
|
|
||||||
|
|
||||||
final GetOccupancyHeatMapParam param;
|
|
||||||
|
|
||||||
@override
|
|
||||||
List<Object> get props => [param];
|
|
||||||
}
|
|
||||||
|
|
||||||
final class ClearOccupancyHeatMapEvent extends OccupancyHeatMapEvent {
|
|
||||||
const ClearOccupancyHeatMapEvent();
|
|
||||||
}
|
|
@ -1,30 +0,0 @@
|
|||||||
part of 'occupancy_heat_map_bloc.dart';
|
|
||||||
|
|
||||||
enum OccupancyHeatMapStatus { initial, loading, loaded, failure }
|
|
||||||
|
|
||||||
final class OccupancyHeatMapState extends Equatable {
|
|
||||||
const OccupancyHeatMapState({
|
|
||||||
this.status = OccupancyHeatMapStatus.initial,
|
|
||||||
this.heatMapData = const [],
|
|
||||||
this.errorMessage,
|
|
||||||
});
|
|
||||||
|
|
||||||
final OccupancyHeatMapStatus status;
|
|
||||||
final String? errorMessage;
|
|
||||||
final List<OccupancyHeatMapModel> heatMapData;
|
|
||||||
|
|
||||||
OccupancyHeatMapState copyWith({
|
|
||||||
OccupancyHeatMapStatus? status,
|
|
||||||
List<OccupancyHeatMapModel>? heatMapData,
|
|
||||||
String? errorMessage,
|
|
||||||
}) {
|
|
||||||
return OccupancyHeatMapState(
|
|
||||||
status: status ?? this.status,
|
|
||||||
heatMapData: heatMapData ?? this.heatMapData,
|
|
||||||
errorMessage: errorMessage ?? this.errorMessage,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
List<Object?> get props => [status, errorMessage, heatMapData];
|
|
||||||
}
|
|
@ -1,57 +0,0 @@
|
|||||||
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';
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
final datePickerState = context.read<AnalyticsDatePickerBloc>().state;
|
|
||||||
|
|
||||||
context.read<OccupancyBloc>().add(
|
|
||||||
LoadOccupancyEvent(
|
|
||||||
GetOccupancyParam(
|
|
||||||
monthDate:
|
|
||||||
'${datePickerState.monthlyDate.year}-${datePickerState.monthlyDate.month}',
|
|
||||||
spaceUuid: selectedSpaces.firstOrNull,
|
|
||||||
communityUuid: selectedCommunities.first,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
context.read<OccupancyHeatMapBloc>().add(
|
|
||||||
LoadOccupancyHeatMapEvent(
|
|
||||||
GetOccupancyHeatMapParam(
|
|
||||||
spaceId: selectedSpaces.isNotEmpty ? selectedSpaces.first : '',
|
|
||||||
communityId:
|
|
||||||
selectedCommunities.isNotEmpty ? selectedCommunities.first : '',
|
|
||||||
year: datePickerState.yearlyDate,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
context.read<RealtimeDeviceChangesBloc>()
|
|
||||||
..add(const RealtimeDeviceChangesClosed())
|
|
||||||
..add(
|
|
||||||
const RealtimeDeviceChangesStarted('14fe6e7e-47af-4a07-ae0a-7c4a26ef8135'),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,68 +1,12 @@
|
|||||||
import 'package:flutter/material.dart';
|
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});
|
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
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final height = MediaQuery.sizeOf(context).height;
|
return const Center(
|
||||||
return LayoutBuilder(
|
child: Text('AnalyticsOccupancyView is Working!'),
|
||||||
builder: (context, constraints) {
|
|
||||||
final isMediumOrLess = constraints.maxWidth <= 900;
|
|
||||||
if (isMediumOrLess) {
|
|
||||||
return SingleChildScrollView(
|
|
||||||
padding: AnalyticsOccupancyView._padding,
|
|
||||||
child: Column(
|
|
||||||
spacing: 32,
|
|
||||||
children: [
|
|
||||||
SizedBox(height: height * 0.45, child: const OccupancyEndSideBar()),
|
|
||||||
SizedBox(height: height * 0.5, child: const OccupancyChartBox()),
|
|
||||||
SizedBox(height: height * 0.5, child: const Placeholder()),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return SingleChildScrollView(
|
|
||||||
child: Container(
|
|
||||||
padding: AnalyticsOccupancyView._padding,
|
|
||||||
height: height * 0.9,
|
|
||||||
child: const Row(
|
|
||||||
spacing: 32,
|
|
||||||
children: [
|
|
||||||
Expanded(
|
|
||||||
flex: 5,
|
|
||||||
child: Column(
|
|
||||||
spacing: 20,
|
|
||||||
children: [
|
|
||||||
Expanded(child: OccupancyChartBox()),
|
|
||||||
Expanded(child: OccupancyHeatMapBox()),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Expanded(flex: 2, child: OccupancyEndSideBar()),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,145 +0,0 @@
|
|||||||
import 'package:fl_chart/fl_chart.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:syncrow_web/pages/analytics/models/occupacy.dart';
|
|
||||||
import 'package:syncrow_web/pages/analytics/modules/energy_management/helpers/energy_management_charts_helper.dart';
|
|
||||||
import 'package:syncrow_web/utils/color_manager.dart';
|
|
||||||
import 'package:syncrow_web/utils/extension/build_context_x.dart';
|
|
||||||
|
|
||||||
class OccupancyChart extends StatelessWidget {
|
|
||||||
const OccupancyChart({required this.chartData, super.key});
|
|
||||||
|
|
||||||
final List<Occupacy> chartData;
|
|
||||||
|
|
||||||
static const _chartWidth = 16.0;
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return BarChart(
|
|
||||||
BarChartData(
|
|
||||||
maxY: 1.0,
|
|
||||||
gridData: EnergyManagementChartsHelper.gridData().copyWith(
|
|
||||||
checkToShowHorizontalLine: (value) => true,
|
|
||||||
horizontalInterval: 0.25,
|
|
||||||
),
|
|
||||||
borderData: EnergyManagementChartsHelper.borderData(),
|
|
||||||
barTouchData: _barTouchData(context),
|
|
||||||
titlesData: _titlesData(context),
|
|
||||||
barGroups: List.generate(chartData.length, (index) {
|
|
||||||
final actual = chartData[index];
|
|
||||||
return BarChartGroupData(
|
|
||||||
x: index,
|
|
||||||
barsSpace: 0,
|
|
||||||
groupVertically: true,
|
|
||||||
barRods: [
|
|
||||||
BarChartRodData(
|
|
||||||
toY: 1.0,
|
|
||||||
fromY: double.parse(actual.occupancy) + 0.025,
|
|
||||||
color: ColorsManager.graysColor,
|
|
||||||
width: _chartWidth,
|
|
||||||
borderRadius: BorderRadius.circular(10),
|
|
||||||
),
|
|
||||||
BarChartRodData(
|
|
||||||
toY: double.parse(actual.occupancy),
|
|
||||||
color: ColorsManager.vividBlue.withValues(alpha: 0.8),
|
|
||||||
width: _chartWidth,
|
|
||||||
borderRadius: BorderRadius.circular(10),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
}),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
BarTouchData _barTouchData(BuildContext context) {
|
|
||||||
return BarTouchData(
|
|
||||||
touchTooltipData: BarTouchTooltipData(
|
|
||||||
getTooltipColor: (touchTooltipItem) => ColorsManager.whiteColors,
|
|
||||||
tooltipBorder: const BorderSide(
|
|
||||||
color: ColorsManager.semiTransparentBlack,
|
|
||||||
),
|
|
||||||
tooltipRoundedRadius: 16,
|
|
||||||
tooltipPadding: const EdgeInsets.all(8),
|
|
||||||
getTooltipItem: (group, groupIndex, rod, rodIndex) => getTooltipItem(
|
|
||||||
context: context,
|
|
||||||
group: group,
|
|
||||||
groupIndex: groupIndex,
|
|
||||||
rod: rod,
|
|
||||||
rodIndex: rodIndex,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
BarTooltipItem? getTooltipItem({
|
|
||||||
required BuildContext context,
|
|
||||||
required BarChartGroupData group,
|
|
||||||
required int groupIndex,
|
|
||||||
required BarChartRodData rod,
|
|
||||||
required int rodIndex,
|
|
||||||
}) {
|
|
||||||
final data = chartData;
|
|
||||||
|
|
||||||
final occupancyValue = double.parse(data[group.x.toInt()].occupancy);
|
|
||||||
final percentage = '${(occupancyValue * 100).toStringAsFixed(0)}%';
|
|
||||||
|
|
||||||
return BarTooltipItem(
|
|
||||||
percentage,
|
|
||||||
context.textTheme.bodyMedium!.copyWith(
|
|
||||||
color: ColorsManager.blackColor,
|
|
||||||
fontSize: 14,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
FlTitlesData _titlesData(BuildContext context) {
|
|
||||||
final titlesData = EnergyManagementChartsHelper.titlesData(
|
|
||||||
context,
|
|
||||||
leftTitlesInterval: 250,
|
|
||||||
);
|
|
||||||
|
|
||||||
final leftTitles = titlesData.leftTitles.copyWith(
|
|
||||||
sideTitles: titlesData.leftTitles.sideTitles.copyWith(
|
|
||||||
reservedSize: 70,
|
|
||||||
interval: 0.25,
|
|
||||||
getTitlesWidget: (value, meta) => Padding(
|
|
||||||
padding: const EdgeInsetsDirectional.only(end: 12),
|
|
||||||
child: FittedBox(
|
|
||||||
alignment: AlignmentDirectional.centerStart,
|
|
||||||
fit: BoxFit.scaleDown,
|
|
||||||
child: Text(
|
|
||||||
'${(value * 100).toStringAsFixed(0)}%',
|
|
||||||
style: context.textTheme.bodySmall?.copyWith(
|
|
||||||
fontSize: 12,
|
|
||||||
color: ColorsManager.greyColor,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
final bottomTitles = AxisTitles(
|
|
||||||
sideTitles: SideTitles(
|
|
||||||
showTitles: true,
|
|
||||||
getTitlesWidget: (value, _) => FittedBox(
|
|
||||||
alignment: AlignmentDirectional.bottomCenter,
|
|
||||||
fit: BoxFit.scaleDown,
|
|
||||||
child: Text(
|
|
||||||
(value + 1).toString(),
|
|
||||||
style: context.textTheme.bodySmall?.copyWith(
|
|
||||||
color: ColorsManager.greyColor,
|
|
||||||
fontSize: 8,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
reservedSize: 36,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
return titlesData.copyWith(
|
|
||||||
leftTitles: leftTitles,
|
|
||||||
bottomTitles: bottomTitles,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,67 +0,0 @@
|
|||||||
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/analytics/widgets/analytics_date_filter_button.dart';
|
|
||||||
import 'package:syncrow_web/pages/analytics/modules/energy_management/widgets/chart_title.dart';
|
|
||||||
import 'package:syncrow_web/pages/analytics/modules/occupancy/blocs/occupancy/occupancy_bloc.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.dart';
|
|
||||||
import 'package:syncrow_web/pages/analytics/widgets/analytics_error_widget.dart';
|
|
||||||
import 'package:syncrow_web/utils/style.dart';
|
|
||||||
|
|
||||||
class OccupancyChartBox extends StatelessWidget {
|
|
||||||
const OccupancyChartBox({super.key});
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return BlocBuilder<OccupancyBloc, OccupancyState>(
|
|
||||||
builder: (context, state) {
|
|
||||||
return Container(
|
|
||||||
padding: const EdgeInsets.all(30),
|
|
||||||
decoration: containerWhiteDecoration,
|
|
||||||
child: Column(
|
|
||||||
spacing: 20,
|
|
||||||
mainAxisSize: MainAxisSize.min,
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: [
|
|
||||||
AnalyticsErrorWidget(state.errorMessage),
|
|
||||||
Row(
|
|
||||||
children: [
|
|
||||||
const Expanded(
|
|
||||||
flex: 3,
|
|
||||||
child: FittedBox(
|
|
||||||
alignment: AlignmentDirectional.centerStart,
|
|
||||||
fit: BoxFit.scaleDown,
|
|
||||||
child: ChartTitle(title: Text('Occupancy')),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const Spacer(),
|
|
||||||
Expanded(
|
|
||||||
child: FittedBox(
|
|
||||||
fit: BoxFit.scaleDown,
|
|
||||||
alignment: AlignmentDirectional.centerEnd,
|
|
||||||
child: AnalyticsDateFilterButton(
|
|
||||||
onDateSelected: (DateTime value) {
|
|
||||||
context.read<AnalyticsDatePickerBloc>().add(
|
|
||||||
UpdateAnalyticsDatePickerEvent(montlyDate: value),
|
|
||||||
);
|
|
||||||
FetchOccupancyDataHelper.loadOccupancyData(context);
|
|
||||||
},
|
|
||||||
selectedDate: context
|
|
||||||
.watch<AnalyticsDatePickerBloc>()
|
|
||||||
.state
|
|
||||||
.monthlyDate,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
const Divider(height: 0),
|
|
||||||
Expanded(child: OccupancyChart(chartData: state.chartData)),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,133 +0,0 @@
|
|||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
|
||||||
import 'package:syncrow_web/pages/analytics/models/power_clamp_energy_status.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/widgets/power_clamp_energy_data_device_dropdown.dart';
|
|
||||||
import 'package:syncrow_web/pages/analytics/modules/energy_management/widgets/power_clamp_energy_status_widget.dart';
|
|
||||||
import 'package:syncrow_web/pages/device_managment/all_devices/models/device_status.dart';
|
|
||||||
import 'package:syncrow_web/utils/color_manager.dart';
|
|
||||||
import 'package:syncrow_web/utils/constants/assets.dart';
|
|
||||||
import 'package:syncrow_web/utils/extension/build_context_x.dart';
|
|
||||||
import 'package:syncrow_web/utils/style.dart';
|
|
||||||
import 'package:uuid/uuid.dart';
|
|
||||||
|
|
||||||
class OccupancyEndSideBar extends StatelessWidget {
|
|
||||||
const OccupancyEndSideBar({super.key});
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return BlocBuilder<RealtimeDeviceChangesBloc, RealtimeDeviceChangesState>(
|
|
||||||
builder: (context, state) {
|
|
||||||
return Container(
|
|
||||||
decoration: subSectionContainerDecoration.copyWith(
|
|
||||||
borderRadius: BorderRadius.circular(30),
|
|
||||||
),
|
|
||||||
padding: const EdgeInsetsDirectional.all(32),
|
|
||||||
child: Column(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: [
|
|
||||||
_buildHeader(context),
|
|
||||||
Text(
|
|
||||||
'Device ID:',
|
|
||||||
style: context.textTheme.bodySmall?.copyWith(
|
|
||||||
color: ColorsManager.textPrimaryColor,
|
|
||||||
fontWeight: FontWeight.w400,
|
|
||||||
fontSize: 12,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(height: 6),
|
|
||||||
SelectableText(
|
|
||||||
(const Uuid().v4()),
|
|
||||||
style: context.textTheme.bodySmall?.copyWith(
|
|
||||||
color: ColorsManager.blackColor,
|
|
||||||
fontWeight: FontWeight.w400,
|
|
||||||
fontSize: 12,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(height: 10),
|
|
||||||
const Divider(height: 1, color: ColorsManager.greyColor),
|
|
||||||
const SizedBox(height: 50),
|
|
||||||
SizedBox(
|
|
||||||
height: MediaQuery.sizeOf(context).height * 0.2,
|
|
||||||
child: PowerClampEnergyStatusWidget(
|
|
||||||
status: [
|
|
||||||
PowerClampEnergyStatus(
|
|
||||||
iconPath: Assets.presenceState,
|
|
||||||
title: 'Presence Status',
|
|
||||||
value: _valueFromCode(
|
|
||||||
'presence_state',
|
|
||||||
state.deviceStatusList,
|
|
||||||
),
|
|
||||||
unit: '',
|
|
||||||
),
|
|
||||||
PowerClampEnergyStatus(
|
|
||||||
iconPath: Assets.presenceTimeIcon,
|
|
||||||
title: 'Presence Time',
|
|
||||||
value:
|
|
||||||
'${_valueFromCode('none_body_time', state.deviceStatusList)} Min',
|
|
||||||
unit: '',
|
|
||||||
),
|
|
||||||
PowerClampEnergyStatus(
|
|
||||||
iconPath: Assets.currentDistanceIcon,
|
|
||||||
title: 'Detection Distance',
|
|
||||||
value:
|
|
||||||
'${_valueFromCode('space_move_val', state.deviceStatusList)} M',
|
|
||||||
unit: '',
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(height: 20),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
String _valueFromCode(
|
|
||||||
String code,
|
|
||||||
List<Status> status, {
|
|
||||||
String? defaultValue,
|
|
||||||
}) {
|
|
||||||
final value = status
|
|
||||||
.firstWhere(
|
|
||||||
(e) => e.code == code,
|
|
||||||
orElse: () => Status(code: '--', value: '--'),
|
|
||||||
)
|
|
||||||
.value
|
|
||||||
.toString();
|
|
||||||
return value == 'null' ? defaultValue ?? '--' : value;
|
|
||||||
}
|
|
||||||
|
|
||||||
Widget _buildHeader(BuildContext context) {
|
|
||||||
return Row(
|
|
||||||
mainAxisSize: MainAxisSize.min,
|
|
||||||
children: [
|
|
||||||
Expanded(
|
|
||||||
flex: 2,
|
|
||||||
child: FittedBox(
|
|
||||||
fit: BoxFit.scaleDown,
|
|
||||||
alignment: AlignmentDirectional.centerStart,
|
|
||||||
child: SelectableText(
|
|
||||||
'Presnce Sensor',
|
|
||||||
style: context.textTheme.headlineSmall?.copyWith(
|
|
||||||
fontWeight: FontWeight.w700,
|
|
||||||
color: ColorsManager.vividBlue.withValues(alpha: 0.6),
|
|
||||||
fontSize: 18,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const Spacer(),
|
|
||||||
const Expanded(
|
|
||||||
child: FittedBox(
|
|
||||||
fit: BoxFit.scaleDown,
|
|
||||||
alignment: AlignmentDirectional.centerEnd,
|
|
||||||
child: PowerClampEnergyDataDeviceDropdown(),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,84 +0,0 @@
|
|||||||
import 'dart:math' as math show max;
|
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:syncrow_web/pages/analytics/modules/occupancy/widgets/occupancy_heat_map_days.dart';
|
|
||||||
import 'package:syncrow_web/pages/analytics/modules/occupancy/widgets/occupancy_heat_map_gradient.dart';
|
|
||||||
import 'package:syncrow_web/pages/analytics/modules/occupancy/widgets/occupancy_heat_map_months.dart';
|
|
||||||
import 'package:syncrow_web/pages/analytics/modules/occupancy/widgets/occupancy_painter.dart';
|
|
||||||
import 'package:syncrow_web/utils/color_manager.dart';
|
|
||||||
|
|
||||||
class OccupancyHeatMap extends StatelessWidget {
|
|
||||||
const OccupancyHeatMap({required this.heatMapData, super.key});
|
|
||||||
final Map<DateTime, int> heatMapData;
|
|
||||||
|
|
||||||
static const _cellSize = 16.0;
|
|
||||||
static const _totalWeeks = 53;
|
|
||||||
|
|
||||||
int get _maxValue => heatMapData.isNotEmpty
|
|
||||||
? heatMapData.keys.map((key) => heatMapData[key]!).reduce(math.max)
|
|
||||||
: 0;
|
|
||||||
|
|
||||||
DateTime _getStartingDate() {
|
|
||||||
final jan1 = DateTime(DateTime.now().year, 1, 1);
|
|
||||||
final startOfWeek = jan1.subtract(Duration(days: jan1.weekday - 1));
|
|
||||||
return startOfWeek;
|
|
||||||
}
|
|
||||||
|
|
||||||
List<OccupancyPaintItem> _generatePaintItems(DateTime startDate) {
|
|
||||||
return List.generate(_totalWeeks * 7, (index) {
|
|
||||||
final date = startDate.add(Duration(days: index));
|
|
||||||
final value = heatMapData[date] ?? 0;
|
|
||||||
return OccupancyPaintItem(index: index, value: value);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
final startDate = _getStartingDate();
|
|
||||||
final paintItems = _generatePaintItems(startDate);
|
|
||||||
|
|
||||||
return Column(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: [
|
|
||||||
OccupancyHeatMapMonths(startDate: startDate, cellSize: _cellSize),
|
|
||||||
Container(
|
|
||||||
decoration: const BoxDecoration(
|
|
||||||
border: Border(
|
|
||||||
bottom: BorderSide(color: ColorsManager.grayBorder),
|
|
||||||
top: BorderSide(color: ColorsManager.grayBorder),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
width: double.infinity,
|
|
||||||
child: Row(
|
|
||||||
children: [
|
|
||||||
Expanded(
|
|
||||||
child: FittedBox(
|
|
||||||
fit: BoxFit.fill,
|
|
||||||
child: Row(
|
|
||||||
children: [
|
|
||||||
const OccupancyHeatMapDays(cellSize: _cellSize),
|
|
||||||
CustomPaint(
|
|
||||||
size: const Size(_totalWeeks * _cellSize, 7 * _cellSize),
|
|
||||||
child: CustomPaint(
|
|
||||||
isComplex: true,
|
|
||||||
size: const Size(_totalWeeks * _cellSize, 7 * _cellSize),
|
|
||||||
painter: OccupancyPainter(
|
|
||||||
items: paintItems,
|
|
||||||
maxValue: _maxValue,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(height: 20),
|
|
||||||
OccupancyHeatMapGradient(maxValue: _maxValue),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,74 +0,0 @@
|
|||||||
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/analytics/widgets/analytics_date_filter_button.dart';
|
|
||||||
import 'package:syncrow_web/pages/analytics/modules/energy_management/widgets/chart_title.dart';
|
|
||||||
import 'package:syncrow_web/pages/analytics/modules/occupancy/blocs/occupancy_heat_map/occupancy_heat_map_bloc.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_heat_map.dart';
|
|
||||||
import 'package:syncrow_web/pages/analytics/widgets/analytics_error_widget.dart';
|
|
||||||
import 'package:syncrow_web/utils/style.dart';
|
|
||||||
|
|
||||||
class OccupancyHeatMapBox extends StatelessWidget {
|
|
||||||
const OccupancyHeatMapBox({super.key});
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return BlocBuilder<OccupancyHeatMapBloc, OccupancyHeatMapState>(
|
|
||||||
builder: (context, state) {
|
|
||||||
return Container(
|
|
||||||
padding: const EdgeInsets.all(30),
|
|
||||||
decoration: containerWhiteDecoration,
|
|
||||||
child: Column(
|
|
||||||
spacing: 20,
|
|
||||||
mainAxisSize: MainAxisSize.min,
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: [
|
|
||||||
AnalyticsErrorWidget(state.errorMessage),
|
|
||||||
Row(
|
|
||||||
children: [
|
|
||||||
const Expanded(
|
|
||||||
flex: 3,
|
|
||||||
child: FittedBox(
|
|
||||||
alignment: AlignmentDirectional.centerStart,
|
|
||||||
fit: BoxFit.scaleDown,
|
|
||||||
child: ChartTitle(title: Text('Occupancy Heat Map')),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const Spacer(),
|
|
||||||
Expanded(
|
|
||||||
child: FittedBox(
|
|
||||||
fit: BoxFit.scaleDown,
|
|
||||||
alignment: AlignmentDirectional.centerEnd,
|
|
||||||
child: AnalyticsDateFilterButton(
|
|
||||||
onDateSelected: (DateTime value) {
|
|
||||||
context.read<AnalyticsDatePickerBloc>().add(
|
|
||||||
UpdateAnalyticsDatePickerEvent(yearlyDate: value),
|
|
||||||
);
|
|
||||||
FetchOccupancyDataHelper.loadOccupancyData(context);
|
|
||||||
},
|
|
||||||
datePickerType: DatePickerType.year,
|
|
||||||
selectedDate: context
|
|
||||||
.watch<AnalyticsDatePickerBloc>()
|
|
||||||
.state
|
|
||||||
.yearlyDate,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
const Divider(height: 0),
|
|
||||||
Expanded(
|
|
||||||
child: OccupancyHeatMap(
|
|
||||||
heatMapData: state.heatMapData.asMap().map(
|
|
||||||
(_, value) => MapEntry(value.date, value.occupancy),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,51 +0,0 @@
|
|||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:syncrow_web/utils/color_manager.dart';
|
|
||||||
import 'package:syncrow_web/utils/extension/build_context_x.dart';
|
|
||||||
|
|
||||||
class OccupancyHeatMapDays extends StatelessWidget {
|
|
||||||
const OccupancyHeatMapDays({
|
|
||||||
required this.cellSize,
|
|
||||||
this.textColor = ColorsManager.blackColor,
|
|
||||||
super.key,
|
|
||||||
});
|
|
||||||
|
|
||||||
final double cellSize;
|
|
||||||
final Color textColor;
|
|
||||||
|
|
||||||
static const _weekDayLabels = [
|
|
||||||
'Monday',
|
|
||||||
'Tuesday',
|
|
||||||
'Wednesday',
|
|
||||||
'Thursday',
|
|
||||||
'Friday',
|
|
||||||
'Saturday',
|
|
||||||
'Sunday',
|
|
||||||
];
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return Column(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: List.generate(7, (i) {
|
|
||||||
final dayLabel = _weekDayLabels[i];
|
|
||||||
return Container(
|
|
||||||
height: cellSize,
|
|
||||||
alignment: AlignmentDirectional.centerStart,
|
|
||||||
margin: const EdgeInsetsDirectional.all(0.5).add(
|
|
||||||
const EdgeInsetsDirectional.only(end: 4),
|
|
||||||
),
|
|
||||||
padding: const EdgeInsets.only(right: 6),
|
|
||||||
child: Text(
|
|
||||||
dayLabel,
|
|
||||||
textAlign: TextAlign.start,
|
|
||||||
style: context.textTheme.bodySmall?.copyWith(
|
|
||||||
color: textColor,
|
|
||||||
fontSize: 8,
|
|
||||||
fontWeight: FontWeight.w500,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,47 +0,0 @@
|
|||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:syncrow_web/utils/color_manager.dart';
|
|
||||||
|
|
||||||
class OccupancyHeatMapGradient extends StatelessWidget {
|
|
||||||
const OccupancyHeatMapGradient({super.key, required this.maxValue});
|
|
||||||
|
|
||||||
final int maxValue;
|
|
||||||
List<Color> _heatMapColors() {
|
|
||||||
if (maxValue == 0) {
|
|
||||||
return [
|
|
||||||
ColorsManager.vividBlue.withValues(alpha: 0),
|
|
||||||
ColorsManager.vividBlue.withValues(alpha: 0),
|
|
||||||
];
|
|
||||||
}
|
|
||||||
return List.generate(
|
|
||||||
maxValue + 1,
|
|
||||||
(index) => ColorsManager.vividBlue.withValues(alpha: index / maxValue),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return Row(
|
|
||||||
children: [
|
|
||||||
const Spacer(),
|
|
||||||
Tooltip(
|
|
||||||
message: 'Min: 0 - Max: $maxValue',
|
|
||||||
child: Container(
|
|
||||||
width: 150,
|
|
||||||
height: 20,
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
border: Border.all(
|
|
||||||
color: ColorsManager.grayBorder,
|
|
||||||
width: 1,
|
|
||||||
),
|
|
||||||
gradient: LinearGradient(
|
|
||||||
begin: AlignmentDirectional.centerEnd,
|
|
||||||
end: AlignmentDirectional.centerStart,
|
|
||||||
colors: _heatMapColors(),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,60 +0,0 @@
|
|||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:intl/intl.dart';
|
|
||||||
import 'package:syncrow_web/pages/analytics/modules/occupancy/widgets/occupancy_heat_map_days.dart';
|
|
||||||
import 'package:syncrow_web/utils/color_manager.dart';
|
|
||||||
|
|
||||||
class OccupancyHeatMapMonths extends StatelessWidget {
|
|
||||||
const OccupancyHeatMapMonths({
|
|
||||||
required this.startDate,
|
|
||||||
required this.cellSize,
|
|
||||||
super.key,
|
|
||||||
});
|
|
||||||
|
|
||||||
final DateTime startDate;
|
|
||||||
final double cellSize;
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return Container(
|
|
||||||
height: 48,
|
|
||||||
width: double.infinity,
|
|
||||||
color: Colors.transparent,
|
|
||||||
child: Row(
|
|
||||||
mainAxisSize: MainAxisSize.min,
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.end,
|
|
||||||
children: [
|
|
||||||
OccupancyHeatMapDays(
|
|
||||||
cellSize: cellSize / 3,
|
|
||||||
textColor: Colors.transparent,
|
|
||||||
),
|
|
||||||
...List.generate(12, (monthIndex) {
|
|
||||||
final monthStartDate = DateTime(startDate.year, monthIndex + 1, 1);
|
|
||||||
final monthName = DateFormat.MMM().format(monthStartDate);
|
|
||||||
return Expanded(
|
|
||||||
child: RotatedBox(
|
|
||||||
quarterTurns: 3,
|
|
||||||
child: Container(
|
|
||||||
padding: EdgeInsetsDirectional.zero,
|
|
||||||
margin: EdgeInsetsDirectional.zero,
|
|
||||||
decoration: const BoxDecoration(
|
|
||||||
border: Border(
|
|
||||||
top: BorderSide(color: ColorsManager.borderColor),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
width: cellSize * 4,
|
|
||||||
child: Padding(
|
|
||||||
padding: const EdgeInsets.only(left: 4, top: 2),
|
|
||||||
child: Text(
|
|
||||||
monthName,
|
|
||||||
style: const TextStyle(fontSize: 8),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,84 +0,0 @@
|
|||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:syncrow_web/utils/color_manager.dart';
|
|
||||||
|
|
||||||
class OccupancyPaintItem {
|
|
||||||
final int index;
|
|
||||||
final int value;
|
|
||||||
|
|
||||||
const OccupancyPaintItem({required this.index, required this.value});
|
|
||||||
}
|
|
||||||
|
|
||||||
class OccupancyPainter extends CustomPainter {
|
|
||||||
OccupancyPainter({
|
|
||||||
required this.items,
|
|
||||||
required this.maxValue,
|
|
||||||
});
|
|
||||||
|
|
||||||
final List<OccupancyPaintItem> items;
|
|
||||||
final int maxValue;
|
|
||||||
|
|
||||||
static const double cellSize = 16.0;
|
|
||||||
|
|
||||||
@override
|
|
||||||
void paint(Canvas canvas, Size size) {
|
|
||||||
final Paint fillPaint = Paint();
|
|
||||||
final Paint borderPaint = Paint()
|
|
||||||
..color = ColorsManager.grayBorder.withValues(alpha: 0.4)
|
|
||||||
..style = PaintingStyle.stroke;
|
|
||||||
|
|
||||||
for (final item in items) {
|
|
||||||
final column = item.index ~/ 7;
|
|
||||||
final row = item.index % 7;
|
|
||||||
|
|
||||||
final x = column * cellSize;
|
|
||||||
final y = row * cellSize;
|
|
||||||
|
|
||||||
fillPaint.color = _getColor(item.value);
|
|
||||||
final rect = Rect.fromLTWH(x, y, cellSize, cellSize);
|
|
||||||
canvas.drawRect(rect, fillPaint);
|
|
||||||
|
|
||||||
_drawDashedLine(
|
|
||||||
canvas,
|
|
||||||
Offset(x, y),
|
|
||||||
Offset(x + cellSize, y),
|
|
||||||
borderPaint,
|
|
||||||
);
|
|
||||||
_drawDashedLine(
|
|
||||||
canvas,
|
|
||||||
Offset(x, y + cellSize),
|
|
||||||
Offset(x + cellSize, y + cellSize),
|
|
||||||
borderPaint,
|
|
||||||
);
|
|
||||||
|
|
||||||
canvas.drawLine(Offset(x, y), Offset(x, y + cellSize), borderPaint);
|
|
||||||
canvas.drawLine(
|
|
||||||
Offset(x + cellSize, y), Offset(x + cellSize, y + cellSize), borderPaint);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void _drawDashedLine(Canvas canvas, Offset start, Offset end, Paint paint) {
|
|
||||||
const double dashWidth = 2.0;
|
|
||||||
const double dashSpace = 4.0;
|
|
||||||
final double totalLength = (end - start).distance;
|
|
||||||
final Offset direction = (end - start) / (end - start).distance;
|
|
||||||
|
|
||||||
double currentLength = 0.0;
|
|
||||||
while (currentLength < totalLength) {
|
|
||||||
final Offset dashStart = start + direction * currentLength;
|
|
||||||
final double nextLength = currentLength + dashWidth;
|
|
||||||
final Offset dashEnd =
|
|
||||||
start + direction * (nextLength < totalLength ? nextLength : totalLength);
|
|
||||||
canvas.drawLine(dashStart, dashEnd, paint);
|
|
||||||
currentLength = nextLength + dashSpace;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Color _getColor(int value) {
|
|
||||||
if (maxValue == 0) return ColorsManager.vividBlue.withValues(alpha: 0);
|
|
||||||
final opacity = value.clamp(0, maxValue) / maxValue;
|
|
||||||
return ColorsManager.vividBlue.withValues(alpha: opacity);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
bool shouldRepaint(covariant CustomPainter oldDelegate) => false;
|
|
||||||
}
|
|
@ -1,19 +0,0 @@
|
|||||||
class GetOccupancyHeatMapParam {
|
|
||||||
final DateTime year;
|
|
||||||
final String communityId;
|
|
||||||
final String spaceId;
|
|
||||||
|
|
||||||
const GetOccupancyHeatMapParam({
|
|
||||||
required this.year,
|
|
||||||
required this.communityId,
|
|
||||||
required this.spaceId,
|
|
||||||
});
|
|
||||||
|
|
||||||
Map<String, dynamic> toJson() {
|
|
||||||
return {
|
|
||||||
'year': year.toIso8601String(),
|
|
||||||
'communityId': communityId,
|
|
||||||
'spaceId': spaceId,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,19 +0,0 @@
|
|||||||
class GetOccupancyParam {
|
|
||||||
final String monthDate;
|
|
||||||
final String? spaceUuid;
|
|
||||||
final String communityUuid;
|
|
||||||
|
|
||||||
GetOccupancyParam({
|
|
||||||
required this.monthDate,
|
|
||||||
required this.spaceUuid,
|
|
||||||
required this.communityUuid,
|
|
||||||
});
|
|
||||||
|
|
||||||
Map<String, dynamic> toJson() {
|
|
||||||
return {
|
|
||||||
'monthDate': monthDate,
|
|
||||||
'spaceUuid': spaceUuid,
|
|
||||||
'communityUuid': communityUuid,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,21 +1,19 @@
|
|||||||
class GetTotalEnergyConsumptionParam {
|
class GetTotalEnergyConsumptionParam {
|
||||||
final DateTime? monthDate;
|
final DateTime? startDate;
|
||||||
|
final DateTime? endDate;
|
||||||
final String? spaceId;
|
final String? spaceId;
|
||||||
final String? communityId;
|
|
||||||
|
|
||||||
const GetTotalEnergyConsumptionParam({
|
const GetTotalEnergyConsumptionParam({
|
||||||
this.monthDate,
|
this.startDate,
|
||||||
|
this.endDate,
|
||||||
this.spaceId,
|
this.spaceId,
|
||||||
this.communityId,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
Map<String, dynamic> toJson() {
|
Map<String, dynamic> toJson() {
|
||||||
return {
|
return {
|
||||||
'monthDate':
|
'startDate': startDate?.toIso8601String(),
|
||||||
'${monthDate?.year}-${monthDate?.month.toString().padLeft(2, '0')}',
|
'endDate': endDate?.toIso8601String(),
|
||||||
if (communityId == null || communityId!.isEmpty) 'spaceUuid': spaceId,
|
'spaceId': spaceId,
|
||||||
'communityUuid': communityId,
|
|
||||||
'groupByDevice': false,
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,19 +0,0 @@
|
|||||||
import 'package:syncrow_web/pages/analytics/models/occupacy.dart';
|
|
||||||
import 'package:syncrow_web/pages/analytics/params/get_occupancy_param.dart';
|
|
||||||
import 'package:syncrow_web/pages/analytics/services/occupacy/occupacy_service.dart';
|
|
||||||
|
|
||||||
class FakeOccupacyService implements OccupacyService {
|
|
||||||
@override
|
|
||||||
Future<List<Occupacy>> load(GetOccupancyParam param) async {
|
|
||||||
return await Future.delayed(
|
|
||||||
const Duration(seconds: 1),
|
|
||||||
() => List.generate(
|
|
||||||
30,
|
|
||||||
(index) => Occupacy(
|
|
||||||
date: DateTime.now().subtract(Duration(days: index)).toString(),
|
|
||||||
occupancy: ((index / 100)).toString(),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,6 +0,0 @@
|
|||||||
import 'package:syncrow_web/pages/analytics/models/occupacy.dart';
|
|
||||||
import 'package:syncrow_web/pages/analytics/params/get_occupancy_param.dart';
|
|
||||||
|
|
||||||
abstract interface class OccupacyService {
|
|
||||||
Future<List<Occupacy>> load(GetOccupancyParam param);
|
|
||||||
}
|
|
@ -1,25 +0,0 @@
|
|||||||
import 'package:syncrow_web/pages/analytics/models/occupancy_heat_map_model.dart';
|
|
||||||
import 'package:syncrow_web/pages/analytics/params/get_occupancy_heat_map_param.dart';
|
|
||||||
import 'package:syncrow_web/pages/analytics/services/occupancy_heat_map/occupancy_heat_map_service.dart';
|
|
||||||
|
|
||||||
class FakeOccupancyHeatMapService implements OccupancyHeatMapService {
|
|
||||||
@override
|
|
||||||
Future<List<OccupancyHeatMapModel>> load(GetOccupancyHeatMapParam param) {
|
|
||||||
return Future.delayed(const Duration(milliseconds: 200), () {
|
|
||||||
final now = DateTime.now();
|
|
||||||
final startOfYear = DateTime(now.year, 1, 1);
|
|
||||||
final endOfYear = DateTime(now.year, 12, 31);
|
|
||||||
final daysInYear = endOfYear.difference(startOfYear).inDays + 1;
|
|
||||||
|
|
||||||
final List<OccupancyHeatMapModel> data = List.generate(
|
|
||||||
daysInYear,
|
|
||||||
(index) => OccupancyHeatMapModel(
|
|
||||||
date: startOfYear.add(Duration(days: index)),
|
|
||||||
occupancy: ((index + 1) * 10) % 100,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
return data;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,6 +0,0 @@
|
|||||||
import 'package:syncrow_web/pages/analytics/models/occupancy_heat_map_model.dart';
|
|
||||||
import 'package:syncrow_web/pages/analytics/params/get_occupancy_heat_map_param.dart';
|
|
||||||
|
|
||||||
abstract interface class OccupancyHeatMapService {
|
|
||||||
Future<List<OccupancyHeatMapModel>> load(GetOccupancyHeatMapParam param);
|
|
||||||
}
|
|
@ -23,7 +23,7 @@ class FirebaseRealtimeDeviceService implements RealtimeDeviceService {
|
|||||||
|
|
||||||
return Status(
|
return Status(
|
||||||
code: status['code']?.toString() ?? '',
|
code: status['code']?.toString() ?? '',
|
||||||
value: status['value']?.toString() ?? '',
|
value: num.tryParse(status['value']?.toString() ?? '0'),
|
||||||
);
|
);
|
||||||
}).toList();
|
}).toList();
|
||||||
});
|
});
|
||||||
|
@ -0,0 +1,19 @@
|
|||||||
|
import 'package:syncrow_web/pages/analytics/models/energy_data_model.dart';
|
||||||
|
import 'package:syncrow_web/pages/analytics/params/get_total_energy_consumption_param.dart';
|
||||||
|
import 'package:syncrow_web/pages/analytics/services/total_energy_consumption/total_energy_consumption_service.dart';
|
||||||
|
|
||||||
|
class FakeTotalEnergyConsumptionService implements TotalEnergyConsumptionService {
|
||||||
|
@override
|
||||||
|
Future<List<EnergyDataModel>> load(
|
||||||
|
GetTotalEnergyConsumptionParam param,
|
||||||
|
) {
|
||||||
|
return Future.value(
|
||||||
|
List.generate(30, (index) {
|
||||||
|
return EnergyDataModel(
|
||||||
|
date: DateTime(2025, 1, index + 1),
|
||||||
|
value: 20000 + (index * 1000) % 5000,
|
||||||
|
);
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -14,37 +14,20 @@ class RemoteTotalEnergyConsumptionService implements TotalEnergyConsumptionServi
|
|||||||
) async {
|
) async {
|
||||||
try {
|
try {
|
||||||
final response = await _httpService.get(
|
final response = await _httpService.get(
|
||||||
path: '/power-clamp/historical',
|
path: 'endpoint',
|
||||||
showServerMessage: true,
|
showServerMessage: true,
|
||||||
queryParameters: param.toJson(),
|
expectedResponseModel: (data) {
|
||||||
expectedResponseModel: _TotalEnergyConsumptionResponseMapper.map,
|
final json = data as Map<String, dynamic>? ?? {};
|
||||||
|
final mappedData = json['data'] as List<dynamic>? ?? [];
|
||||||
|
return mappedData.map((e) {
|
||||||
|
final jsonData = e as Map<String, dynamic>;
|
||||||
|
return EnergyDataModel.fromJson(jsonData);
|
||||||
|
}).toList();
|
||||||
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
return response;
|
return response;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
throw Exception('Failed to load total energy consumption: $e');
|
throw Exception('Failed to load total energy consumption: $e');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract final class _TotalEnergyConsumptionResponseMapper {
|
|
||||||
const _TotalEnergyConsumptionResponseMapper._();
|
|
||||||
|
|
||||||
static List<EnergyDataModel> map(dynamic data) {
|
|
||||||
final json = data as Map<String, dynamic>? ?? {};
|
|
||||||
final dailyData = json['data'] as List<dynamic>? ?? [];
|
|
||||||
|
|
||||||
return dailyData.map((dayData) {
|
|
||||||
final date = dayData['date'] as String;
|
|
||||||
final energyValue = double.tryParse(
|
|
||||||
dayData['total_energy_consumed_kw'] as String? ?? '0',
|
|
||||||
) ??
|
|
||||||
0.0;
|
|
||||||
|
|
||||||
return EnergyDataModel(
|
|
||||||
date: DateTime.parse(date),
|
|
||||||
value: energyValue,
|
|
||||||
);
|
|
||||||
}).toList();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -5,7 +5,6 @@ class ChartsLoadingWidget extends StatelessWidget {
|
|||||||
required this.isLoading,
|
required this.isLoading,
|
||||||
super.key,
|
super.key,
|
||||||
});
|
});
|
||||||
|
|
||||||
final bool isLoading;
|
final bool isLoading;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -143,19 +143,6 @@ class HomeBloc extends Bloc<HomeEvent, HomeState> {
|
|||||||
color: ColorsManager.primaryColor,
|
color: ColorsManager.primaryColor,
|
||||||
),
|
),
|
||||||
|
|
||||||
HomeItemModel(
|
|
||||||
title: 'Syncrow Analytics',
|
|
||||||
icon: Assets.devicesIcon,
|
|
||||||
active: true,
|
|
||||||
onPress: (context) {
|
|
||||||
context.read<SpaceTreeBloc>().add(ClearCachedData());
|
|
||||||
BlocProvider.of<RoutineBloc>(context)
|
|
||||||
.add(const TriggerSwitchTabsEvent(isRoutineTab: false));
|
|
||||||
context.go(RoutesConst.analytics);
|
|
||||||
},
|
|
||||||
color: ColorsManager.primaryColor,
|
|
||||||
),
|
|
||||||
|
|
||||||
// HomeItemModel(
|
// HomeItemModel(
|
||||||
// title: 'Move in',
|
// title: 'Move in',
|
||||||
// icon: Assets.moveinIcon,
|
// icon: Assets.moveinIcon,
|
||||||
|
@ -19,7 +19,6 @@ import 'package:syncrow_web/utils/color_manager.dart';
|
|||||||
import 'package:syncrow_web/utils/constants/assets.dart';
|
import 'package:syncrow_web/utils/constants/assets.dart';
|
||||||
import 'package:syncrow_web/utils/extension/build_context_x.dart';
|
import 'package:syncrow_web/utils/extension/build_context_x.dart';
|
||||||
import 'package:syncrow_web/utils/style.dart';
|
import 'package:syncrow_web/utils/style.dart';
|
||||||
|
|
||||||
class UsersPage extends StatelessWidget {
|
class UsersPage extends StatelessWidget {
|
||||||
UsersPage({super.key});
|
UsersPage({super.key});
|
||||||
|
|
||||||
|
@ -1,10 +1,9 @@
|
|||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
|
||||||
import 'package:bloc/bloc.dart';
|
|
||||||
import 'package:equatable/equatable.dart';
|
import 'package:equatable/equatable.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:syncrow_web/pages/common/bloc/project_manager.dart';
|
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
|
import 'package:syncrow_web/pages/common/bloc/project_manager.dart';
|
||||||
import 'package:syncrow_web/pages/device_managment/all_devices/models/devices_model.dart';
|
import 'package:syncrow_web/pages/device_managment/all_devices/models/devices_model.dart';
|
||||||
import 'package:syncrow_web/pages/routines/bloc/automation_scene_trigger_bloc/automation_status_update.dart';
|
import 'package:syncrow_web/pages/routines/bloc/automation_scene_trigger_bloc/automation_status_update.dart';
|
||||||
import 'package:syncrow_web/pages/routines/bloc/create_routine_bloc/create_routine_bloc.dart';
|
import 'package:syncrow_web/pages/routines/bloc/create_routine_bloc/create_routine_bloc.dart';
|
||||||
@ -90,8 +89,8 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
|
|||||||
final updatedIfItems = List<Map<String, dynamic>>.from(state.ifItems);
|
final updatedIfItems = List<Map<String, dynamic>>.from(state.ifItems);
|
||||||
|
|
||||||
// Find the index of the item in teh current itemsList
|
// Find the index of the item in teh current itemsList
|
||||||
int index =
|
int index = updatedIfItems
|
||||||
updatedIfItems.indexWhere((map) => map['uniqueCustomId'] == event.item['uniqueCustomId']);
|
.indexWhere((map) => map['uniqueCustomId'] == event.item['uniqueCustomId']);
|
||||||
// Replace the map if the index is valid
|
// Replace the map if the index is valid
|
||||||
if (index != -1) {
|
if (index != -1) {
|
||||||
updatedIfItems[index] = event.item;
|
updatedIfItems[index] = event.item;
|
||||||
@ -100,9 +99,11 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (event.isTabToRun) {
|
if (event.isTabToRun) {
|
||||||
emit(state.copyWith(ifItems: updatedIfItems, isTabToRun: true, isAutomation: false));
|
emit(state.copyWith(
|
||||||
|
ifItems: updatedIfItems, isTabToRun: true, isAutomation: false));
|
||||||
} else {
|
} else {
|
||||||
emit(state.copyWith(ifItems: updatedIfItems, isTabToRun: false, isAutomation: true));
|
emit(state.copyWith(
|
||||||
|
ifItems: updatedIfItems, isTabToRun: false, isAutomation: true));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -110,8 +111,8 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
|
|||||||
final currentItems = List<Map<String, dynamic>>.from(state.thenItems);
|
final currentItems = List<Map<String, dynamic>>.from(state.thenItems);
|
||||||
|
|
||||||
// Find the index of the item in teh current itemsList
|
// Find the index of the item in teh current itemsList
|
||||||
int index =
|
int index = currentItems
|
||||||
currentItems.indexWhere((map) => map['uniqueCustomId'] == event.item['uniqueCustomId']);
|
.indexWhere((map) => map['uniqueCustomId'] == event.item['uniqueCustomId']);
|
||||||
// Replace the map if the index is valid
|
// Replace the map if the index is valid
|
||||||
if (index != -1) {
|
if (index != -1) {
|
||||||
currentItems[index] = event.item;
|
currentItems[index] = event.item;
|
||||||
@ -122,7 +123,8 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
|
|||||||
emit(state.copyWith(thenItems: currentItems));
|
emit(state.copyWith(thenItems: currentItems));
|
||||||
}
|
}
|
||||||
|
|
||||||
void _onAddFunctionsToRoutine(AddFunctionToRoutine event, Emitter<RoutineState> emit) {
|
void _onAddFunctionsToRoutine(
|
||||||
|
AddFunctionToRoutine event, Emitter<RoutineState> emit) {
|
||||||
try {
|
try {
|
||||||
if (event.functions.isEmpty) return;
|
if (event.functions.isEmpty) return;
|
||||||
|
|
||||||
@ -172,17 +174,20 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
|
|||||||
BuildContext context = NavigationService.navigatorKey.currentContext!;
|
BuildContext context = NavigationService.navigatorKey.currentContext!;
|
||||||
var createRoutineBloc = context.read<CreateRoutineBloc>();
|
var createRoutineBloc = context.read<CreateRoutineBloc>();
|
||||||
final projectUuid = await ProjectManager.getProjectUUID() ?? '';
|
final projectUuid = await ProjectManager.getProjectUUID() ?? '';
|
||||||
if (createRoutineBloc.selectedSpaceId == '' && createRoutineBloc.selectedCommunityId == '') {
|
if (createRoutineBloc.selectedSpaceId == '' &&
|
||||||
|
createRoutineBloc.selectedCommunityId == '') {
|
||||||
var spaceBloc = context.read<SpaceTreeBloc>();
|
var spaceBloc = context.read<SpaceTreeBloc>();
|
||||||
for (var communityId in spaceBloc.state.selectedCommunities) {
|
for (var communityId in spaceBloc.state.selectedCommunities) {
|
||||||
List<String> spacesList = spaceBloc.state.selectedCommunityAndSpaces[communityId] ?? [];
|
List<String> spacesList =
|
||||||
|
spaceBloc.state.selectedCommunityAndSpaces[communityId] ?? [];
|
||||||
for (var spaceId in spacesList) {
|
for (var spaceId in spacesList) {
|
||||||
scenes.addAll(await SceneApi.getScenes(spaceId, communityId, projectUuid));
|
scenes
|
||||||
|
.addAll(await SceneApi.getScenes(spaceId, communityId, projectUuid));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
scenes.addAll(await SceneApi.getScenes(
|
scenes.addAll(await SceneApi.getScenes(createRoutineBloc.selectedSpaceId,
|
||||||
createRoutineBloc.selectedSpaceId, createRoutineBloc.selectedCommunityId, projectUuid));
|
createRoutineBloc.selectedCommunityId, projectUuid));
|
||||||
}
|
}
|
||||||
|
|
||||||
emit(state.copyWith(
|
emit(state.copyWith(
|
||||||
@ -199,7 +204,8 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _onLoadAutomation(LoadAutomation event, Emitter<RoutineState> emit) async {
|
Future<void> _onLoadAutomation(
|
||||||
|
LoadAutomation event, Emitter<RoutineState> emit) async {
|
||||||
emit(state.copyWith(isLoading: true, errorMessage: null));
|
emit(state.copyWith(isLoading: true, errorMessage: null));
|
||||||
List<ScenesModel> automations = [];
|
List<ScenesModel> automations = [];
|
||||||
final projectId = await ProjectManager.getProjectUUID() ?? '';
|
final projectId = await ProjectManager.getProjectUUID() ?? '';
|
||||||
@ -207,17 +213,22 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
|
|||||||
BuildContext context = NavigationService.navigatorKey.currentContext!;
|
BuildContext context = NavigationService.navigatorKey.currentContext!;
|
||||||
var createRoutineBloc = context.read<CreateRoutineBloc>();
|
var createRoutineBloc = context.read<CreateRoutineBloc>();
|
||||||
try {
|
try {
|
||||||
if (createRoutineBloc.selectedSpaceId == '' && createRoutineBloc.selectedCommunityId == '') {
|
if (createRoutineBloc.selectedSpaceId == '' &&
|
||||||
|
createRoutineBloc.selectedCommunityId == '') {
|
||||||
var spaceBloc = context.read<SpaceTreeBloc>();
|
var spaceBloc = context.read<SpaceTreeBloc>();
|
||||||
for (var communityId in spaceBloc.state.selectedCommunities) {
|
for (var communityId in spaceBloc.state.selectedCommunities) {
|
||||||
List<String> spacesList = spaceBloc.state.selectedCommunityAndSpaces[communityId] ?? [];
|
List<String> spacesList =
|
||||||
|
spaceBloc.state.selectedCommunityAndSpaces[communityId] ?? [];
|
||||||
for (var spaceId in spacesList) {
|
for (var spaceId in spacesList) {
|
||||||
automations.addAll(await SceneApi.getAutomation(spaceId, communityId, projectId));
|
automations.addAll(
|
||||||
|
await SceneApi.getAutomation(spaceId, communityId, projectId));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
automations.addAll(await SceneApi.getAutomation(
|
automations.addAll(await SceneApi.getAutomation(
|
||||||
createRoutineBloc.selectedSpaceId, createRoutineBloc.selectedCommunityId, projectId));
|
createRoutineBloc.selectedSpaceId,
|
||||||
|
createRoutineBloc.selectedCommunityId,
|
||||||
|
projectId));
|
||||||
}
|
}
|
||||||
emit(state.copyWith(
|
emit(state.copyWith(
|
||||||
automations: automations,
|
automations: automations,
|
||||||
@ -233,14 +244,16 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
FutureOr<void> _onSearchRoutines(SearchRoutines event, Emitter<RoutineState> emit) async {
|
FutureOr<void> _onSearchRoutines(
|
||||||
|
SearchRoutines event, Emitter<RoutineState> emit) async {
|
||||||
emit(state.copyWith(isLoading: true, errorMessage: null));
|
emit(state.copyWith(isLoading: true, errorMessage: null));
|
||||||
await Future.delayed(const Duration(seconds: 1));
|
await Future.delayed(const Duration(seconds: 1));
|
||||||
emit(state.copyWith(isLoading: false, errorMessage: null));
|
emit(state.copyWith(isLoading: false, errorMessage: null));
|
||||||
emit(state.copyWith(searchText: event.query));
|
emit(state.copyWith(searchText: event.query));
|
||||||
}
|
}
|
||||||
|
|
||||||
FutureOr<void> _onAddSelectedIcon(AddSelectedIcon event, Emitter<RoutineState> emit) {
|
FutureOr<void> _onAddSelectedIcon(
|
||||||
|
AddSelectedIcon event, Emitter<RoutineState> emit) {
|
||||||
emit(state.copyWith(selectedIcon: event.icon));
|
emit(state.copyWith(selectedIcon: event.icon));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -254,7 +267,8 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
|
|||||||
return actions.last['deviceId'] == 'delay';
|
return actions.last['deviceId'] == 'delay';
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _onCreateScene(CreateSceneEvent event, Emitter<RoutineState> emit) async {
|
Future<void> _onCreateScene(
|
||||||
|
CreateSceneEvent event, Emitter<RoutineState> emit) async {
|
||||||
try {
|
try {
|
||||||
// Check if first action is delay
|
// Check if first action is delay
|
||||||
// if (_isFirstActionDelay(state.thenItems)) {
|
// if (_isFirstActionDelay(state.thenItems)) {
|
||||||
@ -343,7 +357,8 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _onCreateAutomation(CreateAutomationEvent event, Emitter<RoutineState> emit) async {
|
Future<void> _onCreateAutomation(
|
||||||
|
CreateAutomationEvent event, Emitter<RoutineState> emit) async {
|
||||||
try {
|
try {
|
||||||
final projectUuid = await ProjectManager.getProjectUUID() ?? '';
|
final projectUuid = await ProjectManager.getProjectUUID() ?? '';
|
||||||
if (state.routineName == null || state.routineName!.isEmpty) {
|
if (state.routineName == null || state.routineName!.isEmpty) {
|
||||||
@ -456,7 +471,8 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
|
|||||||
actions: actions,
|
actions: actions,
|
||||||
);
|
);
|
||||||
|
|
||||||
final result = await SceneApi.createAutomation(createAutomationModel, projectUuid);
|
final result =
|
||||||
|
await SceneApi.createAutomation(createAutomationModel, projectUuid);
|
||||||
if (result['success']) {
|
if (result['success']) {
|
||||||
add(ResetRoutineState());
|
add(ResetRoutineState());
|
||||||
add(const LoadAutomation());
|
add(const LoadAutomation());
|
||||||
@ -477,17 +493,21 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
FutureOr<void> _onRemoveDragCard(RemoveDragCard event, Emitter<RoutineState> emit) {
|
FutureOr<void> _onRemoveDragCard(
|
||||||
|
RemoveDragCard event, Emitter<RoutineState> emit) {
|
||||||
if (event.isFromThen) {
|
if (event.isFromThen) {
|
||||||
final thenItems = List<Map<String, dynamic>>.from(state.thenItems);
|
final thenItems = List<Map<String, dynamic>>.from(state.thenItems);
|
||||||
final selectedFunctions = Map<String, List<DeviceFunctionData>>.from(state.selectedFunctions);
|
final selectedFunctions =
|
||||||
|
Map<String, List<DeviceFunctionData>>.from(state.selectedFunctions);
|
||||||
|
|
||||||
thenItems.removeAt(event.index);
|
thenItems.removeAt(event.index);
|
||||||
selectedFunctions.remove(event.key);
|
selectedFunctions.remove(event.key);
|
||||||
emit(state.copyWith(thenItems: thenItems, selectedFunctions: selectedFunctions));
|
emit(state.copyWith(
|
||||||
|
thenItems: thenItems, selectedFunctions: selectedFunctions));
|
||||||
} else {
|
} else {
|
||||||
final ifItems = List<Map<String, dynamic>>.from(state.ifItems);
|
final ifItems = List<Map<String, dynamic>>.from(state.ifItems);
|
||||||
final selectedFunctions = Map<String, List<DeviceFunctionData>>.from(state.selectedFunctions);
|
final selectedFunctions =
|
||||||
|
Map<String, List<DeviceFunctionData>>.from(state.selectedFunctions);
|
||||||
|
|
||||||
ifItems.removeAt(event.index);
|
ifItems.removeAt(event.index);
|
||||||
selectedFunctions.remove(event.key);
|
selectedFunctions.remove(event.key);
|
||||||
@ -510,11 +530,13 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
|
|||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
FutureOr<void> _onEffectiveTimeEvent(EffectiveTimePeriodEvent event, Emitter<RoutineState> emit) {
|
FutureOr<void> _onEffectiveTimeEvent(
|
||||||
|
EffectiveTimePeriodEvent event, Emitter<RoutineState> emit) {
|
||||||
emit(state.copyWith(effectiveTime: event.effectiveTime));
|
emit(state.copyWith(effectiveTime: event.effectiveTime));
|
||||||
}
|
}
|
||||||
|
|
||||||
FutureOr<void> _onSetRoutineName(SetRoutineName event, Emitter<RoutineState> emit) {
|
FutureOr<void> _onSetRoutineName(
|
||||||
|
SetRoutineName event, Emitter<RoutineState> emit) {
|
||||||
emit(state.copyWith(
|
emit(state.copyWith(
|
||||||
routineName: event.name,
|
routineName: event.name,
|
||||||
));
|
));
|
||||||
@ -641,7 +663,8 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
|
|||||||
// return (thenItems, ifItems, currentFunctions);
|
// return (thenItems, ifItems, currentFunctions);
|
||||||
// }
|
// }
|
||||||
|
|
||||||
Future<void> _onGetSceneDetails(GetSceneDetails event, Emitter<RoutineState> emit) async {
|
Future<void> _onGetSceneDetails(
|
||||||
|
GetSceneDetails event, Emitter<RoutineState> emit) async {
|
||||||
try {
|
try {
|
||||||
emit(state.copyWith(
|
emit(state.copyWith(
|
||||||
isLoading: true,
|
isLoading: true,
|
||||||
@ -690,9 +713,10 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
|
|||||||
deviceCards[deviceId] = {
|
deviceCards[deviceId] = {
|
||||||
'entityId': action.entityId,
|
'entityId': action.entityId,
|
||||||
'deviceId': action.actionExecutor == 'delay' ? 'delay' : action.entityId,
|
'deviceId': action.actionExecutor == 'delay' ? 'delay' : action.entityId,
|
||||||
'uniqueCustomId': action.type == 'automation' || action.actionExecutor == 'delay'
|
'uniqueCustomId':
|
||||||
? action.entityId
|
action.type == 'automation' || action.actionExecutor == 'delay'
|
||||||
: const Uuid().v4(),
|
? action.entityId
|
||||||
|
: const Uuid().v4(),
|
||||||
'title': action.actionExecutor == 'delay'
|
'title': action.actionExecutor == 'delay'
|
||||||
? 'Delay'
|
? 'Delay'
|
||||||
: action.type == 'automation'
|
: action.type == 'automation'
|
||||||
@ -732,7 +756,8 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
// emit(state.copyWith(automationActionExecutor: action.actionExecutor));
|
// emit(state.copyWith(automationActionExecutor: action.actionExecutor));
|
||||||
} else if (action.executorProperty != null && action.actionExecutor != 'delay') {
|
} else if (action.executorProperty != null &&
|
||||||
|
action.actionExecutor != 'delay') {
|
||||||
final functions = matchingDevice?.functions ?? [];
|
final functions = matchingDevice?.functions ?? [];
|
||||||
final functionCode = action.executorProperty?.functionCode;
|
final functionCode = action.executorProperty?.functionCode;
|
||||||
for (DeviceFunction function in functions) {
|
for (DeviceFunction function in functions) {
|
||||||
@ -798,7 +823,8 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
FutureOr<void> _onResetRoutineState(ResetRoutineState event, Emitter<RoutineState> emit) {
|
FutureOr<void> _onResetRoutineState(
|
||||||
|
ResetRoutineState event, Emitter<RoutineState> emit) {
|
||||||
emit(state.copyWith(
|
emit(state.copyWith(
|
||||||
ifItems: [],
|
ifItems: [],
|
||||||
thenItems: [],
|
thenItems: [],
|
||||||
@ -831,7 +857,8 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
|
|||||||
var spaceBloc = context.read<SpaceTreeBloc>();
|
var spaceBloc = context.read<SpaceTreeBloc>();
|
||||||
if (state.isTabToRun) {
|
if (state.isTabToRun) {
|
||||||
await SceneApi.deleteScene(
|
await SceneApi.deleteScene(
|
||||||
unitUuid: spaceBloc.state.selectedSpaces[0], sceneId: state.sceneId ?? '');
|
unitUuid: spaceBloc.state.selectedSpaces[0],
|
||||||
|
sceneId: state.sceneId ?? '');
|
||||||
} else {
|
} else {
|
||||||
await SceneApi.deleteAutomation(
|
await SceneApi.deleteAutomation(
|
||||||
unitUuid: spaceBloc.state.selectedSpaces[0],
|
unitUuid: spaceBloc.state.selectedSpaces[0],
|
||||||
@ -876,7 +903,8 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
|
|||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
|
|
||||||
FutureOr<void> _fetchDevices(FetchDevicesInRoutine event, Emitter<RoutineState> emit) async {
|
FutureOr<void> _fetchDevices(
|
||||||
|
FetchDevicesInRoutine event, Emitter<RoutineState> emit) async {
|
||||||
emit(state.copyWith(isLoading: true));
|
emit(state.copyWith(isLoading: true));
|
||||||
try {
|
try {
|
||||||
final projectUuid = await ProjectManager.getProjectUUID() ?? '';
|
final projectUuid = await ProjectManager.getProjectUUID() ?? '';
|
||||||
@ -885,17 +913,21 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
|
|||||||
var createRoutineBloc = context.read<CreateRoutineBloc>();
|
var createRoutineBloc = context.read<CreateRoutineBloc>();
|
||||||
var spaceBloc = context.read<SpaceTreeBloc>();
|
var spaceBloc = context.read<SpaceTreeBloc>();
|
||||||
|
|
||||||
if (createRoutineBloc.selectedSpaceId == '' && createRoutineBloc.selectedCommunityId == '') {
|
if (createRoutineBloc.selectedSpaceId == '' &&
|
||||||
|
createRoutineBloc.selectedCommunityId == '') {
|
||||||
for (var communityId in spaceBloc.state.selectedCommunities) {
|
for (var communityId in spaceBloc.state.selectedCommunities) {
|
||||||
List<String> spacesList = spaceBloc.state.selectedCommunityAndSpaces[communityId] ?? [];
|
List<String> spacesList =
|
||||||
|
spaceBloc.state.selectedCommunityAndSpaces[communityId] ?? [];
|
||||||
for (var spaceId in spacesList) {
|
for (var spaceId in spacesList) {
|
||||||
devices.addAll(
|
devices.addAll(await DevicesManagementApi()
|
||||||
await DevicesManagementApi().fetchDevices(communityId, spaceId, projectUuid));
|
.fetchDevices(communityId, spaceId, projectUuid));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
devices.addAll(await DevicesManagementApi().fetchDevices(
|
devices.addAll(await DevicesManagementApi().fetchDevices(
|
||||||
createRoutineBloc.selectedCommunityId, createRoutineBloc.selectedSpaceId, projectUuid));
|
createRoutineBloc.selectedCommunityId,
|
||||||
|
createRoutineBloc.selectedSpaceId,
|
||||||
|
projectUuid));
|
||||||
}
|
}
|
||||||
|
|
||||||
emit(state.copyWith(isLoading: false, devices: devices));
|
emit(state.copyWith(isLoading: false, devices: devices));
|
||||||
@ -904,7 +936,8 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
FutureOr<void> _onUpdateScene(UpdateScene event, Emitter<RoutineState> emit) async {
|
FutureOr<void> _onUpdateScene(
|
||||||
|
UpdateScene event, Emitter<RoutineState> emit) async {
|
||||||
try {
|
try {
|
||||||
// Check if first action is delay
|
// Check if first action is delay
|
||||||
// if (_isFirstActionDelay(state.thenItems)) {
|
// if (_isFirstActionDelay(state.thenItems)) {
|
||||||
@ -971,7 +1004,8 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
|
|||||||
actions: actions,
|
actions: actions,
|
||||||
);
|
);
|
||||||
|
|
||||||
final result = await SceneApi.updateScene(createSceneModel, state.sceneId ?? '');
|
final result =
|
||||||
|
await SceneApi.updateScene(createSceneModel, state.sceneId ?? '');
|
||||||
if (result['success']) {
|
if (result['success']) {
|
||||||
add(ResetRoutineState());
|
add(ResetRoutineState());
|
||||||
add(const LoadScenes());
|
add(const LoadScenes());
|
||||||
@ -990,7 +1024,8 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
FutureOr<void> _onUpdateAutomation(UpdateAutomation event, Emitter<RoutineState> emit) async {
|
FutureOr<void> _onUpdateAutomation(
|
||||||
|
UpdateAutomation event, Emitter<RoutineState> emit) async {
|
||||||
try {
|
try {
|
||||||
if (state.routineName == null || state.routineName!.isEmpty) {
|
if (state.routineName == null || state.routineName!.isEmpty) {
|
||||||
emit(state.copyWith(
|
emit(state.copyWith(
|
||||||
@ -1106,8 +1141,8 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
|
|||||||
|
|
||||||
if (result['success']) {
|
if (result['success']) {
|
||||||
add(ResetRoutineState());
|
add(ResetRoutineState());
|
||||||
add(LoadAutomation());
|
add(const LoadAutomation());
|
||||||
add(LoadScenes());
|
add(const LoadScenes());
|
||||||
} else {
|
} else {
|
||||||
emit(state.copyWith(
|
emit(state.copyWith(
|
||||||
isLoading: false,
|
isLoading: false,
|
||||||
@ -1291,10 +1326,13 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
final ifItems = deviceIfCards.values.where((card) => card['type'] == 'condition').toList();
|
final ifItems =
|
||||||
|
deviceIfCards.values.where((card) => card['type'] == 'condition').toList();
|
||||||
final thenItems = deviceThenCards.values
|
final thenItems = deviceThenCards.values
|
||||||
.where((card) =>
|
.where((card) =>
|
||||||
card['type'] == 'action' || card['type'] == 'automation' || card['type'] == 'scene')
|
card['type'] == 'action' ||
|
||||||
|
card['type'] == 'automation' ||
|
||||||
|
card['type'] == 'scene')
|
||||||
.toList();
|
.toList();
|
||||||
|
|
||||||
emit(state.copyWith(
|
emit(state.copyWith(
|
||||||
@ -1316,7 +1354,8 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _onSceneTrigger(SceneTrigger event, Emitter<RoutineState> emit) async {
|
Future<void> _onSceneTrigger(
|
||||||
|
SceneTrigger event, Emitter<RoutineState> emit) async {
|
||||||
emit(state.copyWith(loadingSceneId: event.sceneId));
|
emit(state.copyWith(loadingSceneId: event.sceneId));
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@ -1361,21 +1400,24 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
|
|||||||
event.automationStatusUpdate.spaceUuid, event.communityId, projectId);
|
event.automationStatusUpdate.spaceUuid, event.communityId, projectId);
|
||||||
|
|
||||||
// Remove from loading set safely
|
// Remove from loading set safely
|
||||||
final updatedLoadingIds = {...state.loadingAutomationIds!}..remove(event.automationId);
|
final updatedLoadingIds = {...state.loadingAutomationIds!}
|
||||||
|
..remove(event.automationId);
|
||||||
|
|
||||||
emit(state.copyWith(
|
emit(state.copyWith(
|
||||||
automations: updatedAutomations,
|
automations: updatedAutomations,
|
||||||
loadingAutomationIds: updatedLoadingIds,
|
loadingAutomationIds: updatedLoadingIds,
|
||||||
));
|
));
|
||||||
} else {
|
} else {
|
||||||
final updatedLoadingIds = {...state.loadingAutomationIds!}..remove(event.automationId);
|
final updatedLoadingIds = {...state.loadingAutomationIds!}
|
||||||
|
..remove(event.automationId);
|
||||||
emit(state.copyWith(
|
emit(state.copyWith(
|
||||||
loadingAutomationIds: updatedLoadingIds,
|
loadingAutomationIds: updatedLoadingIds,
|
||||||
errorMessage: 'Update failed',
|
errorMessage: 'Update failed',
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
final updatedLoadingIds = {...state.loadingAutomationIds!}..remove(event.automationId);
|
final updatedLoadingIds = {...state.loadingAutomationIds!}
|
||||||
|
..remove(event.automationId);
|
||||||
emit(state.copyWith(
|
emit(state.copyWith(
|
||||||
loadingAutomationIds: updatedLoadingIds,
|
loadingAutomationIds: updatedLoadingIds,
|
||||||
errorMessage: 'Update error: ${e.toString()}',
|
errorMessage: 'Update error: ${e.toString()}',
|
||||||
|
@ -34,9 +34,8 @@ class UserPermissionApi {
|
|||||||
path: ApiEndpoints.roleTypes,
|
path: ApiEndpoints.roleTypes,
|
||||||
showServerMessage: true,
|
showServerMessage: true,
|
||||||
expectedResponseModel: (json) {
|
expectedResponseModel: (json) {
|
||||||
final List<RoleTypeModel> fetchedRoles = (json['data'] as List)
|
final List<RoleTypeModel> fetchedRoles =
|
||||||
.map((item) => RoleTypeModel.fromJson(item))
|
(json['data'] as List).map((item) => RoleTypeModel.fromJson(item)).toList();
|
||||||
.toList();
|
|
||||||
return fetchedRoles;
|
return fetchedRoles;
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
@ -48,9 +47,7 @@ class UserPermissionApi {
|
|||||||
path: ApiEndpoints.permission.replaceAll("roleUuid", roleUuid),
|
path: ApiEndpoints.permission.replaceAll("roleUuid", roleUuid),
|
||||||
showServerMessage: true,
|
showServerMessage: true,
|
||||||
expectedResponseModel: (json) {
|
expectedResponseModel: (json) {
|
||||||
return (json as List)
|
return (json as List).map((data) => PermissionOption.fromJson(data)).toList();
|
||||||
.map((data) => PermissionOption.fromJson(data))
|
|
||||||
.toList();
|
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
return response ?? [];
|
return response ?? [];
|
||||||
@ -195,14 +192,10 @@ class UserPermissionApi {
|
|||||||
|
|
||||||
Future<bool> changeUserStatusById(userUuid, status, String projectUuid) async {
|
Future<bool> changeUserStatusById(userUuid, status, String projectUuid) async {
|
||||||
try {
|
try {
|
||||||
Map<String, dynamic> bodya = {
|
Map<String, dynamic> bodya = {"disable": status, "projectUuid": projectUuid};
|
||||||
"disable": status,
|
|
||||||
"projectUuid": projectUuid
|
|
||||||
};
|
|
||||||
|
|
||||||
final response = await _httpService.put(
|
final response = await _httpService.put(
|
||||||
path: ApiEndpoints.changeUserStatus
|
path: ApiEndpoints.changeUserStatus.replaceAll("{invitedUserUuid}", userUuid),
|
||||||
.replaceAll("{invitedUserUuid}", userUuid),
|
|
||||||
body: bodya,
|
body: bodya,
|
||||||
expectedResponseModel: (json) {
|
expectedResponseModel: (json) {
|
||||||
return json['success'];
|
return json['success'];
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import 'package:flutter_dotenv/flutter_dotenv.dart';
|
import 'package:flutter_dotenv/flutter_dotenv.dart';
|
||||||
|
|
||||||
abstract class ApiEndpoints {
|
abstract class ApiEndpoints {
|
||||||
static const String projectUuid = "0e62577c-06fa-41b9-8a92-99a21fbaf51c";
|
static const String projectUuid = "bcda711e-9fc2-4168-a05e-171b4026d1ff";
|
||||||
static String baseUrl = dotenv.env['BASE_URL'] ?? '';
|
static String baseUrl = dotenv.env['BASE_URL'] ?? '';
|
||||||
static const String signUp = '/authentication/user/signup';
|
static const String signUp = '/authentication/user/signup';
|
||||||
static const String login = '/authentication/user/login';
|
static const String login = '/authentication/user/login';
|
||||||
|
@ -1,3 +1,3 @@
|
|||||||
class TempConst {
|
class TempConst {
|
||||||
static const projectId = '0e62577c-06fa-41b9-8a92-99a21fbaf51c';
|
static const projectId = 'bcda711e-9fc2-4168-a05e-171b4026d1ff';
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user