mirror of
https://github.com/SyncrowIOT/web.git
synced 2025-11-27 16:24:55 +00:00
Compare commits
44 Commits
fix-schedu
...
SP-1708-FE
| Author | SHA1 | Date | |
|---|---|---|---|
| 95cded4bf5 | |||
| 757a96ed9f | |||
| b857736e10 | |||
| 1fccd51440 | |||
| c07ddb0ccd | |||
| 58e99f95b2 | |||
| 227df6fe3d | |||
| 9451ec0cc4 | |||
| fc797c2646 | |||
| 318e1d9af7 | |||
| d47dc349bc | |||
| c221c8499f | |||
| 71cf4b9feb | |||
| c43cf9347f | |||
| 9990b1805e | |||
| 009b7c0316 | |||
| 779c0fe916 | |||
| e448eabda6 | |||
| 9dfb3ed369 | |||
| 63353af38b | |||
| 68b6c9b18c | |||
| fa6ee9a0af | |||
| 3601b02bc3 | |||
| fdd0526c78 | |||
| bdeec7d325 | |||
| 50ff17a0c1 | |||
| 87c2e3261d | |||
| 62a6f9c993 | |||
| f7e4d6ff07 | |||
| 95907661d2 | |||
| 9c9b7d99dc | |||
| c07bae5cbc | |||
| 8cb6c13cd5 | |||
| 949c27938a | |||
| 4c582b865d | |||
| d7467adeda | |||
| 15ee79688d | |||
| e5e88385e9 | |||
| 62d5bbce7e | |||
| 05d784ec11 | |||
| 9ebf474a60 | |||
| db05331e9a | |||
| 44c88fb1c4 | |||
| 2f5ad03431 |
8
assets/icons/empty_barred_chart.svg
Normal file
8
assets/icons/empty_barred_chart.svg
Normal file
@ -0,0 +1,8 @@
|
||||
<svg width="175" height="134" viewBox="0 0 175 134" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<line x1="0.5" y1="2.18557e-08" x2="0.499994" y2="132.759" stroke="#B9C0C5"/>
|
||||
<line x1="175" y1="133.259" x2="-4.37114e-08" y2="133.259" stroke="#B9C0C5"/>
|
||||
<rect x="16.0922" y="66.3794" width="28.1609" height="66.3793" fill="#C7CDD1"/>
|
||||
<rect x="54.3105" y="24.1379" width="28.1609" height="108.621" fill="#ABB4BA"/>
|
||||
<rect x="92.5288" y="78.4484" width="28.1609" height="54.3103" fill="#C7CDD1"/>
|
||||
<rect x="130.747" y="48.2759" width="28.1609" height="84.4828" fill="#ABB4BA"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 583 B |
5
assets/icons/empty_energy_management_chart.svg
Normal file
5
assets/icons/empty_energy_management_chart.svg
Normal file
@ -0,0 +1,5 @@
|
||||
<svg width="175" height="134" viewBox="0 0 175 134" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<line x1="0.5" y1="3.05394e-05" x2="0.499994" y2="132.759" stroke="#B9C0C5"/>
|
||||
<line x1="175" y1="133.259" x2="-4.37114e-08" y2="133.259" stroke="#B9C0C5"/>
|
||||
<path d="M1.5 132.5C13 132.5 6.58852 66.5 29.5 66.5C46.5 66.5 46.1214 24.9349 68.5 24.5C94.2816 23.999 80.7136 78.5065 106.5 78.5C125.715 78.4952 131.5 48.5 145.5 48.5C159.5 48.5 156.5 96 171.5 96" stroke="#ABB4BA" stroke-width="2" stroke-linecap="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 520 B |
7
assets/icons/empty_energy_management_per_device.svg
Normal file
7
assets/icons/empty_energy_management_per_device.svg
Normal file
@ -0,0 +1,7 @@
|
||||
<svg width="175" height="134" viewBox="0 0 175 134" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<line x1="0.5" y1="2.18557e-08" x2="0.499994" y2="132.759" stroke="#B9C0C5"/>
|
||||
<line x1="175" y1="133.259" x2="-4.37114e-08" y2="133.259" stroke="#B9C0C5"/>
|
||||
<path d="M1.5 132.5C13 132.5 6.58852 66.5 29.5 66.5C46.5 66.5 46.1214 24.9348 68.5 24.5C94.2816 23.999 80.7136 78.5064 106.5 78.5C125.715 78.4951 131.5 48.5 145.5 48.5C159.5 48.5 156.5 95.9999 171.5 95.9999" stroke="#ABB4BA" stroke-width="1.5" stroke-linecap="round" stroke-dasharray="4 4"/>
|
||||
<path d="M1.5 132.5C13 132.5 6.58852 78.4999 29.5 78.4999C46.5 78.4999 45.6214 44.9349 68 44.5C93.7816 43.999 80.7136 27.0065 106.5 27C125.715 26.9952 131.5 63.5 145.5 63.5C159.5 63.5 156.5 113.5 171.5 113.5" stroke="#C7CDD1" stroke-width="1.5" stroke-linecap="round" stroke-dasharray="4 4"/>
|
||||
<path d="M1.5 132.5C13 132.5 6.58852 85.9999 29.5 85.9999C46.5 85.9999 45.6214 11.9348 68 11.4999C93.7816 10.9989 80.7136 43.5064 106.5 43.4999C125.715 43.4951 131.5 35.4999 145.5 35.4999C159.5 35.4999 156.5 105.5 171.5 105.5" stroke="#D5D5D5" stroke-width="1.5" stroke-linecap="round" stroke-dasharray="4 4"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.1 KiB |
99
assets/icons/empty_heatmap.svg
Normal file
99
assets/icons/empty_heatmap.svg
Normal file
@ -0,0 +1,99 @@
|
||||
<svg width="181" height="121" viewBox="0 0 181 121" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<line x1="15.5" y1="-2.52181e-08" x2="15.5" y2="120" stroke="#B9B9B9"/>
|
||||
<line x1="45.5" y1="-2.52181e-08" x2="45.5" y2="120" stroke="#B9B9B9"/>
|
||||
<line x1="75.5" y1="-2.52181e-08" x2="75.5" y2="120" stroke="#B9B9B9"/>
|
||||
<line x1="105.5" y1="-2.52181e-08" x2="105.5" y2="120" stroke="#B9B9B9"/>
|
||||
<line x1="135.5" y1="-2.52181e-08" x2="135.5" y2="120" stroke="#B9B9B9"/>
|
||||
<line x1="165.5" y1="-2.52181e-08" x2="165.5" y2="120" stroke="#B9B9B9"/>
|
||||
<line x1="30.5" y1="-2.52181e-08" x2="30.5" y2="120" stroke="#B9B9B9"/>
|
||||
<line x1="60.5" y1="-2.52181e-08" x2="60.5" y2="120" stroke="#B9B9B9"/>
|
||||
<line x1="90.5" y1="-2.52181e-08" x2="90.5" y2="120" stroke="#B9B9B9"/>
|
||||
<line x1="120.5" y1="-2.52181e-08" x2="120.5" y2="120" stroke="#B9B9B9"/>
|
||||
<line x1="150.5" y1="-2.52181e-08" x2="150.5" y2="120" stroke="#B9B9B9"/>
|
||||
<line x1="180.5" y1="-2.52181e-08" x2="180.5" y2="120" stroke="#B9B9B9"/>
|
||||
<line x1="181" y1="120.5" x2="-4.52101e-08" y2="120.5" stroke="#B9B9B9"/>
|
||||
<line x1="181" y1="60.5" x2="-4.52101e-08" y2="60.5" stroke="#B9B9B9"/>
|
||||
<line x1="181" y1="90.5" x2="-4.52101e-08" y2="90.5" stroke="#B9B9B9"/>
|
||||
<line x1="181" y1="30.5" x2="-4.52101e-08" y2="30.5" stroke="#B9B9B9"/>
|
||||
<line x1="181" y1="105.5" x2="-4.52101e-08" y2="105.5" stroke="#B9B9B9"/>
|
||||
<line x1="181" y1="45.5" x2="-4.52101e-08" y2="45.5" stroke="#B9B9B9"/>
|
||||
<line x1="181" y1="75.5" x2="-4.52101e-08" y2="75.5" stroke="#B9B9B9"/>
|
||||
<line x1="181" y1="15.5" x2="-4.52101e-08" y2="15.5" stroke="#B9B9B9"/>
|
||||
<rect x="16" y="16" width="14" height="14" fill="#F2F2F2"/>
|
||||
<rect x="31" y="16" width="14" height="14" fill="#F2F2F2"/>
|
||||
<rect x="46" y="16" width="14" height="14" fill="#F2F2F2"/>
|
||||
<rect x="61" y="16" width="14" height="14" fill="#F2F2F2"/>
|
||||
<rect x="76" y="16" width="14" height="14" fill="#F2F2F2"/>
|
||||
<rect x="91" y="16" width="14" height="14" fill="#F2F2F2"/>
|
||||
<rect x="106" y="16" width="14" height="14" fill="#F2F2F2"/>
|
||||
<rect x="121" y="16" width="14" height="14" fill="#F2F2F2"/>
|
||||
<rect x="136" y="16" width="14" height="14" fill="#F2F2F2"/>
|
||||
<rect x="151" y="16" width="14" height="14" fill="#F2F2F2"/>
|
||||
<rect x="166" y="16" width="14" height="14" fill="#F2F2F2"/>
|
||||
<rect x="16" y="31" width="14" height="14" fill="#F2F2F2"/>
|
||||
<rect x="31" y="31" width="14" height="14" fill="#D5D5D5"/>
|
||||
<rect x="46" y="31" width="14" height="14" fill="#D5D5D5"/>
|
||||
<rect x="61" y="31" width="14" height="14" fill="#D5D5D5"/>
|
||||
<rect x="76" y="31" width="14" height="14" fill="#D5D5D5"/>
|
||||
<rect x="91" y="31" width="14" height="14" fill="#D5D5D5"/>
|
||||
<rect x="106" y="31" width="14" height="14" fill="#D5D5D5"/>
|
||||
<rect x="121" y="31" width="14" height="14" fill="#D5D5D5"/>
|
||||
<rect x="136" y="31" width="14" height="14" fill="#D5D5D5"/>
|
||||
<rect x="151" y="31" width="14" height="14" fill="#D5D5D5"/>
|
||||
<rect x="166" y="31" width="14" height="14" fill="#F2F2F2"/>
|
||||
<rect x="16" y="46" width="14" height="14" fill="#F2F2F2"/>
|
||||
<rect x="31" y="46" width="14" height="14" fill="#D5D5D5"/>
|
||||
<rect x="46" y="46" width="14" height="14" fill="#D5D5D5"/>
|
||||
<rect x="61" y="46" width="14" height="14" fill="#D5D5D5"/>
|
||||
<rect x="76" y="46" width="14" height="14" fill="#D5D5D5"/>
|
||||
<rect x="91" y="46" width="14" height="14" fill="#D5D5D5"/>
|
||||
<rect x="106" y="46" width="14" height="14" fill="#D5D5D5"/>
|
||||
<rect x="121" y="46" width="14" height="14" fill="#D5D5D5"/>
|
||||
<rect x="136" y="46" width="14" height="14" fill="#D5D5D5"/>
|
||||
<rect x="151" y="46" width="14" height="14" fill="#D5D5D5"/>
|
||||
<rect x="166" y="46" width="14" height="14" fill="#F2F2F2"/>
|
||||
<rect x="16" y="61" width="14" height="14" fill="#F2F2F2"/>
|
||||
<rect x="31" y="61" width="14" height="14" fill="#D5D5D5"/>
|
||||
<rect x="46" y="61" width="14" height="14" fill="#D5D5D5"/>
|
||||
<rect x="61" y="61" width="14" height="14" fill="#B1B1B1"/>
|
||||
<rect x="76" y="61" width="14" height="14" fill="#B1B1B1"/>
|
||||
<rect x="91" y="61" width="14" height="14" fill="#B1B1B1"/>
|
||||
<rect x="106" y="61" width="14" height="14" fill="#B1B1B1"/>
|
||||
<rect x="121" y="61" width="14" height="14" fill="#B1B1B1"/>
|
||||
<rect x="136" y="61" width="14" height="14" fill="#D5D5D5"/>
|
||||
<rect x="151" y="61" width="14" height="14" fill="#D5D5D5"/>
|
||||
<rect x="166" y="61" width="14" height="14" fill="#F2F2F2"/>
|
||||
<rect x="16" y="76" width="14" height="14" fill="#F2F2F2"/>
|
||||
<rect x="31" y="76" width="14" height="14" fill="#D5D5D5"/>
|
||||
<rect x="46" y="76" width="14" height="14" fill="#D5D5D5"/>
|
||||
<rect x="61" y="76" width="14" height="14" fill="#D5D5D5"/>
|
||||
<rect x="76" y="76" width="14" height="14" fill="#D5D5D5"/>
|
||||
<rect x="91" y="76" width="14" height="14" fill="#D5D5D5"/>
|
||||
<rect x="106" y="76" width="14" height="14" fill="#D5D5D5"/>
|
||||
<rect x="121" y="76" width="14" height="14" fill="#D5D5D5"/>
|
||||
<rect x="136" y="76" width="14" height="14" fill="#D5D5D5"/>
|
||||
<rect x="151" y="76" width="14" height="14" fill="#D5D5D5"/>
|
||||
<rect x="166" y="76" width="14" height="14" fill="#F2F2F2"/>
|
||||
<rect x="16" y="91" width="14" height="14" fill="#F2F2F2"/>
|
||||
<rect x="31" y="91" width="14" height="14" fill="#D5D5D5"/>
|
||||
<rect x="46" y="91" width="14" height="14" fill="#D5D5D5"/>
|
||||
<rect x="61" y="91" width="14" height="14" fill="#D5D5D5"/>
|
||||
<rect x="76" y="91" width="14" height="14" fill="#D5D5D5"/>
|
||||
<rect x="91" y="91" width="14" height="14" fill="#D5D5D5"/>
|
||||
<rect x="106" y="91" width="14" height="14" fill="#D5D5D5"/>
|
||||
<rect x="121" y="91" width="14" height="14" fill="#D5D5D5"/>
|
||||
<rect x="136" y="91" width="14" height="14" fill="#D5D5D5"/>
|
||||
<rect x="151" y="91" width="14" height="14" fill="#D5D5D5"/>
|
||||
<rect x="166" y="91" width="14" height="14" fill="#F2F2F2"/>
|
||||
<rect x="16" y="106" width="14" height="14" fill="#F2F2F2"/>
|
||||
<rect x="31" y="106" width="14" height="14" fill="#F2F2F2"/>
|
||||
<rect x="46" y="106" width="14" height="14" fill="#F2F2F2"/>
|
||||
<rect x="61" y="106" width="14" height="14" fill="#F2F2F2"/>
|
||||
<rect x="76" y="106" width="14" height="14" fill="#F2F2F2"/>
|
||||
<rect x="91" y="106" width="14" height="14" fill="#F2F2F2"/>
|
||||
<rect x="106" y="106" width="14" height="14" fill="#F2F2F2"/>
|
||||
<rect x="121" y="106" width="14" height="14" fill="#F2F2F2"/>
|
||||
<rect x="136" y="106" width="14" height="14" fill="#F2F2F2"/>
|
||||
<rect x="151" y="106" width="14" height="14" fill="#F2F2F2"/>
|
||||
<rect x="166" y="106" width="14" height="14" fill="#F2F2F2"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 6.1 KiB |
7
assets/icons/empty_range_of_aqi.svg
Normal file
7
assets/icons/empty_range_of_aqi.svg
Normal file
@ -0,0 +1,7 @@
|
||||
<svg width="175" height="134" viewBox="0 0 175 134" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<line x1="0.5" y1="2.18557e-08" x2="0.499994" y2="132.759" stroke="#B9C0C5"/>
|
||||
<line x1="175" y1="133.259" x2="-4.37114e-08" y2="133.259" stroke="#B9C0C5"/>
|
||||
<path d="M1.5 95.9999C13 95.9999 6.58853 66.4999 29.5 66.4999C46.5 66.4999 46.1214 34.9348 68.5 34.4999C94.2816 33.9989 80.7136 65.0065 106.5 65C125.715 64.9952 131.5 50.5 145.5 50.5C159.5 50.5 156.5 70.5 171.5 70.5" stroke="#C7CDD1" stroke-width="2" stroke-linecap="round"/>
|
||||
<path d="M1.5 106C13 106 6.58853 76.4999 29.5 76.4999C46.5 76.4999 46.1214 44.9348 68.5 44.4999C94.2816 43.9989 80.7136 75.0065 106.5 75C125.715 74.9952 131.5 60.5 145.5 60.5C159.5 60.5 156.5 80.5 171.5 80.5" stroke="#F2F2F2" stroke-width="2" stroke-linecap="round"/>
|
||||
<path d="M1.5 116C13 116 6.58853 86.4999 29.5 86.4999C46.5 86.4999 46.1214 54.9348 68.5 54.4999C94.2816 53.9989 80.7136 85.0065 106.5 85C125.715 84.9952 131.5 70.5 145.5 70.5C159.5 70.5 156.5 90.5 171.5 90.5" stroke="#ABB4BA" stroke-width="2" stroke-linecap="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.0 KiB |
@ -20,6 +20,7 @@ class AqiDistributionChart extends StatelessWidget {
|
||||
return BarChart(
|
||||
BarChartData(
|
||||
maxY: 100.1,
|
||||
alignment: BarChartAlignment.start,
|
||||
gridData: EnergyManagementChartsHelper.gridData(
|
||||
horizontalInterval: 20,
|
||||
),
|
||||
|
||||
@ -3,7 +3,9 @@ import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:syncrow_web/pages/analytics/modules/air_quality/blocs/air_quality_distribution/air_quality_distribution_bloc.dart';
|
||||
import 'package:syncrow_web/pages/analytics/modules/air_quality/widgets/aqi_distribution_chart.dart';
|
||||
import 'package:syncrow_web/pages/analytics/modules/air_quality/widgets/aqi_distribution_chart_title.dart';
|
||||
import 'package:syncrow_web/pages/analytics/widgets/analytics_chart_empty_state_widget.dart';
|
||||
import 'package:syncrow_web/pages/analytics/widgets/analytics_error_widget.dart';
|
||||
import 'package:syncrow_web/utils/constants/assets.dart';
|
||||
import 'package:syncrow_web/utils/style.dart';
|
||||
|
||||
class AqiDistributionChartBox extends StatelessWidget {
|
||||
@ -32,8 +34,20 @@ class AqiDistributionChartBox extends StatelessWidget {
|
||||
const SizedBox(height: 10),
|
||||
const Divider(),
|
||||
const SizedBox(height: 20),
|
||||
Expanded(
|
||||
child: AqiDistributionChart(chartData: state.chartData),
|
||||
Visibility(
|
||||
visible: state.chartData.isNotEmpty,
|
||||
replacement: AnalyticsChartEmptyStateWidget(
|
||||
isLoading: state.status == AirQualityDistributionStatus.loading,
|
||||
isError: state.status == AirQualityDistributionStatus.failure,
|
||||
isInitial: state.status == AirQualityDistributionStatus.initial,
|
||||
errorMessage: state.errorMessage,
|
||||
iconPath: Assets.emptyBarredChart,
|
||||
),
|
||||
child: Expanded(
|
||||
child: AqiDistributionChart(
|
||||
chartData: state.chartData,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
|
||||
@ -3,7 +3,9 @@ import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:syncrow_web/pages/analytics/modules/air_quality/blocs/range_of_aqi/range_of_aqi_bloc.dart';
|
||||
import 'package:syncrow_web/pages/analytics/modules/air_quality/widgets/range_of_aqi_chart.dart';
|
||||
import 'package:syncrow_web/pages/analytics/modules/air_quality/widgets/range_of_aqi_chart_title.dart';
|
||||
import 'package:syncrow_web/pages/analytics/widgets/analytics_chart_empty_state_widget.dart';
|
||||
import 'package:syncrow_web/pages/analytics/widgets/analytics_error_widget.dart';
|
||||
import 'package:syncrow_web/utils/constants/assets.dart';
|
||||
import 'package:syncrow_web/utils/style.dart';
|
||||
|
||||
class RangeOfAqiChartBox extends StatelessWidget {
|
||||
@ -32,10 +34,20 @@ class RangeOfAqiChartBox extends StatelessWidget {
|
||||
const SizedBox(height: 10),
|
||||
const Divider(),
|
||||
const SizedBox(height: 20),
|
||||
Expanded(
|
||||
child: RangeOfAqiChart(
|
||||
chartData: state.filteredRangeOfAqi,
|
||||
selectedAqiType: state.selectedAqiType,
|
||||
Visibility(
|
||||
visible: state.filteredRangeOfAqi.isNotEmpty,
|
||||
replacement: AnalyticsChartEmptyStateWidget(
|
||||
isLoading: state.status == RangeOfAqiStatus.loading,
|
||||
isError: state.status == RangeOfAqiStatus.failure,
|
||||
isInitial: state.status == RangeOfAqiStatus.initial,
|
||||
errorMessage: state.errorMessage,
|
||||
iconPath: Assets.emptyRangeOfAqi,
|
||||
),
|
||||
child: Expanded(
|
||||
child: RangeOfAqiChart(
|
||||
chartData: state.filteredRangeOfAqi,
|
||||
selectedAqiType: state.selectedAqiType,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
|
||||
@ -38,7 +38,7 @@ class AnalyticsEnergyManagementView extends StatelessWidget {
|
||||
return SingleChildScrollView(
|
||||
child: Container(
|
||||
padding: _padding,
|
||||
height: MediaQuery.sizeOf(context).height * 1,
|
||||
height: MediaQuery.sizeOf(context).height * 1.05,
|
||||
child: const Column(
|
||||
children: [
|
||||
Expanded(
|
||||
|
||||
@ -5,8 +5,10 @@ import 'package:syncrow_web/pages/analytics/modules/energy_management/blocs/ener
|
||||
import 'package:syncrow_web/pages/analytics/modules/energy_management/widgets/chart_title.dart';
|
||||
import 'package:syncrow_web/pages/analytics/modules/energy_management/widgets/energy_consumption_per_device_chart.dart';
|
||||
import 'package:syncrow_web/pages/analytics/modules/energy_management/widgets/energy_consumption_per_device_devices_list.dart';
|
||||
import 'package:syncrow_web/pages/analytics/widgets/analytics_chart_empty_state_widget.dart';
|
||||
import 'package:syncrow_web/pages/analytics/widgets/analytics_error_widget.dart';
|
||||
import 'package:syncrow_web/pages/analytics/widgets/charts_loading_widget.dart';
|
||||
import 'package:syncrow_web/utils/constants/assets.dart';
|
||||
import 'package:syncrow_web/utils/style.dart';
|
||||
|
||||
class EnergyConsumptionPerDeviceChartBox extends StatelessWidget {
|
||||
@ -54,8 +56,24 @@ class EnergyConsumptionPerDeviceChartBox extends StatelessWidget {
|
||||
const SizedBox(height: 20),
|
||||
const Divider(height: 0),
|
||||
const SizedBox(height: 20),
|
||||
Expanded(
|
||||
child: EnergyConsumptionPerDeviceChart(chartData: state.chartData),
|
||||
Visibility(
|
||||
visible: state.chartData.isNotEmpty &&
|
||||
state.chartData
|
||||
.every((e) => e.energy.every((e) => e.value != 0)),
|
||||
replacement: AnalyticsChartEmptyStateWidget(
|
||||
isLoading:
|
||||
state.status == EnergyConsumptionPerDeviceStatus.loading,
|
||||
isError: state.status == EnergyConsumptionPerDeviceStatus.failure,
|
||||
isInitial:
|
||||
state.status == EnergyConsumptionPerDeviceStatus.initial,
|
||||
errorMessage: state.errorMessage,
|
||||
iconPath: Assets.emptyEnergyManagementPerDevice,
|
||||
),
|
||||
child: Expanded(
|
||||
child: EnergyConsumptionPerDeviceChart(
|
||||
chartData: state.chartData,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
|
||||
@ -3,8 +3,10 @@ import 'package:flutter_bloc/flutter_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/widgets/chart_title.dart';
|
||||
import 'package:syncrow_web/pages/analytics/modules/energy_management/widgets/total_energy_consumption_chart.dart';
|
||||
import 'package:syncrow_web/pages/analytics/widgets/analytics_chart_empty_state_widget.dart';
|
||||
import 'package:syncrow_web/pages/analytics/widgets/analytics_error_widget.dart';
|
||||
import 'package:syncrow_web/pages/analytics/widgets/charts_loading_widget.dart';
|
||||
import 'package:syncrow_web/utils/constants/assets.dart';
|
||||
import 'package:syncrow_web/utils/style.dart';
|
||||
|
||||
class TotalEnergyConsumptionChartBox extends StatelessWidget {
|
||||
@ -41,7 +43,18 @@ class TotalEnergyConsumptionChartBox extends StatelessWidget {
|
||||
const SizedBox(height: 20),
|
||||
const Divider(),
|
||||
const SizedBox(height: 20),
|
||||
TotalEnergyConsumptionChart(chartData: state.chartData),
|
||||
Visibility(
|
||||
visible: state.chartData.isNotEmpty &&
|
||||
state.chartData.every((e) => e.value != 0),
|
||||
replacement: AnalyticsChartEmptyStateWidget(
|
||||
isLoading: state.status == TotalEnergyConsumptionStatus.loading,
|
||||
isError: state.status == TotalEnergyConsumptionStatus.failure,
|
||||
isInitial: state.status == TotalEnergyConsumptionStatus.initial,
|
||||
errorMessage: state.errorMessage,
|
||||
iconPath: Assets.emptyEnergyManagementChart,
|
||||
),
|
||||
child: TotalEnergyConsumptionChart(chartData: state.chartData),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
||||
@ -18,6 +18,7 @@ class OccupancyChart extends StatelessWidget {
|
||||
return BarChart(
|
||||
BarChartData(
|
||||
maxY: 100.001,
|
||||
alignment: BarChartAlignment.start,
|
||||
gridData: EnergyManagementChartsHelper.gridData().copyWith(
|
||||
checkToShowHorizontalLine: (value) => true,
|
||||
horizontalInterval: 20,
|
||||
|
||||
@ -6,8 +6,10 @@ import 'package:syncrow_web/pages/analytics/modules/energy_management/widgets/ch
|
||||
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_chart_empty_state_widget.dart';
|
||||
import 'package:syncrow_web/pages/analytics/widgets/analytics_error_widget.dart';
|
||||
import 'package:syncrow_web/pages/space_tree/bloc/space_tree_bloc.dart';
|
||||
import 'package:syncrow_web/utils/constants/assets.dart';
|
||||
import 'package:syncrow_web/utils/style.dart';
|
||||
|
||||
class OccupancyChartBox extends StatelessWidget {
|
||||
@ -67,7 +69,24 @@ class OccupancyChartBox extends StatelessWidget {
|
||||
const SizedBox(height: 20),
|
||||
const Divider(),
|
||||
const SizedBox(height: 20),
|
||||
Expanded(child: OccupancyChart(chartData: state.chartData)),
|
||||
Visibility(
|
||||
visible: state.chartData.isNotEmpty &&
|
||||
state.chartData.every(
|
||||
(e) => e.occupancy.isNotEmpty,
|
||||
),
|
||||
replacement: AnalyticsChartEmptyStateWidget(
|
||||
isLoading: state.status == OccupancyStatus.loading,
|
||||
isError: state.status == OccupancyStatus.failure,
|
||||
isInitial: state.status == OccupancyStatus.initial,
|
||||
errorMessage: state.errorMessage,
|
||||
iconPath: Assets.emptyBarredChart,
|
||||
),
|
||||
child: Expanded(
|
||||
child: OccupancyChart(
|
||||
chartData: state.chartData,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
|
||||
@ -6,8 +6,10 @@ import 'package:syncrow_web/pages/analytics/modules/energy_management/widgets/ch
|
||||
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_chart_empty_state_widget.dart';
|
||||
import 'package:syncrow_web/pages/analytics/widgets/analytics_error_widget.dart';
|
||||
import 'package:syncrow_web/pages/space_tree/bloc/space_tree_bloc.dart';
|
||||
import 'package:syncrow_web/utils/constants/assets.dart';
|
||||
import 'package:syncrow_web/utils/style.dart';
|
||||
|
||||
class OccupancyHeatMapBox extends StatelessWidget {
|
||||
@ -68,16 +70,29 @@ class OccupancyHeatMapBox extends StatelessWidget {
|
||||
const SizedBox(height: 20),
|
||||
const Divider(),
|
||||
const SizedBox(height: 20),
|
||||
Expanded(
|
||||
child: OccupancyHeatMap(
|
||||
selectedDate:
|
||||
context.watch<AnalyticsDatePickerBloc>().state.yearlyDate,
|
||||
heatMapData: state.heatMapData.asMap().map(
|
||||
(_, value) => MapEntry(
|
||||
value.eventDate,
|
||||
value.countTotalPresenceDetected,
|
||||
Visibility(
|
||||
visible: state.heatMapData.isNotEmpty &&
|
||||
state.heatMapData.every(
|
||||
(e) => e.countTotalPresenceDetected != 0,
|
||||
),
|
||||
replacement: AnalyticsChartEmptyStateWidget(
|
||||
isLoading: state.status == OccupancyHeatMapStatus.loading,
|
||||
isError: state.status == OccupancyHeatMapStatus.failure,
|
||||
isInitial: state.status == OccupancyHeatMapStatus.initial,
|
||||
errorMessage: state.errorMessage,
|
||||
iconPath: Assets.emptyHeatmap,
|
||||
),
|
||||
child: Expanded(
|
||||
child: OccupancyHeatMap(
|
||||
selectedDate:
|
||||
context.watch<AnalyticsDatePickerBloc>().state.yearlyDate,
|
||||
heatMapData: state.heatMapData.asMap().map(
|
||||
(_, value) => MapEntry(
|
||||
value.eventDate,
|
||||
value.countTotalPresenceDetected,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
|
||||
@ -0,0 +1,68 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_svg/svg.dart';
|
||||
import 'package:syncrow_web/common/widgets/app_loading_indicator.dart';
|
||||
import 'package:syncrow_web/utils/color_manager.dart';
|
||||
import 'package:syncrow_web/utils/extension/build_context_x.dart';
|
||||
|
||||
class AnalyticsChartEmptyStateWidget extends StatelessWidget {
|
||||
const AnalyticsChartEmptyStateWidget({
|
||||
required this.iconPath,
|
||||
this.isLoading = false,
|
||||
this.isError = false,
|
||||
this.isInitial = false,
|
||||
this.errorMessage,
|
||||
this.noDataMessage = 'No data to display',
|
||||
this.initialMessage = 'Please select a space to see data',
|
||||
super.key,
|
||||
});
|
||||
|
||||
final bool isLoading;
|
||||
final bool isError;
|
||||
final bool isInitial;
|
||||
final String? errorMessage;
|
||||
final String noDataMessage;
|
||||
final String initialMessage;
|
||||
final String iconPath;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Expanded(
|
||||
child: _buildWidgetBasedOnState(context),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildWidgetBasedOnState(BuildContext context) {
|
||||
final widgetsMap = {
|
||||
isLoading: const AppLoadingIndicator(),
|
||||
isInitial: _buildState(context, initialMessage),
|
||||
isError: _buildState(context, errorMessage ?? 'Something went wrong'),
|
||||
};
|
||||
|
||||
return widgetsMap[true] ?? _buildState(context, noDataMessage);
|
||||
}
|
||||
|
||||
Widget _buildState(BuildContext context, String message) {
|
||||
return Center(
|
||||
child: Column(
|
||||
spacing: 16,
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
const SizedBox(height: 16),
|
||||
Expanded(child: SvgPicture.asset(iconPath, fit: BoxFit.contain)),
|
||||
SelectableText(
|
||||
message,
|
||||
style: isError
|
||||
? context.textTheme.bodyMedium?.copyWith(
|
||||
color: ColorsManager.red,
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w700,
|
||||
)
|
||||
: null,
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -62,9 +62,9 @@ class CurtainModuleItems extends StatelessWidget with HelperResponsiveLayout {
|
||||
BlocProvider.of<CurtainModuleBloc>(context),
|
||||
child: BuildScheduleView(
|
||||
deviceUuid: deviceId,
|
||||
category: 'timer',
|
||||
category: 'Timer',
|
||||
code: 'control',
|
||||
countdownCode: 'timer',
|
||||
countdownCode: 'Timer',
|
||||
deviceType: 'CUR_2',
|
||||
),
|
||||
));
|
||||
|
||||
@ -105,7 +105,7 @@ class HomeBloc extends Bloc<HomeEvent, HomeState> {
|
||||
color: const Color(0xFF0026A2),
|
||||
),
|
||||
HomeItemModel(
|
||||
title: 'Devices Management',
|
||||
title: 'Device Management',
|
||||
icon: Assets.devicesIcon,
|
||||
active: true,
|
||||
onPress: (context) {
|
||||
|
||||
@ -455,7 +455,7 @@ class UsersBloc extends Bloc<UsersEvent, UsersState> {
|
||||
Future<void> checkEmail(
|
||||
CheckEmailEvent event, Emitter<UsersState> emit) async {
|
||||
emit(UsersLoadingState());
|
||||
String? res = await UserPermissionApi().checkEmail(
|
||||
String? res = await UserPermissionApi().checkEmail(
|
||||
emailController.text,
|
||||
);
|
||||
checkEmailValid = res!;
|
||||
|
||||
@ -34,7 +34,8 @@ class _AddNewUserDialogState extends State<AddNewUserDialog> {
|
||||
return Dialog(
|
||||
child: Container(
|
||||
decoration: const BoxDecoration(
|
||||
color: Colors.white, borderRadius: BorderRadius.all(Radius.circular(20))),
|
||||
color: Colors.white,
|
||||
borderRadius: BorderRadius.all(Radius.circular(20))),
|
||||
width: 900,
|
||||
child: Column(
|
||||
children: [
|
||||
@ -63,7 +64,8 @@ class _AddNewUserDialogState extends State<AddNewUserDialog> {
|
||||
children: [
|
||||
_buildStep1Indicator(1, "Basics", _blocRole),
|
||||
_buildStep2Indicator(2, "Spaces", _blocRole),
|
||||
_buildStep3Indicator(3, "Role & Permissions", _blocRole),
|
||||
_buildStep3Indicator(
|
||||
3, "Role & Permissions", _blocRole),
|
||||
],
|
||||
),
|
||||
),
|
||||
@ -105,18 +107,32 @@ class _AddNewUserDialogState extends State<AddNewUserDialog> {
|
||||
),
|
||||
InkWell(
|
||||
onTap: () {
|
||||
final isBasicsStep = currentStep == 1;
|
||||
|
||||
if (isBasicsStep) {
|
||||
// Validate the form first
|
||||
final isValid = _blocRole.formKey.currentState
|
||||
?.validate() ??
|
||||
false;
|
||||
|
||||
if (!isValid)
|
||||
return; // Stop if form is not valid
|
||||
}
|
||||
_blocRole.add(const CheckEmailEvent());
|
||||
|
||||
setState(() {
|
||||
if (currentStep < 3) {
|
||||
currentStep++;
|
||||
if (currentStep == 2) {
|
||||
_blocRole.add(const CheckStepStatus(isEditUser: false));
|
||||
_blocRole.add(const CheckStepStatus(
|
||||
isEditUser: false));
|
||||
} else if (currentStep == 3) {
|
||||
_blocRole.add(const CheckSpacesStepStatus());
|
||||
_blocRole
|
||||
.add(const CheckSpacesStepStatus());
|
||||
}
|
||||
} else {
|
||||
_blocRole.add(SendInviteUsers(context: context));
|
||||
_blocRole
|
||||
.add(SendInviteUsers(context: context));
|
||||
}
|
||||
});
|
||||
},
|
||||
@ -124,8 +140,11 @@ class _AddNewUserDialogState extends State<AddNewUserDialog> {
|
||||
currentStep < 3 ? "Next" : "Save",
|
||||
style: TextStyle(
|
||||
color: (_blocRole.isCompleteSpaces == false ||
|
||||
_blocRole.isCompleteBasics == false ||
|
||||
_blocRole.isCompleteRolePermissions == false) &&
|
||||
_blocRole.isCompleteBasics ==
|
||||
false ||
|
||||
_blocRole
|
||||
.isCompleteRolePermissions ==
|
||||
false) &&
|
||||
currentStep == 3
|
||||
? ColorsManager.grayColor
|
||||
: ColorsManager.secondaryColor),
|
||||
@ -143,7 +162,7 @@ class _AddNewUserDialogState extends State<AddNewUserDialog> {
|
||||
Widget _getFormContent() {
|
||||
switch (currentStep) {
|
||||
case 1:
|
||||
return const BasicsView(
|
||||
return BasicsView(
|
||||
userId: '',
|
||||
);
|
||||
case 2:
|
||||
@ -196,8 +215,12 @@ class _AddNewUserDialogState extends State<AddNewUserDialog> {
|
||||
label,
|
||||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
color: currentStep == step ? ColorsManager.blackColor : ColorsManager.greyColor,
|
||||
fontWeight: currentStep == step ? FontWeight.bold : FontWeight.normal,
|
||||
color: currentStep == step
|
||||
? ColorsManager.blackColor
|
||||
: ColorsManager.greyColor,
|
||||
fontWeight: currentStep == step
|
||||
? FontWeight.bold
|
||||
: FontWeight.normal,
|
||||
),
|
||||
),
|
||||
],
|
||||
@ -260,8 +283,12 @@ class _AddNewUserDialogState extends State<AddNewUserDialog> {
|
||||
label,
|
||||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
color: currentStep == step ? ColorsManager.blackColor : ColorsManager.greyColor,
|
||||
fontWeight: currentStep == step ? FontWeight.bold : FontWeight.normal,
|
||||
color: currentStep == step
|
||||
? ColorsManager.blackColor
|
||||
: ColorsManager.greyColor,
|
||||
fontWeight: currentStep == step
|
||||
? FontWeight.bold
|
||||
: FontWeight.normal,
|
||||
),
|
||||
),
|
||||
],
|
||||
@ -318,8 +345,12 @@ class _AddNewUserDialogState extends State<AddNewUserDialog> {
|
||||
label,
|
||||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
color: currentStep == step ? ColorsManager.blackColor : ColorsManager.greyColor,
|
||||
fontWeight: currentStep == step ? FontWeight.bold : FontWeight.normal,
|
||||
color: currentStep == step
|
||||
? ColorsManager.blackColor
|
||||
: ColorsManager.greyColor,
|
||||
fontWeight: currentStep == step
|
||||
? FontWeight.bold
|
||||
: FontWeight.normal,
|
||||
),
|
||||
),
|
||||
],
|
||||
|
||||
@ -1,9 +1,12 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:intl_phone_field/countries.dart';
|
||||
import 'package:intl_phone_field/country_picker_dialog.dart';
|
||||
import 'package:intl_phone_field/intl_phone_field.dart';
|
||||
import 'package:syncrow_web/pages/roles_and_permission/users_page/add_user_dialog/bloc/users_bloc.dart';
|
||||
import 'package:syncrow_web/pages/roles_and_permission/users_page/add_user_dialog/bloc/users_event.dart';
|
||||
import 'package:syncrow_web/pages/roles_and_permission/users_page/add_user_dialog/bloc/users_status.dart';
|
||||
import 'package:syncrow_web/utils/color_manager.dart';
|
||||
import 'package:syncrow_web/utils/extension/build_context_x.dart';
|
||||
@ -11,7 +14,9 @@ import 'package:syncrow_web/utils/style.dart';
|
||||
|
||||
class BasicsView extends StatelessWidget {
|
||||
final String? userId;
|
||||
const BasicsView({super.key, this.userId = ''});
|
||||
Timer? _debounce;
|
||||
|
||||
BasicsView({super.key, this.userId = ''});
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return BlocBuilder<UsersBloc, UsersState>(builder: (context, state) {
|
||||
@ -21,6 +26,7 @@ class BasicsView extends StatelessWidget {
|
||||
}
|
||||
return Form(
|
||||
key: _blocRole.formKey,
|
||||
autovalidateMode: AutovalidateMode.onUserInteraction,
|
||||
child: ListView(
|
||||
shrinkWrap: true,
|
||||
children: [
|
||||
@ -208,6 +214,14 @@ class BasicsView extends StatelessWidget {
|
||||
fontSize: 12,
|
||||
color: ColorsManager.textGray),
|
||||
),
|
||||
|
||||
onChanged: (value) {
|
||||
if (_debounce?.isActive ?? false) _debounce!.cancel();
|
||||
_debounce = Timer(const Duration(milliseconds: 800), () {
|
||||
_blocRole.add(const CheckEmailEvent());
|
||||
});
|
||||
},
|
||||
|
||||
validator: (value) {
|
||||
if (value == null || value.isEmpty) {
|
||||
return 'Enter Email Address';
|
||||
|
||||
@ -7,6 +7,8 @@ import 'package:syncrow_web/pages/space_management_v2/modules/communities/data/s
|
||||
import 'package:syncrow_web/pages/space_management_v2/modules/communities/domain/params/load_communities_param.dart';
|
||||
import 'package:syncrow_web/pages/space_management_v2/modules/communities/presentation/bloc/communities_bloc.dart';
|
||||
import 'package:syncrow_web/pages/space_management_v2/modules/communities/presentation/communities_tree_selection_bloc/communities_tree_selection_bloc.dart';
|
||||
import 'package:syncrow_web/pages/space_management_v2/modules/space_details/data/services/remote_space_details_service.dart';
|
||||
import 'package:syncrow_web/pages/space_management_v2/modules/space_details/presentation/bloc/space_details_bloc.dart';
|
||||
import 'package:syncrow_web/services/api/http_service.dart';
|
||||
import 'package:syncrow_web/utils/theme/responsive_text_theme.dart';
|
||||
import 'package:syncrow_web/web_layout/web_scaffold.dart';
|
||||
@ -26,6 +28,11 @@ class SpaceManagementPage extends StatelessWidget {
|
||||
)..add(const LoadCommunities(LoadCommunitiesParam())),
|
||||
),
|
||||
BlocProvider(create: (context) => CommunitiesTreeSelectionBloc()),
|
||||
BlocProvider(
|
||||
create: (context) => SpaceDetailsBloc(
|
||||
RemoteSpaceDetailsService(httpService: HTTPService()),
|
||||
),
|
||||
),
|
||||
],
|
||||
child: WebScaffold(
|
||||
appBarTitle: Text(
|
||||
|
||||
@ -0,0 +1,116 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:flutter_svg/svg.dart';
|
||||
import 'package:syncrow_web/pages/space_management_v2/main_module/widgets/community_structure_header_action_buttons.dart';
|
||||
import 'package:syncrow_web/pages/space_management_v2/modules/communities/presentation/communities_tree_selection_bloc/communities_tree_selection_bloc.dart';
|
||||
import 'package:syncrow_web/pages/space_management_v2/modules/create_community/presentation/create_community_dialog.dart';
|
||||
import 'package:syncrow_web/pages/space_management_v2/modules/space_details/presentation/helpers/space_details_dialog_helper.dart';
|
||||
import 'package:syncrow_web/utils/color_manager.dart';
|
||||
import 'package:syncrow_web/utils/constants/assets.dart';
|
||||
|
||||
class CommunityStructureHeader extends StatelessWidget {
|
||||
const CommunityStructureHeader({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final theme = Theme.of(context);
|
||||
final screenWidth = MediaQuery.of(context).size.width;
|
||||
return Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 16.0),
|
||||
decoration: BoxDecoration(
|
||||
color: ColorsManager.whiteColors,
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: ColorsManager.shadowBlackColor,
|
||||
blurRadius: 8,
|
||||
offset: const Offset(0, 4),
|
||||
),
|
||||
],
|
||||
),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Expanded(
|
||||
child: _buildCommunityInfo(context, theme, screenWidth),
|
||||
),
|
||||
const SizedBox(width: 16),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void _showCreateCommunityDialog(BuildContext context) {
|
||||
showDialog<void>(
|
||||
context: context,
|
||||
builder: (context) => CreateCommunityDialog(
|
||||
title: const Text('Edit Community'),
|
||||
onCreateCommunity: (community) {
|
||||
// TODO(FarisArmoush): Implement
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildCommunityInfo(
|
||||
BuildContext context, ThemeData theme, double screenWidth) {
|
||||
final selectedCommunity =
|
||||
context.watch<CommunitiesTreeSelectionBloc>().state.selectedCommunity;
|
||||
final selectedSpace =
|
||||
context.watch<CommunitiesTreeSelectionBloc>().state.selectedSpace;
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
'Community Structure',
|
||||
style: theme.textTheme.headlineLarge
|
||||
?.copyWith(color: ColorsManager.blackColor),
|
||||
),
|
||||
if (selectedCommunity != null)
|
||||
Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: Row(
|
||||
children: [
|
||||
Flexible(
|
||||
child: SelectableText(
|
||||
selectedCommunity.name,
|
||||
style: theme.textTheme.bodyLarge
|
||||
?.copyWith(color: ColorsManager.blackColor),
|
||||
maxLines: 1,
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 2),
|
||||
GestureDetector(
|
||||
onTap: () => _showCreateCommunityDialog(context),
|
||||
child: SvgPicture.asset(
|
||||
Assets.iconEdit,
|
||||
width: 16,
|
||||
height: 16,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
CommunityStructureHeaderActionButtons(
|
||||
onDelete: (space) {},
|
||||
onDuplicate: (space) {},
|
||||
onEdit: (space) {
|
||||
SpaceDetailsDialogHelper.showEdit(
|
||||
context,
|
||||
spaceModel: selectedSpace!,
|
||||
);
|
||||
},
|
||||
selectedSpace: selectedSpace,
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,46 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:syncrow_web/pages/space_management_v2/main_module/widgets/community_structure_header_button.dart';
|
||||
import 'package:syncrow_web/pages/space_management_v2/modules/communities/domain/models/space_model.dart';
|
||||
import 'package:syncrow_web/utils/constants/assets.dart';
|
||||
|
||||
class CommunityStructureHeaderActionButtons extends StatelessWidget {
|
||||
const CommunityStructureHeaderActionButtons({
|
||||
super.key,
|
||||
required this.onDelete,
|
||||
required this.selectedSpace,
|
||||
required this.onDuplicate,
|
||||
required this.onEdit,
|
||||
});
|
||||
|
||||
final void Function(SpaceModel space) onDelete;
|
||||
final void Function(SpaceModel space) onDuplicate;
|
||||
final void Function(SpaceModel space) onEdit;
|
||||
final SpaceModel? selectedSpace;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Wrap(
|
||||
alignment: WrapAlignment.end,
|
||||
spacing: 10,
|
||||
children: [
|
||||
if (selectedSpace != null) ...[
|
||||
CommunityStructureHeaderButton(
|
||||
label: 'Edit',
|
||||
svgAsset: Assets.editSpace,
|
||||
onPressed: () => onEdit(selectedSpace!),
|
||||
),
|
||||
CommunityStructureHeaderButton(
|
||||
label: 'Duplicate',
|
||||
svgAsset: Assets.duplicate,
|
||||
onPressed: () => onDuplicate(selectedSpace!),
|
||||
),
|
||||
CommunityStructureHeaderButton(
|
||||
label: 'Delete',
|
||||
svgAsset: Assets.spaceDelete,
|
||||
onPressed: () => onDelete(selectedSpace!),
|
||||
),
|
||||
],
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,61 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_svg/svg.dart';
|
||||
import 'package:syncrow_web/pages/common/buttons/default_button.dart';
|
||||
import 'package:syncrow_web/utils/color_manager.dart';
|
||||
import 'package:syncrow_web/utils/extension/build_context_x.dart';
|
||||
|
||||
class CommunityStructureHeaderButton extends StatelessWidget {
|
||||
const CommunityStructureHeaderButton({
|
||||
super.key,
|
||||
required this.label,
|
||||
required this.onPressed,
|
||||
this.svgAsset,
|
||||
});
|
||||
|
||||
final String label;
|
||||
final VoidCallback onPressed;
|
||||
final String? svgAsset;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
const double buttonHeight = 40;
|
||||
return ConstrainedBox(
|
||||
constraints: const BoxConstraints(
|
||||
maxWidth: 130,
|
||||
minHeight: buttonHeight,
|
||||
),
|
||||
child: DefaultButton(
|
||||
onPressed: onPressed,
|
||||
borderWidth: 2,
|
||||
backgroundColor: ColorsManager.textFieldGreyColor,
|
||||
foregroundColor: ColorsManager.blackColor,
|
||||
borderRadius: 12.0,
|
||||
padding: 2.0,
|
||||
height: buttonHeight,
|
||||
elevation: 0,
|
||||
borderColor: ColorsManager.lightGrayColor,
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
if (svgAsset != null)
|
||||
SvgPicture.asset(
|
||||
svgAsset!,
|
||||
width: 20,
|
||||
height: 20,
|
||||
),
|
||||
const SizedBox(width: 10),
|
||||
Flexible(
|
||||
child: Text(
|
||||
label,
|
||||
style: context.textTheme.bodySmall
|
||||
?.copyWith(color: ColorsManager.blackColor, fontSize: 14),
|
||||
overflow: TextOverflow.ellipsis,
|
||||
maxLines: 1,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -1,6 +1,7 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:syncrow_web/pages/space_management_v2/main_module/widgets/community_structure_canvas.dart';
|
||||
import 'package:syncrow_web/pages/space_management_v2/main_module/widgets/community_structure_header.dart';
|
||||
import 'package:syncrow_web/pages/space_management_v2/main_module/widgets/create_space_button.dart';
|
||||
import 'package:syncrow_web/pages/space_management_v2/modules/communities/presentation/communities_tree_selection_bloc/communities_tree_selection_bloc.dart';
|
||||
|
||||
@ -18,9 +19,17 @@ class SpaceManagementCommunityStructure extends StatelessWidget {
|
||||
replacement: const Row(
|
||||
children: [spacer, Expanded(child: CreateSpaceButton()), spacer],
|
||||
),
|
||||
child: CommunityStructureCanvas(
|
||||
community: selectedCommunity,
|
||||
selectedSpace: selectedSpace,
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
const CommunityStructureHeader(),
|
||||
Expanded(
|
||||
child: CommunityStructureCanvas(
|
||||
community: selectedCommunity,
|
||||
selectedSpace: selectedSpace,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@ -19,6 +19,16 @@ class SpaceModel extends Equatable {
|
||||
required this.parent,
|
||||
});
|
||||
|
||||
factory SpaceModel.empty() => const SpaceModel(
|
||||
uuid: '',
|
||||
createdAt: null,
|
||||
updatedAt: null,
|
||||
spaceName: '',
|
||||
icon: '',
|
||||
children: [],
|
||||
parent: null,
|
||||
);
|
||||
|
||||
factory SpaceModel.fromJson(Map<String, dynamic> json) {
|
||||
return SpaceModel(
|
||||
uuid: json['uuid'] as String? ?? '',
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
import 'package:dio/dio.dart';
|
||||
import 'package:syncrow_web/pages/common/bloc/project_manager.dart';
|
||||
import 'package:syncrow_web/pages/space_management_v2/modules/space_details/domain/models/space_details_model.dart';
|
||||
import 'package:syncrow_web/pages/space_management_v2/modules/space_details/domain/params/load_spaces_param.dart';
|
||||
import 'package:syncrow_web/pages/space_management_v2/modules/space_details/domain/params/load_space_details_param.dart';
|
||||
import 'package:syncrow_web/pages/space_management_v2/modules/space_details/domain/services/space_details_service.dart';
|
||||
import 'package:syncrow_web/services/api/api_exception.dart';
|
||||
import 'package:syncrow_web/services/api/http_service.dart';
|
||||
@ -15,12 +16,15 @@ class RemoteSpaceDetailsService implements SpaceDetailsService {
|
||||
static const _defaultErrorMessage = 'Failed to load space details';
|
||||
|
||||
@override
|
||||
Future<SpaceDetailsModel> getSpaceDetails(LoadSpacesParam param) async {
|
||||
Future<SpaceDetailsModel> getSpaceDetails(LoadSpaceDetailsParam param) async {
|
||||
try {
|
||||
final response = await _httpService.get(
|
||||
path: 'endpoint',
|
||||
path: await _makeEndpoint(param),
|
||||
expectedResponseModel: (data) {
|
||||
return SpaceDetailsModel.fromJson(data as Map<String, dynamic>);
|
||||
final response = data as Map<String, dynamic>;
|
||||
return SpaceDetailsModel.fromJson(
|
||||
response['data'] as Map<String, dynamic>,
|
||||
);
|
||||
},
|
||||
);
|
||||
return response;
|
||||
@ -37,4 +41,13 @@ class RemoteSpaceDetailsService implements SpaceDetailsService {
|
||||
throw APIException(formattedErrorMessage);
|
||||
}
|
||||
}
|
||||
|
||||
Future<String> _makeEndpoint(LoadSpaceDetailsParam param) async {
|
||||
final projectUuid = await ProjectManager.getProjectUUID();
|
||||
if (projectUuid == null || projectUuid.isEmpty) {
|
||||
throw APIException('Project UUID is not set');
|
||||
}
|
||||
|
||||
return '/projects/$projectUuid/communities/${param.communityUuid}/spaces/${param.spaceUuid}';
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
import 'package:equatable/equatable.dart';
|
||||
import 'package:syncrow_web/pages/space_management_v2/modules/products/domain/models/product.dart';
|
||||
import 'package:syncrow_web/pages/space_management_v2/modules/tags/domain/models/tag.dart';
|
||||
import 'package:syncrow_web/utils/constants/assets.dart';
|
||||
|
||||
class SpaceDetailsModel extends Equatable {
|
||||
final String uuid;
|
||||
@ -17,14 +18,21 @@ class SpaceDetailsModel extends Equatable {
|
||||
required this.subspaces,
|
||||
});
|
||||
|
||||
factory SpaceDetailsModel.empty() => const SpaceDetailsModel(
|
||||
uuid: '',
|
||||
spaceName: '',
|
||||
icon: Assets.villa,
|
||||
productAllocations: [],
|
||||
subspaces: [],
|
||||
);
|
||||
factory SpaceDetailsModel.fromJson(Map<String, dynamic> json) {
|
||||
return SpaceDetailsModel(
|
||||
uuid: json['uuid'] as String,
|
||||
spaceName: json['spaceName'] as String,
|
||||
icon: json['icon'] as String,
|
||||
productAllocations: (json['productAllocations'] as List)
|
||||
.map((e) => ProductAllocation.fromJson(e as Map<String, dynamic>))
|
||||
.toList(),
|
||||
.map((e) => ProductAllocation.fromJson(e as Map<String, dynamic>))
|
||||
.toList(),
|
||||
subspaces: (json['subspaces'] as List)
|
||||
.map((e) => Subspace.fromJson(e as Map<String, dynamic>))
|
||||
.toList(),
|
||||
@ -41,6 +49,22 @@ class SpaceDetailsModel extends Equatable {
|
||||
};
|
||||
}
|
||||
|
||||
SpaceDetailsModel copyWith({
|
||||
String? uuid,
|
||||
String? spaceName,
|
||||
String? icon,
|
||||
List<ProductAllocation>? productAllocations,
|
||||
List<Subspace>? subspaces,
|
||||
}) {
|
||||
return SpaceDetailsModel(
|
||||
uuid: uuid ?? this.uuid,
|
||||
spaceName: spaceName ?? this.spaceName,
|
||||
icon: icon ?? this.icon,
|
||||
productAllocations: productAllocations ?? this.productAllocations,
|
||||
subspaces: subspaces ?? this.subspaces,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
List<Object?> get props => [uuid, spaceName, icon, productAllocations, subspaces];
|
||||
}
|
||||
@ -48,12 +72,10 @@ class SpaceDetailsModel extends Equatable {
|
||||
class ProductAllocation extends Equatable {
|
||||
final Product product;
|
||||
final Tag tag;
|
||||
final String? location;
|
||||
|
||||
const ProductAllocation({
|
||||
required this.product,
|
||||
required this.tag,
|
||||
this.location,
|
||||
});
|
||||
|
||||
factory ProductAllocation.fromJson(Map<String, dynamic> json) {
|
||||
@ -70,6 +92,16 @@ class ProductAllocation extends Equatable {
|
||||
};
|
||||
}
|
||||
|
||||
ProductAllocation copyWith({
|
||||
Product? product,
|
||||
Tag? tag,
|
||||
}) {
|
||||
return ProductAllocation(
|
||||
product: product ?? this.product,
|
||||
tag: tag ?? this.tag,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
List<Object?> get props => [product, tag];
|
||||
}
|
||||
@ -88,7 +120,7 @@ class Subspace extends Equatable {
|
||||
factory Subspace.fromJson(Map<String, dynamic> json) {
|
||||
return Subspace(
|
||||
uuid: json['uuid'] as String,
|
||||
name: json['name'] as String,
|
||||
name: json['subspaceName'] as String,
|
||||
productAllocations: (json['productAllocations'] as List)
|
||||
.map((e) => ProductAllocation.fromJson(e as Map<String, dynamic>))
|
||||
.toList(),
|
||||
@ -103,6 +135,18 @@ class Subspace extends Equatable {
|
||||
};
|
||||
}
|
||||
|
||||
Subspace copyWith({
|
||||
String? uuid,
|
||||
String? name,
|
||||
List<ProductAllocation>? productAllocations,
|
||||
}) {
|
||||
return Subspace(
|
||||
uuid: uuid ?? this.uuid,
|
||||
name: name ?? this.name,
|
||||
productAllocations: productAllocations ?? this.productAllocations,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
List<Object?> get props => [uuid, name, productAllocations];
|
||||
}
|
||||
|
||||
@ -0,0 +1,9 @@
|
||||
class LoadSpaceDetailsParam {
|
||||
const LoadSpaceDetailsParam({
|
||||
required this.spaceUuid,
|
||||
required this.communityUuid,
|
||||
});
|
||||
|
||||
final String spaceUuid;
|
||||
final String communityUuid;
|
||||
}
|
||||
@ -1,3 +0,0 @@
|
||||
class LoadSpacesParam {
|
||||
const LoadSpacesParam();
|
||||
}
|
||||
@ -1,6 +1,6 @@
|
||||
import 'package:syncrow_web/pages/space_management_v2/modules/space_details/domain/models/space_details_model.dart';
|
||||
import 'package:syncrow_web/pages/space_management_v2/modules/space_details/domain/params/load_spaces_param.dart';
|
||||
import 'package:syncrow_web/pages/space_management_v2/modules/space_details/domain/params/load_space_details_param.dart';
|
||||
|
||||
abstract class SpaceDetailsService {
|
||||
Future<SpaceDetailsModel> getSpaceDetails(LoadSpacesParam param);
|
||||
Future<SpaceDetailsModel> getSpaceDetails(LoadSpaceDetailsParam param);
|
||||
}
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import 'package:bloc/bloc.dart';
|
||||
import 'package:equatable/equatable.dart';
|
||||
import 'package:syncrow_web/pages/space_management_v2/modules/space_details/domain/models/space_details_model.dart';
|
||||
import 'package:syncrow_web/pages/space_management_v2/modules/space_details/domain/params/load_spaces_param.dart';
|
||||
import 'package:syncrow_web/pages/space_management_v2/modules/space_details/domain/params/load_space_details_param.dart';
|
||||
import 'package:syncrow_web/pages/space_management_v2/modules/space_details/domain/services/space_details_service.dart';
|
||||
import 'package:syncrow_web/services/api/api_exception.dart';
|
||||
|
||||
@ -9,12 +9,13 @@ part 'space_details_event.dart';
|
||||
part 'space_details_state.dart';
|
||||
|
||||
class SpaceDetailsBloc extends Bloc<SpaceDetailsEvent, SpaceDetailsState> {
|
||||
final SpaceDetailsService _spaceDetailsService;
|
||||
|
||||
SpaceDetailsBloc(this._spaceDetailsService) : super(SpaceDetailsInitial()) {
|
||||
on<LoadSpaceDetails>(_onLoadSpaceDetails);
|
||||
on<ClearSpaceDetails>(_onClearSpaceDetails);
|
||||
}
|
||||
|
||||
final SpaceDetailsService _spaceDetailsService;
|
||||
|
||||
Future<void> _onLoadSpaceDetails(
|
||||
LoadSpaceDetails event,
|
||||
Emitter<SpaceDetailsState> emit,
|
||||
@ -31,4 +32,11 @@ class SpaceDetailsBloc extends Bloc<SpaceDetailsEvent, SpaceDetailsState> {
|
||||
emit(SpaceDetailsFailure(e.toString()));
|
||||
}
|
||||
}
|
||||
|
||||
void _onClearSpaceDetails(
|
||||
ClearSpaceDetails event,
|
||||
Emitter<SpaceDetailsState> emit,
|
||||
) {
|
||||
emit(SpaceDetailsInitial());
|
||||
}
|
||||
}
|
||||
|
||||
@ -7,11 +7,18 @@ sealed class SpaceDetailsEvent extends Equatable {
|
||||
List<Object> get props => [];
|
||||
}
|
||||
|
||||
class LoadSpaceDetails extends SpaceDetailsEvent {
|
||||
final class LoadSpaceDetails extends SpaceDetailsEvent {
|
||||
const LoadSpaceDetails(this.param);
|
||||
|
||||
final LoadSpacesParam param;
|
||||
final LoadSpaceDetailsParam param;
|
||||
|
||||
@override
|
||||
List<Object> get props => [param];
|
||||
}
|
||||
|
||||
final class ClearSpaceDetails extends SpaceDetailsEvent {
|
||||
const ClearSpaceDetails();
|
||||
|
||||
@override
|
||||
List<Object> get props => [];
|
||||
}
|
||||
@ -21,10 +21,10 @@ final class SpaceDetailsLoaded extends SpaceDetailsState {
|
||||
}
|
||||
|
||||
final class SpaceDetailsFailure extends SpaceDetailsState {
|
||||
final String message;
|
||||
final String errorMessage;
|
||||
|
||||
const SpaceDetailsFailure(this.message);
|
||||
const SpaceDetailsFailure(this.errorMessage);
|
||||
|
||||
@override
|
||||
List<Object> get props => [message];
|
||||
List<Object> get props => [errorMessage];
|
||||
}
|
||||
|
||||
@ -1,11 +1,46 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:syncrow_web/pages/space_management_v2/modules/communities/domain/models/space_model.dart';
|
||||
import 'package:syncrow_web/pages/space_management_v2/modules/space_details/data/services/remote_space_details_service.dart';
|
||||
import 'package:syncrow_web/pages/space_management_v2/modules/space_details/presentation/bloc/space_details_bloc.dart';
|
||||
import 'package:syncrow_web/pages/space_management_v2/modules/space_details/presentation/widgets/space_details_dialog.dart';
|
||||
import 'package:syncrow_web/services/api/http_service.dart';
|
||||
|
||||
abstract final class SpaceDetailsDialogHelper {
|
||||
static void showCreate(BuildContext context) {
|
||||
showDialog<void>(
|
||||
context: context,
|
||||
builder: (context) => const SpaceDetailsDialog(),
|
||||
builder: (_) => BlocProvider(
|
||||
create: (context) => SpaceDetailsBloc(
|
||||
RemoteSpaceDetailsService(httpService: HTTPService()),
|
||||
),
|
||||
child: SpaceDetailsDialog(
|
||||
context: context,
|
||||
title: const Text('Create Space'),
|
||||
spaceModel: SpaceModel.empty(),
|
||||
onSave: print,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
static void showEdit(
|
||||
BuildContext context, {
|
||||
required SpaceModel spaceModel,
|
||||
}) {
|
||||
showDialog<void>(
|
||||
context: context,
|
||||
builder: (_) => BlocProvider(
|
||||
create: (context) => SpaceDetailsBloc(
|
||||
RemoteSpaceDetailsService(httpService: HTTPService()),
|
||||
),
|
||||
child: SpaceDetailsDialog(
|
||||
context: context,
|
||||
title: const Text('Edit Space'),
|
||||
spaceModel: spaceModel,
|
||||
onSave: (space) {},
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,60 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_svg/svg.dart';
|
||||
import 'package:syncrow_web/utils/color_manager.dart';
|
||||
|
||||
class ButtonContentWidget extends StatelessWidget {
|
||||
final String label;
|
||||
final String? svgAssets;
|
||||
final bool disabled;
|
||||
|
||||
const ButtonContentWidget({
|
||||
required this.label,
|
||||
this.svgAssets,
|
||||
this.disabled = false,
|
||||
super.key,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final screenWidth = MediaQuery.of(context).size.width;
|
||||
|
||||
return Opacity(
|
||||
opacity: disabled ? 0.5 : 1.0,
|
||||
child: Container(
|
||||
width: screenWidth * 0.25,
|
||||
decoration: BoxDecoration(
|
||||
color: ColorsManager.textFieldGreyColor,
|
||||
border: Border.all(
|
||||
color: ColorsManager.neutralGray,
|
||||
width: 3.0,
|
||||
),
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
),
|
||||
padding: const EdgeInsets.symmetric(vertical: 10.0, horizontal: 16.0),
|
||||
child: Row(
|
||||
children: [
|
||||
if (svgAssets != null)
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(left: 6.0),
|
||||
child: SvgPicture.asset(
|
||||
svgAssets!,
|
||||
width: screenWidth * 0.015,
|
||||
height: screenWidth * 0.015,
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 10),
|
||||
Expanded(
|
||||
child: Text(
|
||||
label,
|
||||
style: const TextStyle(
|
||||
color: ColorsManager.blackColor,
|
||||
fontSize: 16,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,45 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:syncrow_web/pages/common/buttons/cancel_button.dart';
|
||||
import 'package:syncrow_web/pages/common/buttons/default_button.dart';
|
||||
import 'package:syncrow_web/utils/color_manager.dart';
|
||||
|
||||
class SpaceDetailsActionButtons extends StatelessWidget {
|
||||
const SpaceDetailsActionButtons({
|
||||
super.key,
|
||||
required this.onSave,
|
||||
required this.onCancel,
|
||||
});
|
||||
|
||||
final VoidCallback onCancel;
|
||||
final VoidCallback? onSave;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
spacing: 10,
|
||||
children: [
|
||||
Expanded(child: _buildCancelButton(context)),
|
||||
Expanded(child: _buildSaveButton()),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildCancelButton(BuildContext context) {
|
||||
return CancelButton(
|
||||
onPressed: onCancel,
|
||||
label: 'Cancel',
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildSaveButton() {
|
||||
return DefaultButton(
|
||||
onPressed: onSave,
|
||||
borderRadius: 10,
|
||||
backgroundColor: ColorsManager.secondaryColor,
|
||||
foregroundColor: ColorsManager.whiteColors,
|
||||
child: const Text('OK'),
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,92 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:syncrow_web/common/edit_chip.dart';
|
||||
import 'package:syncrow_web/pages/space_management_v2/modules/space_details/domain/models/space_details_model.dart';
|
||||
import 'package:syncrow_web/pages/space_management_v2/modules/space_details/presentation/widgets/button_content_widget.dart';
|
||||
import 'package:syncrow_web/utils/color_manager.dart';
|
||||
import 'package:syncrow_web/utils/constants/assets.dart';
|
||||
|
||||
class SpaceDetailsDevicesBox extends StatelessWidget {
|
||||
const SpaceDetailsDevicesBox({
|
||||
required this.space,
|
||||
super.key,
|
||||
});
|
||||
|
||||
final SpaceDetailsModel space;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final productAllocations = space.productAllocations;
|
||||
final subspaces = space.subspaces;
|
||||
final isAnySubspaceHasProductAllocations =
|
||||
subspaces.any((subspace) => subspace.productAllocations.isNotEmpty);
|
||||
if (productAllocations.isNotEmpty || isAnySubspaceHasProductAllocations) {
|
||||
return Container(
|
||||
width: double.infinity,
|
||||
padding: const EdgeInsets.all(8),
|
||||
decoration: BoxDecoration(
|
||||
color: ColorsManager.textFieldGreyColor,
|
||||
borderRadius: BorderRadius.circular(15),
|
||||
border: Border.all(
|
||||
color: ColorsManager.textFieldGreyColor,
|
||||
width: 3.0,
|
||||
),
|
||||
),
|
||||
child: Wrap(
|
||||
spacing: 8.0,
|
||||
runSpacing: 8.0,
|
||||
children: [
|
||||
// Combine tags from spaceModel and subspaces
|
||||
// ...TagHelper.groupTags([
|
||||
// ...?tags,
|
||||
// ...?subspaces?.expand((subspace) => subspace.tags ?? [])
|
||||
// ]).entries.map(
|
||||
// (entry) => Chip(
|
||||
// avatar: SizedBox(
|
||||
// width: 24,
|
||||
// height: 24,
|
||||
// child: SvgPicture.asset(
|
||||
// entry.key.icon ?? 'assets/icons/gateway.svg',
|
||||
// fit: BoxFit.contain,
|
||||
// ),
|
||||
// ),
|
||||
// label: Text(
|
||||
// 'x${entry.value}',
|
||||
// style: Theme.of(context)
|
||||
// .textTheme
|
||||
// .bodySmall
|
||||
// ?.copyWith(color: ColorsManager.spaceColor),
|
||||
// ),
|
||||
// backgroundColor: ColorsManager.whiteColors,
|
||||
// shape: RoundedRectangleBorder(
|
||||
// borderRadius: BorderRadius.circular(16),
|
||||
// side: const BorderSide(
|
||||
// color: ColorsManager.spaceColor,
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
|
||||
EditChip(
|
||||
onTap: () {},
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
} else {
|
||||
return TextButton(
|
||||
onPressed: () {},
|
||||
style: TextButton.styleFrom(
|
||||
padding: EdgeInsets.zero,
|
||||
),
|
||||
child: const SizedBox(
|
||||
width: double.infinity,
|
||||
child: ButtonContentWidget(
|
||||
svgAssets: Assets.addIcon,
|
||||
label: 'Add Devices',
|
||||
// disabled: isTagsAndSubspaceModelDisabled,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,12 +1,101 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:syncrow_web/pages/space_management_v2/modules/communities/domain/models/space_model.dart';
|
||||
import 'package:syncrow_web/pages/space_management_v2/modules/communities/presentation/communities_tree_selection_bloc/communities_tree_selection_bloc.dart';
|
||||
import 'package:syncrow_web/pages/space_management_v2/modules/space_details/domain/models/space_details_model.dart';
|
||||
import 'package:syncrow_web/pages/space_management_v2/modules/space_details/domain/params/load_space_details_param.dart';
|
||||
import 'package:syncrow_web/pages/space_management_v2/modules/space_details/presentation/bloc/space_details_bloc.dart';
|
||||
import 'package:syncrow_web/pages/space_management_v2/modules/space_details/presentation/widgets/space_details_form.dart';
|
||||
import 'package:syncrow_web/utils/color_manager.dart';
|
||||
import 'package:syncrow_web/utils/extension/build_context_x.dart';
|
||||
|
||||
class SpaceDetailsDialog extends StatelessWidget {
|
||||
const SpaceDetailsDialog({super.key});
|
||||
class SpaceDetailsDialog extends StatefulWidget {
|
||||
const SpaceDetailsDialog({
|
||||
required this.title,
|
||||
required this.spaceModel,
|
||||
required this.onSave,
|
||||
required this.context,
|
||||
super.key,
|
||||
});
|
||||
|
||||
final Widget title;
|
||||
final SpaceModel spaceModel;
|
||||
final void Function(SpaceDetailsModel space) onSave;
|
||||
final BuildContext context;
|
||||
|
||||
@override
|
||||
State<SpaceDetailsDialog> createState() => _SpaceDetailsDialogState();
|
||||
}
|
||||
|
||||
class _SpaceDetailsDialogState extends State<SpaceDetailsDialog> {
|
||||
@override
|
||||
void initState() {
|
||||
final isCreateMode = widget.spaceModel.uuid.isEmpty;
|
||||
|
||||
if (!isCreateMode) {
|
||||
final param = LoadSpaceDetailsParam(
|
||||
spaceUuid: widget.spaceModel.uuid,
|
||||
communityUuid: widget.context
|
||||
.read<CommunitiesTreeSelectionBloc>()
|
||||
.state
|
||||
.selectedCommunity!
|
||||
.uuid,
|
||||
);
|
||||
widget.context.read<SpaceDetailsBloc>().add(LoadSpaceDetails(param));
|
||||
}
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return const Dialog(
|
||||
child: Text('Create Space'),
|
||||
final isCreateMode = widget.spaceModel.uuid.isEmpty;
|
||||
if (isCreateMode) {
|
||||
return SpaceDetailsForm(
|
||||
title: widget.title,
|
||||
space: SpaceDetailsModel.empty(),
|
||||
onSave: widget.onSave,
|
||||
);
|
||||
}
|
||||
|
||||
return BlocBuilder<SpaceDetailsBloc, SpaceDetailsState>(
|
||||
bloc: widget.context.read<SpaceDetailsBloc>(),
|
||||
builder: (context, state) => switch (state) {
|
||||
SpaceDetailsInitial() => _buildLoadingDialog(),
|
||||
SpaceDetailsLoading() => _buildLoadingDialog(),
|
||||
SpaceDetailsLoaded(:final spaceDetails) => SpaceDetailsForm(
|
||||
title: widget.title,
|
||||
space: spaceDetails,
|
||||
onSave: widget.onSave,
|
||||
),
|
||||
SpaceDetailsFailure(:final errorMessage) => _buildErrorDialog(
|
||||
errorMessage,
|
||||
),
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildLoadingDialog() {
|
||||
return AlertDialog(
|
||||
title: widget.title,
|
||||
backgroundColor: ColorsManager.whiteColors,
|
||||
content: const Center(child: CircularProgressIndicator()),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildErrorDialog(String errorMessage) {
|
||||
return AlertDialog(
|
||||
title: widget.title,
|
||||
backgroundColor: ColorsManager.whiteColors,
|
||||
content: Center(
|
||||
child: SelectableText(
|
||||
errorMessage,
|
||||
style: context.textTheme.bodyLarge?.copyWith(
|
||||
color: ColorsManager.red,
|
||||
fontWeight: FontWeight.w500,
|
||||
fontSize: 18,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,77 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:syncrow_web/pages/space_management_v2/modules/space_details/domain/models/space_details_model.dart';
|
||||
import 'package:syncrow_web/pages/space_management_v2/modules/space_details/presentation/widgets/space_details_action_buttons.dart';
|
||||
import 'package:syncrow_web/pages/space_management_v2/modules/space_details/presentation/widgets/space_details_devices_box.dart';
|
||||
import 'package:syncrow_web/pages/space_management_v2/modules/space_details/presentation/widgets/space_icon_picker.dart';
|
||||
import 'package:syncrow_web/pages/space_management_v2/modules/space_details/presentation/widgets/space_name_text_field.dart';
|
||||
import 'package:syncrow_web/pages/space_management_v2/modules/space_details/presentation/widgets/space_sub_spaces_box.dart';
|
||||
import 'package:syncrow_web/pages/space_management_v2/modules/update_space/presentation/bloc/space_details_model_bloc/space_details_model_bloc.dart';
|
||||
import 'package:syncrow_web/utils/color_manager.dart';
|
||||
import 'package:syncrow_web/utils/extension/build_context_x.dart';
|
||||
|
||||
class SpaceDetailsForm extends StatelessWidget {
|
||||
const SpaceDetailsForm({
|
||||
required this.title,
|
||||
required this.space,
|
||||
required this.onSave,
|
||||
super.key,
|
||||
});
|
||||
|
||||
final Widget title;
|
||||
final SpaceDetailsModel space;
|
||||
final void Function(SpaceDetailsModel space) onSave;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return BlocProvider(
|
||||
create: (context) => SpaceDetailsModelBloc(initialState: space),
|
||||
child: BlocBuilder<SpaceDetailsModelBloc, SpaceDetailsModel>(
|
||||
buildWhen: (previous, current) => previous != current,
|
||||
builder: (context, state) {
|
||||
return AlertDialog(
|
||||
title: title,
|
||||
backgroundColor: ColorsManager.whiteColors,
|
||||
content: SizedBox(
|
||||
height: context.screenHeight * 0.3,
|
||||
width: context.screenWidth * 0.5,
|
||||
child: Row(
|
||||
spacing: 20,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Expanded(child: SpaceIconPicker(iconPath: state.icon)),
|
||||
Expanded(
|
||||
flex: 2,
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
SpaceNameTextField(
|
||||
initialValue: state.spaceName,
|
||||
isNameFieldExist: (value) => state.subspaces.any(
|
||||
(subspace) => subspace.name == value,
|
||||
),
|
||||
),
|
||||
const Spacer(),
|
||||
SpaceSubSpacesBox(
|
||||
subspaces: state.subspaces,
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
SpaceDetailsDevicesBox(space: state),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
actions: [
|
||||
SpaceDetailsActionButtons(
|
||||
onSave: () => onSave(state),
|
||||
onCancel: Navigator.of(context).pop,
|
||||
),
|
||||
],
|
||||
);
|
||||
}),
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,76 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:flutter_svg/svg.dart';
|
||||
import 'package:syncrow_web/pages/space_management_v2/modules/space_details/presentation/widgets/space_icon_selection_dialog.dart';
|
||||
import 'package:syncrow_web/pages/space_management_v2/modules/update_space/presentation/bloc/space_details_model_bloc/space_details_model_bloc.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';
|
||||
|
||||
class SpaceIconPicker extends StatelessWidget {
|
||||
const SpaceIconPicker({
|
||||
required this.iconPath,
|
||||
super.key,
|
||||
});
|
||||
|
||||
final String iconPath;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Center(
|
||||
child: Stack(
|
||||
|
||||
alignment: Alignment.center,
|
||||
children: [
|
||||
Container(
|
||||
width: context.screenWidth * 0.175,
|
||||
height: context.screenHeight * 0.175,
|
||||
decoration: const BoxDecoration(
|
||||
color: ColorsManager.boxColor,
|
||||
shape: BoxShape.circle,
|
||||
),
|
||||
padding: const EdgeInsets.all(24),
|
||||
child: SvgPicture.asset(
|
||||
iconPath,
|
||||
width: context.screenWidth * 0.08,
|
||||
height: context.screenHeight * 0.08,
|
||||
),
|
||||
),
|
||||
Positioned.directional(
|
||||
top: 12,
|
||||
start: context.screenHeight * 0.06,
|
||||
textDirection: Directionality.of(context),
|
||||
child: InkWell(
|
||||
onTap: () {
|
||||
showDialog<String?>(
|
||||
context: context,
|
||||
builder: (context) => SpaceIconSelectionDialog(
|
||||
selectedIcon: iconPath,
|
||||
),
|
||||
).then((value) {
|
||||
if (value != null) {
|
||||
if (context.mounted) {
|
||||
context.read<SpaceDetailsModelBloc>().add(
|
||||
UpdateSpaceDetailsIcon(value),
|
||||
);
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
child: Container(
|
||||
decoration: const BoxDecoration(
|
||||
shape: BoxShape.circle,
|
||||
),
|
||||
child: SvgPicture.asset(
|
||||
Assets.iconEdit,
|
||||
width: 16,
|
||||
height: 16,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,75 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_svg/svg.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';
|
||||
|
||||
class SpaceIconSelectionDialog extends StatelessWidget {
|
||||
const SpaceIconSelectionDialog({super.key, required this.selectedIcon});
|
||||
final String selectedIcon;
|
||||
|
||||
static const List<String> _icons = [
|
||||
Assets.location,
|
||||
Assets.villa,
|
||||
Assets.gym,
|
||||
Assets.sauna,
|
||||
Assets.bbq,
|
||||
Assets.building,
|
||||
Assets.desk,
|
||||
Assets.door,
|
||||
Assets.parking,
|
||||
Assets.pool,
|
||||
Assets.stair,
|
||||
Assets.steamRoom,
|
||||
Assets.street,
|
||||
Assets.unit,
|
||||
];
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return AlertDialog(
|
||||
title: SelectableText(
|
||||
'Space Icon',
|
||||
style: context.textTheme.headlineMedium,
|
||||
),
|
||||
backgroundColor: ColorsManager.whiteColors,
|
||||
content: Container(
|
||||
width: context.screenWidth * 0.45,
|
||||
height: context.screenHeight * 0.275,
|
||||
padding: const EdgeInsets.all(12),
|
||||
decoration: BoxDecoration(
|
||||
color: ColorsManager.boxColor,
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
),
|
||||
child: GridView.builder(
|
||||
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
|
||||
crossAxisCount: 7,
|
||||
crossAxisSpacing: 8,
|
||||
mainAxisSpacing: 16,
|
||||
),
|
||||
itemCount: _icons.length,
|
||||
itemBuilder: (context, index) {
|
||||
final isSelected = selectedIcon == _icons[index];
|
||||
return Container(
|
||||
padding: const EdgeInsetsDirectional.all(2),
|
||||
decoration: BoxDecoration(
|
||||
shape: BoxShape.circle,
|
||||
border: isSelected
|
||||
? Border.all(color: ColorsManager.vividBlue, width: 2)
|
||||
: null,
|
||||
),
|
||||
child: IconButton(
|
||||
onPressed: () => Navigator.of(context).pop(_icons[index]),
|
||||
icon: SvgPicture.asset(
|
||||
_icons[index],
|
||||
width: context.screenWidth * 0.03,
|
||||
height: context.screenHeight * 0.08,
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,85 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:syncrow_web/pages/space_management_v2/modules/update_space/presentation/bloc/space_details_model_bloc/space_details_model_bloc.dart';
|
||||
import 'package:syncrow_web/utils/color_manager.dart';
|
||||
import 'package:syncrow_web/utils/extension/build_context_x.dart';
|
||||
|
||||
class SpaceNameTextField extends StatefulWidget {
|
||||
const SpaceNameTextField({
|
||||
required this.initialValue,
|
||||
required this.isNameFieldExist,
|
||||
super.key,
|
||||
});
|
||||
|
||||
final String? initialValue;
|
||||
final bool Function(String value) isNameFieldExist;
|
||||
|
||||
@override
|
||||
State<SpaceNameTextField> createState() => _SpaceNameTextFieldState();
|
||||
}
|
||||
|
||||
class _SpaceNameTextFieldState extends State<SpaceNameTextField> {
|
||||
late final TextEditingController _controller;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
_controller = TextEditingController(text: widget.initialValue);
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_controller.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
final _formKey = GlobalKey<FormState>();
|
||||
|
||||
String? _validateName(String? value) {
|
||||
if (value == null || value.isEmpty) {
|
||||
return '*Space name should not be empty.';
|
||||
}
|
||||
if (widget.isNameFieldExist(value)) {
|
||||
return '*Name already exists';
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Form(
|
||||
key: _formKey,
|
||||
autovalidateMode: AutovalidateMode.onUserInteraction,
|
||||
child: TextFormField(
|
||||
controller: _controller,
|
||||
onChanged: (value) => context.read<SpaceDetailsModelBloc>().add(
|
||||
UpdateSpaceDetailsName(value),
|
||||
),
|
||||
validator: _validateName,
|
||||
style: context.textTheme.bodyMedium,
|
||||
decoration: InputDecoration(
|
||||
hintText: 'Please enter the name',
|
||||
hintStyle: context.textTheme.bodyMedium!.copyWith(
|
||||
color: ColorsManager.lightGrayColor,
|
||||
),
|
||||
filled: true,
|
||||
fillColor: ColorsManager.boxColor,
|
||||
enabledBorder: _buildBorder(context, ColorsManager.vividBlue),
|
||||
focusedBorder: _buildBorder(context, ColorsManager.primaryColor),
|
||||
errorBorder: _buildBorder(context, context.theme.colorScheme.error),
|
||||
focusedErrorBorder: _buildBorder(context, context.theme.colorScheme.error),
|
||||
errorStyle: context.textTheme.bodySmall?.copyWith(
|
||||
color: context.theme.colorScheme.error,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
OutlineInputBorder _buildBorder(BuildContext context, [Color? color]) {
|
||||
return OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
borderSide: BorderSide(width: 1, color: color ?? ColorsManager.boxColor),
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,74 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:syncrow_web/common/edit_chip.dart';
|
||||
import 'package:syncrow_web/pages/space_management_v2/modules/space_details/domain/models/space_details_model.dart';
|
||||
import 'package:syncrow_web/pages/space_management_v2/modules/space_details/presentation/widgets/button_content_widget.dart';
|
||||
import 'package:syncrow_web/pages/space_management_v2/modules/space_details/presentation/widgets/space_sub_spaces_dialog.dart';
|
||||
import 'package:syncrow_web/pages/space_management_v2/modules/space_details/presentation/widgets/subspace_name_display_widget.dart';
|
||||
import 'package:syncrow_web/pages/space_management_v2/modules/update_space/presentation/bloc/space_details_model_bloc/space_details_model_bloc.dart';
|
||||
import 'package:syncrow_web/utils/color_manager.dart';
|
||||
import 'package:syncrow_web/utils/constants/assets.dart';
|
||||
|
||||
class SpaceSubSpacesBox extends StatelessWidget {
|
||||
const SpaceSubSpacesBox({super.key, required this.subspaces});
|
||||
|
||||
final List<Subspace> subspaces;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
if (subspaces.isEmpty) {
|
||||
return TextButton(
|
||||
style: TextButton.styleFrom(
|
||||
padding: EdgeInsets.zero,
|
||||
overlayColor: ColorsManager.transparentColor,
|
||||
),
|
||||
onPressed: () => _showSubSpacesDialog(context),
|
||||
child: const SizedBox(
|
||||
width: double.infinity,
|
||||
child: ButtonContentWidget(
|
||||
svgAssets: Assets.addIcon,
|
||||
label: 'Create Sub Spaces',
|
||||
),
|
||||
),
|
||||
);
|
||||
} else {
|
||||
return Container(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
width: double.infinity,
|
||||
decoration: BoxDecoration(
|
||||
color: ColorsManager.textFieldGreyColor,
|
||||
borderRadius: BorderRadius.circular(15),
|
||||
border: Border.all(
|
||||
color: ColorsManager.textFieldGreyColor,
|
||||
width: 3.0,
|
||||
),
|
||||
),
|
||||
child: Wrap(
|
||||
spacing: 8.0,
|
||||
runSpacing: 8.0,
|
||||
children: [
|
||||
...subspaces.map((e) => SubspaceNameDisplayWidget(subSpace: e)),
|
||||
EditChip(
|
||||
onTap: () => _showSubSpacesDialog(context),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
void _showSubSpacesDialog(BuildContext context) {
|
||||
showDialog<void>(
|
||||
context: context,
|
||||
barrierDismissible: false,
|
||||
builder: (_) => SpaceSubSpacesDialog(
|
||||
subspaces: subspaces,
|
||||
onSave: (subspaces) {
|
||||
context.read<SpaceDetailsModelBloc>().add(
|
||||
UpdateSpaceDetailsSubspaces(subspaces),
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,89 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:syncrow_web/pages/space_management_v2/modules/space_details/domain/models/space_details_model.dart';
|
||||
import 'package:syncrow_web/pages/space_management_v2/modules/space_details/presentation/widgets/space_details_action_buttons.dart';
|
||||
import 'package:syncrow_web/pages/space_management_v2/modules/space_details/presentation/widgets/sub_spaces_input.dart';
|
||||
import 'package:uuid/uuid.dart';
|
||||
|
||||
class SpaceSubSpacesDialog extends StatefulWidget {
|
||||
const SpaceSubSpacesDialog({
|
||||
required this.subspaces,
|
||||
required this.onSave,
|
||||
super.key,
|
||||
});
|
||||
|
||||
final List<Subspace> subspaces;
|
||||
final void Function(List<Subspace> subspaces) onSave;
|
||||
|
||||
@override
|
||||
State<SpaceSubSpacesDialog> createState() => _SpaceSubSpacesDialogState();
|
||||
}
|
||||
|
||||
class _SpaceSubSpacesDialogState extends State<SpaceSubSpacesDialog> {
|
||||
late List<Subspace> _subspaces;
|
||||
|
||||
bool get _hasDuplicateNames =>
|
||||
_subspaces.map((subspace) => subspace.name.toLowerCase()).toSet().length !=
|
||||
_subspaces.length;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_subspaces = List.from(widget.subspaces);
|
||||
}
|
||||
|
||||
void _handleSubspaceAdded(String name) {
|
||||
setState(() {
|
||||
_subspaces = [
|
||||
..._subspaces,
|
||||
Subspace(
|
||||
name: name,
|
||||
uuid: const Uuid().v4(),
|
||||
productAllocations: const [],
|
||||
),
|
||||
];
|
||||
});
|
||||
}
|
||||
|
||||
void _handleSubspaceDeleted(String uuid) => setState(
|
||||
() => _subspaces = _subspaces.where((s) => s.uuid != uuid).toList(),
|
||||
);
|
||||
|
||||
void _handleSave() {
|
||||
widget.onSave(_subspaces);
|
||||
Navigator.of(context).pop();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return AlertDialog(
|
||||
title: const Text('Create Sub Spaces'),
|
||||
content: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
SubSpacesInput(
|
||||
subSpaces: _subspaces,
|
||||
onSubspaceAdded: _handleSubspaceAdded,
|
||||
onSubspaceDeleted: _handleSubspaceDeleted,
|
||||
),
|
||||
AnimatedSwitcher(
|
||||
duration: const Duration(milliseconds: 100),
|
||||
child: Visibility(
|
||||
key: ValueKey(_hasDuplicateNames),
|
||||
visible: _hasDuplicateNames,
|
||||
child: const Text(
|
||||
'Error: Duplicate subspace names are not allowed.',
|
||||
style: TextStyle(color: Colors.red),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
actions: [
|
||||
SpaceDetailsActionButtons(
|
||||
onSave: _hasDuplicateNames ? null : _handleSave,
|
||||
onCancel: Navigator.of(context).pop,
|
||||
)
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,107 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:syncrow_web/pages/space_management_v2/modules/space_details/domain/models/space_details_model.dart';
|
||||
import 'package:syncrow_web/pages/space_management_v2/modules/space_details/presentation/widgets/subspace_chip.dart';
|
||||
import 'package:syncrow_web/utils/color_manager.dart';
|
||||
import 'package:syncrow_web/utils/extension/build_context_x.dart';
|
||||
|
||||
class SubSpacesInput extends StatefulWidget {
|
||||
const SubSpacesInput({
|
||||
super.key,
|
||||
required this.subSpaces,
|
||||
required this.onSubspaceAdded,
|
||||
required this.onSubspaceDeleted,
|
||||
});
|
||||
|
||||
final List<Subspace> subSpaces;
|
||||
final void Function(String name) onSubspaceAdded;
|
||||
final void Function(String uuid) onSubspaceDeleted;
|
||||
|
||||
@override
|
||||
State<SubSpacesInput> createState() => _SubSpacesInputState();
|
||||
}
|
||||
|
||||
class _SubSpacesInputState extends State<SubSpacesInput> {
|
||||
late final TextEditingController _subspaceNameController;
|
||||
late final FocusNode _focusNode;
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_subspaceNameController = TextEditingController();
|
||||
_focusNode = FocusNode();
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_subspaceNameController.dispose();
|
||||
_focusNode.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
width: context.screenWidth * 0.35,
|
||||
padding: const EdgeInsets.symmetric(
|
||||
vertical: 10,
|
||||
horizontal: 16,
|
||||
),
|
||||
decoration: BoxDecoration(
|
||||
color: ColorsManager.boxColor,
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
),
|
||||
child: Wrap(
|
||||
spacing: 8,
|
||||
runSpacing: 8,
|
||||
alignment: WrapAlignment.start,
|
||||
crossAxisAlignment: WrapCrossAlignment.center,
|
||||
children: [
|
||||
...widget.subSpaces.asMap().entries.map(
|
||||
(entry) {
|
||||
final index = entry.key;
|
||||
final subSpace = entry.value;
|
||||
|
||||
final lowerName = subSpace.name.toLowerCase();
|
||||
|
||||
final duplicateIndices = widget.subSpaces
|
||||
.asMap()
|
||||
.entries
|
||||
.where((e) => e.value.name.toLowerCase() == lowerName)
|
||||
.map((e) => e.key)
|
||||
.toList();
|
||||
final isDuplicate = duplicateIndices.length > 1 &&
|
||||
duplicateIndices.indexOf(index) != 0;
|
||||
return SubspaceChip(
|
||||
subSpace: subSpace,
|
||||
isDuplicate: isDuplicate,
|
||||
onDeleted: () => widget.onSubspaceDeleted(subSpace.uuid),
|
||||
);
|
||||
},
|
||||
),
|
||||
SizedBox(
|
||||
width: 200,
|
||||
child: TextField(
|
||||
focusNode: _focusNode,
|
||||
controller: _subspaceNameController,
|
||||
decoration: InputDecoration(
|
||||
border: InputBorder.none,
|
||||
hintText: widget.subSpaces.isEmpty ? 'Please enter the name' : null,
|
||||
hintStyle: context.textTheme.bodySmall?.copyWith(
|
||||
color: ColorsManager.lightGrayColor,
|
||||
),
|
||||
),
|
||||
onSubmitted: (value) {
|
||||
final trimmedValue = value.trim();
|
||||
if (trimmedValue.isNotEmpty) {
|
||||
widget.onSubspaceAdded(trimmedValue);
|
||||
_subspaceNameController.clear();
|
||||
_focusNode.requestFocus();
|
||||
}
|
||||
},
|
||||
style: context.textTheme.bodyMedium,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,55 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:syncrow_web/pages/space_management_v2/modules/space_details/domain/models/space_details_model.dart';
|
||||
import 'package:syncrow_web/utils/color_manager.dart';
|
||||
import 'package:syncrow_web/utils/extension/build_context_x.dart';
|
||||
|
||||
class SubspaceChip extends StatelessWidget {
|
||||
const SubspaceChip({
|
||||
required this.subSpace,
|
||||
required this.isDuplicate,
|
||||
required this.onDeleted,
|
||||
super.key,
|
||||
});
|
||||
|
||||
final Subspace subSpace;
|
||||
final bool isDuplicate;
|
||||
final void Function() onDeleted;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Chip(
|
||||
label: Text(
|
||||
subSpace.name,
|
||||
style: context.textTheme.bodySmall?.copyWith(
|
||||
color: isDuplicate ? ColorsManager.red : ColorsManager.spaceColor,
|
||||
),
|
||||
),
|
||||
backgroundColor: ColorsManager.whiteColors,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
side: BorderSide(
|
||||
color: isDuplicate ? ColorsManager.red : ColorsManager.transparentColor,
|
||||
width: 0,
|
||||
),
|
||||
),
|
||||
deleteIcon: Container(
|
||||
padding: const EdgeInsetsDirectional.all(1),
|
||||
decoration: BoxDecoration(
|
||||
shape: BoxShape.circle,
|
||||
border: Border.all(
|
||||
color: ColorsManager.lightGrayColor,
|
||||
width: 1.5,
|
||||
),
|
||||
),
|
||||
child: const FittedBox(
|
||||
fit: BoxFit.scaleDown,
|
||||
child: Icon(
|
||||
Icons.close,
|
||||
color: ColorsManager.lightGrayColor,
|
||||
),
|
||||
),
|
||||
),
|
||||
onDeleted: onDeleted,
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,171 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:syncrow_web/pages/space_management_v2/modules/space_details/domain/models/space_details_model.dart';
|
||||
import 'package:syncrow_web/pages/space_management_v2/modules/update_space/presentation/bloc/space_details_model_bloc/space_details_model_bloc.dart';
|
||||
import 'package:syncrow_web/utils/color_manager.dart';
|
||||
import 'package:syncrow_web/utils/extension/build_context_x.dart';
|
||||
|
||||
class SubspaceNameDisplayWidget extends StatefulWidget {
|
||||
const SubspaceNameDisplayWidget({super.key, required this.subSpace});
|
||||
|
||||
final Subspace subSpace;
|
||||
|
||||
@override
|
||||
State<SubspaceNameDisplayWidget> createState() =>
|
||||
_SubspaceNameDisplayWidgetState();
|
||||
}
|
||||
|
||||
class _SubspaceNameDisplayWidgetState extends State<SubspaceNameDisplayWidget> {
|
||||
late final TextEditingController _controller;
|
||||
late final FocusNode _focusNode;
|
||||
bool _isEditing = false;
|
||||
bool _hasDuplicateName = false;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
_controller = TextEditingController(text: widget.subSpace.name);
|
||||
_focusNode = FocusNode();
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_controller.dispose();
|
||||
_focusNode.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
bool _checkForDuplicateName(String name) {
|
||||
final bloc = context.read<SpaceDetailsModelBloc>();
|
||||
return bloc.state.subspaces
|
||||
.where((s) => s.uuid != widget.subSpace.uuid)
|
||||
.any((s) => s.name.toLowerCase() == name.toLowerCase());
|
||||
}
|
||||
|
||||
void _handleNameChange(String value) {
|
||||
setState(() {
|
||||
_hasDuplicateName = _checkForDuplicateName(value);
|
||||
});
|
||||
}
|
||||
|
||||
void _tryToFinishEditing() {
|
||||
if (!_hasDuplicateName) {
|
||||
_onFinishEditing();
|
||||
}
|
||||
}
|
||||
|
||||
void _tryToSubmit(String value) {
|
||||
if (_hasDuplicateName) return;
|
||||
|
||||
final bloc = context.read<SpaceDetailsModelBloc>();
|
||||
bloc.add(
|
||||
UpdateSpaceDetailsSubspaces(
|
||||
bloc.state.subspaces
|
||||
.map(
|
||||
(e) => e.uuid == widget.subSpace.uuid ? e.copyWith(name: value) : e,
|
||||
)
|
||||
.toList(),
|
||||
),
|
||||
);
|
||||
_onFinishEditing();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final textStyle = context.textTheme.bodySmall?.copyWith(
|
||||
color: ColorsManager.spaceColor,
|
||||
);
|
||||
return InkWell(
|
||||
onTap: () {
|
||||
setState(() => _isEditing = true);
|
||||
_focusNode.requestFocus();
|
||||
},
|
||||
child: Chip(
|
||||
backgroundColor: ColorsManager.whiteColors,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
side: const BorderSide(color: ColorsManager.transparentColor),
|
||||
),
|
||||
onDeleted: () {
|
||||
final bloc = context.read<SpaceDetailsModelBloc>();
|
||||
bloc.add(
|
||||
UpdateSpaceDetailsSubspaces(
|
||||
bloc.state.subspaces
|
||||
.where((s) => s.uuid != widget.subSpace.uuid)
|
||||
.toList(),
|
||||
),
|
||||
);
|
||||
},
|
||||
deleteIcon: Container(
|
||||
padding: const EdgeInsetsDirectional.all(1),
|
||||
decoration: BoxDecoration(
|
||||
shape: BoxShape.circle,
|
||||
border: Border.all(
|
||||
color: ColorsManager.lightGrayColor,
|
||||
width: 1.5,
|
||||
),
|
||||
),
|
||||
child: const FittedBox(
|
||||
child: Icon(
|
||||
Icons.close,
|
||||
color: ColorsManager.lightGrayColor,
|
||||
),
|
||||
),
|
||||
),
|
||||
label: Visibility(
|
||||
visible: _isEditing,
|
||||
replacement: Text(
|
||||
widget.subSpace.name,
|
||||
style: textStyle,
|
||||
),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
SizedBox(
|
||||
width: context.screenWidth * 0.065,
|
||||
height: context.screenHeight * 0.025,
|
||||
child: TextField(
|
||||
focusNode: _focusNode,
|
||||
controller: _controller,
|
||||
style: textStyle?.copyWith(
|
||||
color: _hasDuplicateName ? Colors.red : null,
|
||||
),
|
||||
decoration: const InputDecoration.collapsed(
|
||||
hintText: '',
|
||||
),
|
||||
onChanged: _handleNameChange,
|
||||
onTapOutside: (_) => _tryToFinishEditing(),
|
||||
onSubmitted: _tryToSubmit,
|
||||
),
|
||||
),
|
||||
if (_hasDuplicateName)
|
||||
AnimatedSwitcher(
|
||||
duration: const Duration(milliseconds: 250),
|
||||
child: Visibility(
|
||||
key: ValueKey(_hasDuplicateName),
|
||||
visible: _hasDuplicateName,
|
||||
child: Text(
|
||||
'Name already exists',
|
||||
style: textStyle?.copyWith(
|
||||
color: Colors.red,
|
||||
fontSize: 8,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void _onFinishEditing() {
|
||||
setState(() {
|
||||
_isEditing = false;
|
||||
_hasDuplicateName = false;
|
||||
});
|
||||
_focusNode.unfocus();
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,45 @@
|
||||
import 'package:bloc/bloc.dart';
|
||||
import 'package:equatable/equatable.dart';
|
||||
import 'package:syncrow_web/pages/space_management_v2/modules/space_details/domain/models/space_details_model.dart';
|
||||
|
||||
part 'space_details_model_event.dart';
|
||||
|
||||
class SpaceDetailsModelBloc extends Bloc<SpaceDetailsModelEvent, SpaceDetailsModel> {
|
||||
SpaceDetailsModelBloc({
|
||||
required SpaceDetailsModel initialState,
|
||||
}) : super(initialState) {
|
||||
on<UpdateSpaceDetailsIcon>(_onUpdateSpaceDetailsIcon);
|
||||
on<UpdateSpaceDetailsName>(_onUpdateSpaceDetailsName);
|
||||
on<UpdateSpaceDetailsSubspaces>(_onUpdateSpaceDetailsSubspaces);
|
||||
on<UpdateSpaceDetailsProductAllocations>(
|
||||
_onUpdateSpaceDetailsProductAllocations);
|
||||
}
|
||||
|
||||
void _onUpdateSpaceDetailsIcon(
|
||||
UpdateSpaceDetailsIcon event,
|
||||
Emitter<SpaceDetailsModel> emit,
|
||||
) {
|
||||
emit(state.copyWith(icon: event.icon));
|
||||
}
|
||||
|
||||
void _onUpdateSpaceDetailsName(
|
||||
UpdateSpaceDetailsName event,
|
||||
Emitter<SpaceDetailsModel> emit,
|
||||
) {
|
||||
emit(state.copyWith(spaceName: event.name));
|
||||
}
|
||||
|
||||
void _onUpdateSpaceDetailsSubspaces(
|
||||
UpdateSpaceDetailsSubspaces event,
|
||||
Emitter<SpaceDetailsModel> emit,
|
||||
) {
|
||||
emit(state.copyWith(subspaces: event.subspaces));
|
||||
}
|
||||
|
||||
void _onUpdateSpaceDetailsProductAllocations(
|
||||
UpdateSpaceDetailsProductAllocations event,
|
||||
Emitter<SpaceDetailsModel> emit,
|
||||
) {
|
||||
emit(state.copyWith(productAllocations: event.productAllocations));
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,44 @@
|
||||
part of 'space_details_model_bloc.dart';
|
||||
|
||||
sealed class SpaceDetailsModelEvent extends Equatable {
|
||||
const SpaceDetailsModelEvent();
|
||||
|
||||
@override
|
||||
List<Object> get props => [];
|
||||
}
|
||||
|
||||
final class UpdateSpaceDetailsIcon extends SpaceDetailsModelEvent {
|
||||
const UpdateSpaceDetailsIcon(this.icon);
|
||||
|
||||
final String icon;
|
||||
|
||||
@override
|
||||
List<Object> get props => [icon];
|
||||
}
|
||||
|
||||
final class UpdateSpaceDetailsName extends SpaceDetailsModelEvent {
|
||||
const UpdateSpaceDetailsName(this.name);
|
||||
|
||||
final String name;
|
||||
|
||||
@override
|
||||
List<Object> get props => [name];
|
||||
}
|
||||
|
||||
final class UpdateSpaceDetailsSubspaces extends SpaceDetailsModelEvent {
|
||||
const UpdateSpaceDetailsSubspaces(this.subspaces);
|
||||
|
||||
final List<Subspace> subspaces;
|
||||
|
||||
@override
|
||||
List<Object> get props => [subspaces];
|
||||
}
|
||||
|
||||
final class UpdateSpaceDetailsProductAllocations extends SpaceDetailsModelEvent {
|
||||
const UpdateSpaceDetailsProductAllocations(this.productAllocations);
|
||||
|
||||
final List<ProductAllocation> productAllocations;
|
||||
|
||||
@override
|
||||
List<Object> get props => [productAllocations];
|
||||
}
|
||||
@ -14,8 +14,7 @@ class Assets {
|
||||
static const String rightLine = 'assets/images/right_line.png';
|
||||
static const String google = 'assets/images/google.svg';
|
||||
static const String facebook = 'assets/images/facebook.svg';
|
||||
static const String invisiblePassword =
|
||||
'assets/images/Password_invisible.svg';
|
||||
static const String invisiblePassword = 'assets/images/Password_invisible.svg';
|
||||
static const String visiblePassword = 'assets/images/password_visible.svg';
|
||||
static const String accessIcon = 'assets/images/access_icon.svg';
|
||||
static const String spaseManagementIcon =
|
||||
@ -34,8 +33,7 @@ class Assets {
|
||||
static const String emptyTable = 'assets/images/empty_table.svg';
|
||||
|
||||
// General assets
|
||||
static const String motionlessDetection =
|
||||
'assets/icons/motionless_detection.svg';
|
||||
static const String motionlessDetection = 'assets/icons/motionless_detection.svg';
|
||||
static const String acHeating = 'assets/icons/ac_heating.svg';
|
||||
static const String acPowerOff = 'assets/icons/ac_power_off.svg';
|
||||
static const String acFanMiddle = 'assets/icons/ac_fan_middle.svg';
|
||||
@ -72,22 +70,19 @@ class Assets {
|
||||
'assets/icons/automation_functions/temp_password_unlock.svg';
|
||||
static const String doorlockNormalOpen =
|
||||
'assets/icons/automation_functions/doorlock_normal_open.svg';
|
||||
static const String doorbell =
|
||||
'assets/icons/automation_functions/doorbell.svg';
|
||||
static const String doorbell = 'assets/icons/automation_functions/doorbell.svg';
|
||||
static const String remoteUnlockViaApp =
|
||||
'assets/icons/automation_functions/remote_unlock_via_app.svg';
|
||||
static const String doubleLock =
|
||||
'assets/icons/automation_functions/double_lock.svg';
|
||||
static const String selfTestResult =
|
||||
'assets/icons/automation_functions/self_test_result.svg';
|
||||
static const String lockAlarm =
|
||||
'assets/icons/automation_functions/lock_alarm.svg';
|
||||
static const String lockAlarm = 'assets/icons/automation_functions/lock_alarm.svg';
|
||||
static const String presenceState =
|
||||
'assets/icons/automation_functions/presence_state.svg';
|
||||
static const String currentTemp =
|
||||
'assets/icons/automation_functions/current_temp.svg';
|
||||
static const String presence =
|
||||
'assets/icons/automation_functions/presence.svg';
|
||||
static const String presence = 'assets/icons/automation_functions/presence.svg';
|
||||
static const String residualElectricity =
|
||||
'assets/icons/automation_functions/residual_electricity.svg';
|
||||
static const String hijackAlarm =
|
||||
@ -104,15 +99,12 @@ class Assets {
|
||||
|
||||
// Presence Sensor Assets
|
||||
static const String sensorMotionIcon = 'assets/icons/sensor_motion_ic.svg';
|
||||
static const String sensorPresenceIcon =
|
||||
'assets/icons/sensor_presence_ic.svg';
|
||||
static const String sensorPresenceIcon = 'assets/icons/sensor_presence_ic.svg';
|
||||
static const String sensorVacantIcon = 'assets/icons/sensor_vacant_ic.svg';
|
||||
static const String illuminanceRecordIcon =
|
||||
'assets/icons/illuminance_record_ic.svg';
|
||||
static const String presenceRecordIcon =
|
||||
'assets/icons/presence_record_ic.svg';
|
||||
static const String helpDescriptionIcon =
|
||||
'assets/icons/help_description_ic.svg';
|
||||
static const String presenceRecordIcon = 'assets/icons/presence_record_ic.svg';
|
||||
static const String helpDescriptionIcon = 'assets/icons/help_description_ic.svg';
|
||||
|
||||
static const String lightPulp = 'assets/icons/light_pulb.svg';
|
||||
static const String acDevice = 'assets/icons/ac_device.svg';
|
||||
@ -166,12 +158,10 @@ class Assets {
|
||||
static const String unit = 'assets/icons/unit_icon.svg';
|
||||
static const String villa = 'assets/icons/villa_icon.svg';
|
||||
static const String iconEdit = 'assets/icons/icon_edit_icon.svg';
|
||||
static const String textFieldSearch =
|
||||
'assets/icons/textfield_search_icon.svg';
|
||||
static const String textFieldSearch = 'assets/icons/textfield_search_icon.svg';
|
||||
static const String roundedAddIcon = 'assets/icons/rounded_add_icon.svg';
|
||||
static const String addIcon = 'assets/icons/add_icon.svg';
|
||||
static const String smartThermostatIcon =
|
||||
'assets/icons/smart_thermostat_icon.svg';
|
||||
static const String smartThermostatIcon = 'assets/icons/smart_thermostat_icon.svg';
|
||||
static const String smartLightIcon = 'assets/icons/smart_light_icon.svg';
|
||||
static const String presenceSensor = 'assets/icons/presence_sensor.svg';
|
||||
static const String Gang3SwitchIcon = 'assets/icons/3_Gang_switch_icon.svg';
|
||||
@ -219,8 +209,7 @@ class Assets {
|
||||
//assets/icons/water_leak_normal.svg
|
||||
static const String waterLeakNormal = 'assets/icons/water_leak_normal.svg';
|
||||
//assets/icons/water_leak_detected.svg
|
||||
static const String waterLeakDetected =
|
||||
'assets/icons/water_leak_detected.svg';
|
||||
static const String waterLeakDetected = 'assets/icons/water_leak_detected.svg';
|
||||
|
||||
//assets/icons/automation_records.svg
|
||||
static const String automationRecords = 'assets/icons/automation_records.svg';
|
||||
@ -291,16 +280,13 @@ class Assets {
|
||||
'assets/icons/functions_icons/sensitivity.svg';
|
||||
static const String assetsSensitivityOperationIcon =
|
||||
'assets/icons/functions_icons/sesitivity_operation_icon.svg';
|
||||
static const String assetsAcPower =
|
||||
'assets/icons/functions_icons/ac_power.svg';
|
||||
static const String assetsAcPower = 'assets/icons/functions_icons/ac_power.svg';
|
||||
static const String assetsAcPowerOFF =
|
||||
'assets/icons/functions_icons/ac_power_off.svg';
|
||||
static const String assetsChildLock =
|
||||
'assets/icons/functions_icons/child_lock.svg';
|
||||
static const String assetsFreezing =
|
||||
'assets/icons/functions_icons/freezing.svg';
|
||||
static const String assetsFanSpeed =
|
||||
'assets/icons/functions_icons/fan_speed.svg';
|
||||
static const String assetsFreezing = 'assets/icons/functions_icons/freezing.svg';
|
||||
static const String assetsFanSpeed = 'assets/icons/functions_icons/fan_speed.svg';
|
||||
static const String assetsAcCooling =
|
||||
'assets/icons/functions_icons/ac_cooling.svg';
|
||||
static const String assetsAcHeating =
|
||||
@ -309,8 +295,7 @@ class Assets {
|
||||
'assets/icons/functions_icons/celsius_degrees.svg';
|
||||
static const String assetsTempreture =
|
||||
'assets/icons/functions_icons/tempreture.svg';
|
||||
static const String assetsAcFanLow =
|
||||
'assets/icons/functions_icons/ac_fan_low.svg';
|
||||
static const String assetsAcFanLow = 'assets/icons/functions_icons/ac_fan_low.svg';
|
||||
static const String assetsAcFanMiddle =
|
||||
'assets/icons/functions_icons/ac_fan_middle.svg';
|
||||
static const String assetsAcFanHigh =
|
||||
@ -329,8 +314,7 @@ class Assets {
|
||||
'assets/icons/functions_icons/far_detection.svg';
|
||||
static const String assetsFarDetectionFunction =
|
||||
'assets/icons/functions_icons/far_detection_function.svg';
|
||||
static const String assetsIndicator =
|
||||
'assets/icons/functions_icons/indicator.svg';
|
||||
static const String assetsIndicator = 'assets/icons/functions_icons/indicator.svg';
|
||||
static const String assetsMotionDetection =
|
||||
'assets/icons/functions_icons/motion_detection.svg';
|
||||
static const String assetsMotionlessDetection =
|
||||
@ -343,8 +327,7 @@ class Assets {
|
||||
'assets/icons/functions_icons/master_state.svg';
|
||||
static const String assetsSwitchAlarmSound =
|
||||
'assets/icons/functions_icons/switch_alarm_sound.svg';
|
||||
static const String assetsResetOff =
|
||||
'assets/icons/functions_icons/reset_off.svg';
|
||||
static const String assetsResetOff = 'assets/icons/functions_icons/reset_off.svg';
|
||||
|
||||
// Assets for automation_functions
|
||||
static const String assetsCardUnlock =
|
||||
@ -388,15 +371,13 @@ class Assets {
|
||||
static const String activeUser = 'assets/icons/active_user.svg';
|
||||
static const String deActiveUser = 'assets/icons/deactive_user.svg';
|
||||
static const String invitedIcon = 'assets/icons/invited_icon.svg';
|
||||
static const String rectangleCheckBox =
|
||||
'assets/icons/rectangle_check_box.png';
|
||||
static const String rectangleCheckBox = 'assets/icons/rectangle_check_box.png';
|
||||
static const String CheckBoxChecked = 'assets/icons/box_checked.png';
|
||||
static const String emptyBox = 'assets/icons/empty_box.png';
|
||||
static const String completeProcessIcon =
|
||||
'assets/icons/compleate_process_icon.svg';
|
||||
static const String completedDoneIcon = 'assets/images/completed_done.svg';
|
||||
static const String currentProcessIcon =
|
||||
'assets/icons/current_process_icon.svg';
|
||||
static const String currentProcessIcon = 'assets/icons/current_process_icon.svg';
|
||||
static const String uncomplete_ProcessIcon =
|
||||
'assets/icons/uncompleate_process_icon.svg';
|
||||
static const String wrongProcessIcon = 'assets/icons/wrong_process_icon.svg';
|
||||
@ -417,11 +398,9 @@ class Assets {
|
||||
static const String successIcon = 'assets/icons/success_icon.svg';
|
||||
static const String spaceLocationIcon = 'assets/icons/spaseLocationIcon.svg';
|
||||
static const String scenesPlayIcon = 'assets/icons/scenesPlayIcon.png';
|
||||
static const String scenesPlayIconCheck =
|
||||
'assets/icons/scenesPlayIconCheck.png';
|
||||
static const String scenesPlayIconCheck = 'assets/icons/scenesPlayIconCheck.png';
|
||||
static const String presenceStateIcon = 'assets/icons/presence_state.svg';
|
||||
static const String currentDistanceIcon =
|
||||
'assets/icons/current_distance_icon.svg';
|
||||
static const String currentDistanceIcon = 'assets/icons/current_distance_icon.svg';
|
||||
|
||||
static const String farDetectionIcon = 'assets/icons/far_detection_icon.svg';
|
||||
static const String motionDetectionSensitivityIcon =
|
||||
@ -444,44 +423,29 @@ class Assets {
|
||||
static const String cpsMode4 = 'assets/icons/cps_mode4.svg';
|
||||
static const String closeToMotion = 'assets/icons/close_to_motion.svg';
|
||||
static const String farAwayMotion = 'assets/icons/far_away_motion.svg';
|
||||
static const String communicationFault =
|
||||
'assets/icons/communication_fault.svg';
|
||||
static const String communicationFault = 'assets/icons/communication_fault.svg';
|
||||
static const String radarFault = 'assets/icons/radar_fault.svg';
|
||||
static const String selfTestingSuccess =
|
||||
'assets/icons/self_testing_success.svg';
|
||||
static const String selfTestingFailure =
|
||||
'assets/icons/self_testing_failure.svg';
|
||||
static const String selfTestingTimeout =
|
||||
'assets/icons/self_testing_timeout.svg';
|
||||
static const String selfTestingSuccess = 'assets/icons/self_testing_success.svg';
|
||||
static const String selfTestingFailure = 'assets/icons/self_testing_failure.svg';
|
||||
static const String selfTestingTimeout = 'assets/icons/self_testing_timeout.svg';
|
||||
static const String movingSpeed = 'assets/icons/moving_speed.svg';
|
||||
static const String boundary = 'assets/icons/boundary.svg';
|
||||
static const String motionMeter = 'assets/icons/motion_meter.svg';
|
||||
static const String spatialStaticValue =
|
||||
'assets/icons/spatial_static_value.svg';
|
||||
static const String spatialMotionValue =
|
||||
'assets/icons/spatial_motion_value.svg';
|
||||
static const String spatialStaticValue = 'assets/icons/spatial_static_value.svg';
|
||||
static const String spatialMotionValue = 'assets/icons/spatial_motion_value.svg';
|
||||
static const String presenceJudgementThrshold =
|
||||
'assets/icons/presence_judgement_threshold.svg';
|
||||
static const String spaceType = 'assets/icons/space_type.svg';
|
||||
static const String sportsPara = 'assets/icons/sports_para.svg';
|
||||
static const String sensitivityFeature1 =
|
||||
'assets/icons/sensitivity_feature_1.svg';
|
||||
static const String sensitivityFeature2 =
|
||||
'assets/icons/sensitivity_feature_2.svg';
|
||||
static const String sensitivityFeature3 =
|
||||
'assets/icons/sensitivity_feature_3.svg';
|
||||
static const String sensitivityFeature4 =
|
||||
'assets/icons/sensitivity_feature_4.svg';
|
||||
static const String sensitivityFeature5 =
|
||||
'assets/icons/sensitivity_feature_5.svg';
|
||||
static const String sensitivityFeature6 =
|
||||
'assets/icons/sensitivity_feature_6.svg';
|
||||
static const String sensitivityFeature7 =
|
||||
'assets/icons/sensitivity_feature_7.svg';
|
||||
static const String sensitivityFeature8 =
|
||||
'assets/icons/sensitivity_feature_8.svg';
|
||||
static const String sensitivityFeature9 =
|
||||
'assets/icons/sensitivity_feature_9.svg';
|
||||
static const String sensitivityFeature1 = 'assets/icons/sensitivity_feature_1.svg';
|
||||
static const String sensitivityFeature2 = 'assets/icons/sensitivity_feature_2.svg';
|
||||
static const String sensitivityFeature3 = 'assets/icons/sensitivity_feature_3.svg';
|
||||
static const String sensitivityFeature4 = 'assets/icons/sensitivity_feature_4.svg';
|
||||
static const String sensitivityFeature5 = 'assets/icons/sensitivity_feature_5.svg';
|
||||
static const String sensitivityFeature6 = 'assets/icons/sensitivity_feature_6.svg';
|
||||
static const String sensitivityFeature7 = 'assets/icons/sensitivity_feature_7.svg';
|
||||
static const String sensitivityFeature8 = 'assets/icons/sensitivity_feature_8.svg';
|
||||
static const String sensitivityFeature9 = 'assets/icons/sensitivity_feature_9.svg';
|
||||
static const String deviceTagIcon = 'assets/icons/device_tag_ic.svg';
|
||||
static const String targetConfirmTimeIcon =
|
||||
'assets/icons/target_confirm_time_icon.svg';
|
||||
@ -489,13 +453,10 @@ class Assets {
|
||||
static const String indentLevelIcon = 'assets/icons/indent_level_icon.svg';
|
||||
static const String triggerLevelIcon = 'assets/icons/trigger_level_icon.svg';
|
||||
static const String blankCalendar = 'assets/icons/blank_calendar.svg';
|
||||
static const String refreshStatusIcon =
|
||||
'assets/icons/refresh_status_icon.svg';
|
||||
static const String energyConsumedIcon =
|
||||
'assets/icons/energy_consumed_icon.svg';
|
||||
static const String refreshStatusIcon = 'assets/icons/refresh_status_icon.svg';
|
||||
static const String energyConsumedIcon = 'assets/icons/energy_consumed_icon.svg';
|
||||
|
||||
static const String closeSettingsIcon =
|
||||
'assets/icons/close_settings_icon.svg';
|
||||
static const String closeSettingsIcon = 'assets/icons/close_settings_icon.svg';
|
||||
|
||||
static const String editNameIconSettings =
|
||||
'assets/icons/edit_name_icon_settings.svg';
|
||||
@ -508,4 +469,11 @@ class Assets {
|
||||
static const String humidityAqiSidebar = 'assets/icons/humidity.svg';
|
||||
static const String autocadOccupancyImage =
|
||||
'assets/images/autocad_occupancy_image.png';
|
||||
static const String emptyBarredChart = 'assets/icons/empty_barred_chart.svg';
|
||||
static const String emptyEnergyManagementChart =
|
||||
'assets/icons/empty_energy_management_chart.svg';
|
||||
static const String emptyEnergyManagementPerDevice =
|
||||
'assets/icons/empty_energy_management_per_device.svg';
|
||||
static const String emptyHeatmap = 'assets/icons/empty_heatmap.svg';
|
||||
static const String emptyRangeOfAqi = 'assets/icons/empty_range_of_aqi.svg';
|
||||
}
|
||||
|
||||
@ -52,4 +52,7 @@ final myTheme = ThemeData(
|
||||
borderRadius: BorderRadius.circular(4),
|
||||
),
|
||||
),
|
||||
dialogTheme: const DialogThemeData(
|
||||
backgroundColor: ColorsManager.whiteColors,
|
||||
),
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user