mirror of
https://github.com/SyncrowIOT/web.git
synced 2025-11-26 12:44:55 +00:00
Compare commits
19 Commits
SP-1713-Im
...
SP-1711-FE
| Author | SHA1 | Date | |
|---|---|---|---|
| 5a2299ea2f | |||
| 90f8305aa1 | |||
| 329b2ba472 | |||
| 0fb9149613 | |||
| 87b45fff1d | |||
| 95ae50d12d | |||
| 479aa4a091 | |||
| 03a6c5474b | |||
| 5f30a5a61b | |||
| 0712e6d64b | |||
| 1f82e84115 | |||
| 23c3bf11f9 | |||
| 5201a65a97 | |||
| e4cc5fce50 | |||
| 8dea89db0e | |||
| ad5ada9d55 | |||
| 7172a0e3fb | |||
| 78898968e8 | |||
| 666c64231f |
@ -38,9 +38,9 @@ class RangeOfAqiValue extends Equatable {
|
|||||||
factory RangeOfAqiValue.fromJson(Map<String, dynamic> json) {
|
factory RangeOfAqiValue.fromJson(Map<String, dynamic> json) {
|
||||||
return RangeOfAqiValue(
|
return RangeOfAqiValue(
|
||||||
type: json['type'] as String,
|
type: json['type'] as String,
|
||||||
min: (json['min'] as num).toDouble(),
|
min: (json['min'] as num? ?? 0).toDouble(),
|
||||||
average: (json['average'] as num).toDouble(),
|
average: (json['average'] as num? ?? 0).toDouble(),
|
||||||
max: (json['max'] as num).toDouble(),
|
max: (json['max'] as num? ?? 0).toDouble(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -24,11 +24,13 @@ abstract final class FetchAirQualityDataHelper {
|
|||||||
}) {
|
}) {
|
||||||
final date = context.read<AnalyticsDatePickerBloc>().state.monthlyDate;
|
final date = context.read<AnalyticsDatePickerBloc>().state.monthlyDate;
|
||||||
final aqiType = context.read<AirQualityDistributionBloc>().state.selectedAqiType;
|
final aqiType = context.read<AirQualityDistributionBloc>().state.selectedAqiType;
|
||||||
loadAnalyticsDevices(
|
if (shouldFetchAnalyticsDevices) {
|
||||||
context,
|
loadAnalyticsDevices(
|
||||||
communityUuid: communityUuid,
|
context,
|
||||||
spaceUuid: spaceUuid,
|
communityUuid: communityUuid,
|
||||||
);
|
spaceUuid: spaceUuid,
|
||||||
|
);
|
||||||
|
}
|
||||||
loadRangeOfAqi(
|
loadRangeOfAqi(
|
||||||
context,
|
context,
|
||||||
spaceUuid: spaceUuid,
|
spaceUuid: spaceUuid,
|
||||||
|
|||||||
@ -23,6 +23,7 @@ abstract final class RangeOfAqiChartsHelper {
|
|||||||
return titlesData.copyWith(
|
return titlesData.copyWith(
|
||||||
bottomTitles: titlesData.bottomTitles.copyWith(
|
bottomTitles: titlesData.bottomTitles.copyWith(
|
||||||
sideTitles: titlesData.bottomTitles.sideTitles.copyWith(
|
sideTitles: titlesData.bottomTitles.sideTitles.copyWith(
|
||||||
|
reservedSize: 36,
|
||||||
getTitlesWidget: (value, meta) => Padding(
|
getTitlesWidget: (value, meta) => Padding(
|
||||||
padding: const EdgeInsetsDirectional.only(top: 20.0),
|
padding: const EdgeInsetsDirectional.only(top: 20.0),
|
||||||
child: Text(
|
child: Text(
|
||||||
@ -40,6 +41,7 @@ abstract final class RangeOfAqiChartsHelper {
|
|||||||
reservedSize: 70,
|
reservedSize: 70,
|
||||||
interval: 50,
|
interval: 50,
|
||||||
maxIncluded: false,
|
maxIncluded: false,
|
||||||
|
minIncluded: true,
|
||||||
getTitlesWidget: (value, meta) {
|
getTitlesWidget: (value, meta) {
|
||||||
final text = value >= 300 ? '301+' : value.toInt().toString();
|
final text = value >= 300 ? '301+' : value.toInt().toString();
|
||||||
return Padding(
|
return Padding(
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:syncrow_web/pages/analytics/modules/air_quality/widgets/air_quality_end_side_widget.dart';
|
import 'package:syncrow_web/pages/analytics/modules/air_quality/widgets/air_quality_end_side_widget.dart';
|
||||||
import 'package:syncrow_web/pages/analytics/modules/air_quality/widgets/aqi_distribution_chart_box.dart';
|
import 'package:syncrow_web/pages/analytics/modules/air_quality/widgets/aqi_distribution_chart_box.dart';
|
||||||
|
import 'package:syncrow_web/pages/analytics/modules/air_quality/widgets/aqi_legend.dart';
|
||||||
import 'package:syncrow_web/pages/analytics/modules/air_quality/widgets/range_of_aqi_chart_box.dart';
|
import 'package:syncrow_web/pages/analytics/modules/air_quality/widgets/range_of_aqi_chart_box.dart';
|
||||||
|
|
||||||
class AirQualityView extends StatelessWidget {
|
class AirQualityView extends StatelessWidget {
|
||||||
@ -20,6 +21,10 @@ class AirQualityView extends StatelessWidget {
|
|||||||
child: Column(
|
child: Column(
|
||||||
spacing: 32,
|
spacing: 32,
|
||||||
children: [
|
children: [
|
||||||
|
SizedBox(
|
||||||
|
height: height * 0.1,
|
||||||
|
child: const AqiLegend(),
|
||||||
|
),
|
||||||
SizedBox(
|
SizedBox(
|
||||||
height: height * 1.2,
|
height: height * 1.2,
|
||||||
child: const AirQualityEndSideWidget(),
|
child: const AirQualityEndSideWidget(),
|
||||||
@ -40,7 +45,7 @@ class AirQualityView extends StatelessWidget {
|
|||||||
return SingleChildScrollView(
|
return SingleChildScrollView(
|
||||||
child: Container(
|
child: Container(
|
||||||
padding: _padding,
|
padding: _padding,
|
||||||
height: height * 1.1,
|
height: height * 1.2,
|
||||||
child: const Column(
|
child: const Column(
|
||||||
children: [
|
children: [
|
||||||
Expanded(
|
Expanded(
|
||||||
@ -52,8 +57,9 @@ class AirQualityView extends StatelessWidget {
|
|||||||
child: Column(
|
child: Column(
|
||||||
spacing: 20,
|
spacing: 20,
|
||||||
children: [
|
children: [
|
||||||
Expanded(child: RangeOfAqiChartBox()),
|
Expanded(flex: 2, child: AqiLegend()),
|
||||||
Expanded(child: AqiDistributionChartBox()),
|
Expanded(flex: 12, child: RangeOfAqiChartBox()),
|
||||||
|
Expanded(flex: 12, child: AqiDistributionChartBox()),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|||||||
@ -32,8 +32,13 @@ class AqiDistributionChart extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
List<BarChartGroupData> _buildBarGroups() {
|
List<BarChartGroupData> _buildBarGroups() {
|
||||||
return List.generate(chartData.length, (index) {
|
final groups = <BarChartGroupData>[];
|
||||||
final data = chartData[index];
|
for (var i = 0; i < chartData.length; i++) {
|
||||||
|
final data = chartData[i];
|
||||||
|
final isAllZero = data.data.every((d) => d.percentage == 0);
|
||||||
|
if (isAllZero) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
final stackItems = <BarChartRodData>[];
|
final stackItems = <BarChartRodData>[];
|
||||||
double currentY = 0;
|
double currentY = 0;
|
||||||
var isFirstElement = true;
|
var isFirstElement = true;
|
||||||
@ -56,13 +61,15 @@ class AqiDistributionChart extends StatelessWidget {
|
|||||||
currentY += percentageData.percentage + _rodStackItemsSpacing;
|
currentY += percentageData.percentage + _rodStackItemsSpacing;
|
||||||
isFirstElement = false;
|
isFirstElement = false;
|
||||||
}
|
}
|
||||||
|
groups.add(
|
||||||
return BarChartGroupData(
|
BarChartGroupData(
|
||||||
x: index,
|
x: i,
|
||||||
barRods: stackItems,
|
barRods: stackItems,
|
||||||
groupVertically: true,
|
groupVertically: true,
|
||||||
|
),
|
||||||
);
|
);
|
||||||
});
|
}
|
||||||
|
return groups;
|
||||||
}
|
}
|
||||||
|
|
||||||
BarTouchData _barTouchData(BuildContext context) {
|
BarTouchData _barTouchData(BuildContext context) {
|
||||||
@ -73,6 +80,7 @@ class AqiDistributionChart extends StatelessWidget {
|
|||||||
color: ColorsManager.semiTransparentBlack,
|
color: ColorsManager.semiTransparentBlack,
|
||||||
),
|
),
|
||||||
tooltipRoundedRadius: 16,
|
tooltipRoundedRadius: 16,
|
||||||
|
maxContentWidth: 500,
|
||||||
tooltipPadding: const EdgeInsets.all(8),
|
tooltipPadding: const EdgeInsets.all(8),
|
||||||
getTooltipItem: (group, groupIndex, rod, rodIndex) {
|
getTooltipItem: (group, groupIndex, rod, rodIndex) {
|
||||||
final data = chartData[group.x];
|
final data = chartData[group.x];
|
||||||
@ -81,10 +89,13 @@ class AqiDistributionChart extends StatelessWidget {
|
|||||||
|
|
||||||
final textStyle = context.textTheme.bodySmall?.copyWith(
|
final textStyle = context.textTheme.bodySmall?.copyWith(
|
||||||
color: ColorsManager.blackColor,
|
color: ColorsManager.blackColor,
|
||||||
fontSize: 8,
|
fontSize: 11,
|
||||||
);
|
);
|
||||||
|
|
||||||
for (final percentageData in data.data) {
|
for (final percentageData in data.data) {
|
||||||
|
if (percentageData.percentage == 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
final percentage = percentageData.percentage.toStringAsFixed(1);
|
final percentage = percentageData.percentage.toStringAsFixed(1);
|
||||||
final type = percentageData.type[0].toUpperCase() +
|
final type = percentageData.type[0].toUpperCase() +
|
||||||
percentageData.type.substring(1).replaceAll('_', ' ');
|
percentageData.type.substring(1).replaceAll('_', ' ');
|
||||||
@ -98,7 +109,7 @@ class AqiDistributionChart extends StatelessWidget {
|
|||||||
DateFormat('dd/MM/yyyy').format(data.date),
|
DateFormat('dd/MM/yyyy').format(data.date),
|
||||||
context.textTheme.bodyMedium!.copyWith(
|
context.textTheme.bodyMedium!.copyWith(
|
||||||
color: ColorsManager.blackColor,
|
color: ColorsManager.blackColor,
|
||||||
fontSize: 9,
|
fontSize: 12,
|
||||||
fontWeight: FontWeight.w600,
|
fontWeight: FontWeight.w600,
|
||||||
),
|
),
|
||||||
textAlign: TextAlign.start,
|
textAlign: TextAlign.start,
|
||||||
@ -118,7 +129,6 @@ class AqiDistributionChart extends StatelessWidget {
|
|||||||
final leftTitles = titlesData.leftTitles.copyWith(
|
final leftTitles = titlesData.leftTitles.copyWith(
|
||||||
sideTitles: titlesData.leftTitles.sideTitles.copyWith(
|
sideTitles: titlesData.leftTitles.sideTitles.copyWith(
|
||||||
reservedSize: 70,
|
reservedSize: 70,
|
||||||
interval: 20,
|
|
||||||
maxIncluded: false,
|
maxIncluded: false,
|
||||||
minIncluded: true,
|
minIncluded: true,
|
||||||
getTitlesWidget: (value, meta) => Padding(
|
getTitlesWidget: (value, meta) => Padding(
|
||||||
@ -140,7 +150,7 @@ class AqiDistributionChart extends StatelessWidget {
|
|||||||
|
|
||||||
final bottomTitles = AxisTitles(
|
final bottomTitles = AxisTitles(
|
||||||
sideTitles: SideTitles(
|
sideTitles: SideTitles(
|
||||||
showTitles: true,
|
showTitles: chartData.isNotEmpty,
|
||||||
getTitlesWidget: (value, _) => FittedBox(
|
getTitlesWidget: (value, _) => FittedBox(
|
||||||
alignment: AlignmentDirectional.bottomCenter,
|
alignment: AlignmentDirectional.bottomCenter,
|
||||||
fit: BoxFit.scaleDown,
|
fit: BoxFit.scaleDown,
|
||||||
@ -148,7 +158,7 @@ class AqiDistributionChart extends StatelessWidget {
|
|||||||
chartData[value.toInt()].date.day.toString(),
|
chartData[value.toInt()].date.day.toString(),
|
||||||
style: context.textTheme.bodySmall?.copyWith(
|
style: context.textTheme.bodySmall?.copyWith(
|
||||||
color: ColorsManager.lightGreyColor,
|
color: ColorsManager.lightGreyColor,
|
||||||
fontSize: 8,
|
fontSize: 12,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|||||||
@ -19,7 +19,7 @@ class AqiDistributionChartTitle extends StatelessWidget {
|
|||||||
children: [
|
children: [
|
||||||
ChartsLoadingWidget(isLoading: isLoading),
|
ChartsLoadingWidget(isLoading: isLoading),
|
||||||
const Expanded(
|
const Expanded(
|
||||||
flex: 3,
|
flex: 4,
|
||||||
child: FittedBox(
|
child: FittedBox(
|
||||||
fit: BoxFit.scaleDown,
|
fit: BoxFit.scaleDown,
|
||||||
alignment: AlignmentDirectional.centerStart,
|
alignment: AlignmentDirectional.centerStart,
|
||||||
@ -28,23 +28,26 @@ class AqiDistributionChartTitle extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
FittedBox(
|
Expanded(
|
||||||
alignment: AlignmentDirectional.centerEnd,
|
flex: 2,
|
||||||
fit: BoxFit.scaleDown,
|
child: FittedBox(
|
||||||
child: AqiTypeDropdown(
|
alignment: AlignmentDirectional.centerEnd,
|
||||||
onChanged: (value) {
|
fit: BoxFit.scaleDown,
|
||||||
if (value != null) {
|
child: AqiTypeDropdown(
|
||||||
final bloc = context.read<AirQualityDistributionBloc>();
|
onChanged: (value) {
|
||||||
try {
|
if (value != null) {
|
||||||
final param = _makeLoadAqiDistributionParam(context, value);
|
final bloc = context.read<AirQualityDistributionBloc>();
|
||||||
bloc.add(LoadAirQualityDistribution(param));
|
try {
|
||||||
} catch (_) {
|
final param = _makeLoadAqiDistributionParam(context, value);
|
||||||
return;
|
bloc.add(LoadAirQualityDistribution(param));
|
||||||
} finally {
|
} catch (_) {
|
||||||
bloc.add(UpdateAqiTypeEvent(value));
|
return;
|
||||||
|
} finally {
|
||||||
|
bloc.add(UpdateAqiTypeEvent(value));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
},
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
|||||||
@ -0,0 +1,38 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:syncrow_web/pages/analytics/modules/air_quality/helpers/range_of_aqi_charts_helper.dart';
|
||||||
|
import 'package:syncrow_web/pages/analytics/modules/analytics/widgets/chart_informative_cell.dart';
|
||||||
|
import 'package:syncrow_web/utils/style.dart';
|
||||||
|
|
||||||
|
class AqiLegend extends StatelessWidget {
|
||||||
|
const AqiLegend({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Container(
|
||||||
|
padding: const EdgeInsetsDirectional.all(20),
|
||||||
|
decoration: subSectionContainerDecoration.copyWith(
|
||||||
|
borderRadius: BorderRadius.circular(20),
|
||||||
|
),
|
||||||
|
child: Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
spacing: 16,
|
||||||
|
children: RangeOfAqiChartsHelper.gradientData.map((e) {
|
||||||
|
return Flexible(
|
||||||
|
flex: 4,
|
||||||
|
child: FittedBox(
|
||||||
|
fit: BoxFit.fill,
|
||||||
|
child: ChartInformativeCell(
|
||||||
|
color: e.$1,
|
||||||
|
title: FittedBox(
|
||||||
|
fit: BoxFit.fill,
|
||||||
|
child: Text(e.$2),
|
||||||
|
),
|
||||||
|
height: null,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}).toList(),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -47,36 +47,37 @@ class AqiLocationInfoCell extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
Align(
|
Align(
|
||||||
alignment: AlignmentDirectional.bottomEnd,
|
alignment: AlignmentDirectional.bottomCenter,
|
||||||
child: Padding(
|
child: Row(
|
||||||
padding: const EdgeInsetsDirectional.all(10),
|
crossAxisAlignment: CrossAxisAlignment.end,
|
||||||
child: SizedBox(
|
children: [
|
||||||
height: 40,
|
Expanded(
|
||||||
width: 120,
|
child: SvgPicture.asset(
|
||||||
child: FittedBox(
|
svgPath,
|
||||||
fit: BoxFit.scaleDown,
|
fit: BoxFit.scaleDown,
|
||||||
alignment: AlignmentDirectional.bottomEnd,
|
alignment: AlignmentDirectional.bottomStart,
|
||||||
child: Text(
|
),
|
||||||
value,
|
),
|
||||||
style: context.textTheme.bodySmall?.copyWith(
|
Expanded(
|
||||||
color: ColorsManager.vividBlue.withValues(alpha: 0.7),
|
child: FittedBox(
|
||||||
fontWeight: FontWeight.w700,
|
fit: BoxFit.scaleDown,
|
||||||
fontSize: 24,
|
alignment: AlignmentDirectional.bottomEnd,
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsetsDirectional.all(10),
|
||||||
|
child: Text(
|
||||||
|
value,
|
||||||
|
style: context.textTheme.bodySmall?.copyWith(
|
||||||
|
color: ColorsManager.vividBlue.withValues(
|
||||||
|
alpha: 0.7,
|
||||||
|
),
|
||||||
|
fontWeight: FontWeight.w700,
|
||||||
|
fontSize: 24,
|
||||||
|
),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
],
|
||||||
),
|
|
||||||
),
|
|
||||||
Align(
|
|
||||||
alignment: AlignmentDirectional.bottomStart,
|
|
||||||
child: SizedBox.square(
|
|
||||||
dimension: MediaQuery.sizeOf(context).width * 0.45,
|
|
||||||
child: FittedBox(
|
|
||||||
fit: BoxFit.scaleDown,
|
|
||||||
alignment: AlignmentDirectional.bottomStart,
|
|
||||||
child: SvgPicture.asset(svgPath),
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
|||||||
@ -7,16 +7,18 @@ class ChartInformativeCell extends StatelessWidget {
|
|||||||
required this.title,
|
required this.title,
|
||||||
required this.color,
|
required this.color,
|
||||||
this.hasBorder = false,
|
this.hasBorder = false,
|
||||||
|
this.height,
|
||||||
});
|
});
|
||||||
|
|
||||||
final Widget title;
|
final Widget title;
|
||||||
final Color color;
|
final Color color;
|
||||||
final bool hasBorder;
|
final bool hasBorder;
|
||||||
|
final double? height;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Container(
|
return Container(
|
||||||
height: MediaQuery.sizeOf(context).height * 0.0385,
|
height: height ?? MediaQuery.sizeOf(context).height * 0.0385,
|
||||||
padding: const EdgeInsetsDirectional.symmetric(
|
padding: const EdgeInsetsDirectional.symmetric(
|
||||||
vertical: 8,
|
vertical: 8,
|
||||||
horizontal: 12,
|
horizontal: 12,
|
||||||
|
|||||||
@ -179,31 +179,36 @@ class _DynamicTableState extends State<DynamicTable> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildEmptyState() => Column(
|
Widget _buildEmptyState() => Container(
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
height: widget.size.height,
|
||||||
children: [
|
color: ColorsManager.whiteColors,
|
||||||
Row(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
children: [
|
||||||
children: [
|
Row(
|
||||||
Column(
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
children: [
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
SvgPicture.asset(Assets.emptyTable),
|
children: [
|
||||||
const SizedBox(height: 15),
|
Column(
|
||||||
Text(
|
children: [
|
||||||
widget.tableName == 'AccessManagement'
|
SvgPicture.asset(Assets.emptyTable),
|
||||||
? 'No Password '
|
const SizedBox(height: 15),
|
||||||
: 'No Devices',
|
Text(
|
||||||
style: Theme.of(context)
|
widget.tableName == 'AccessManagement'
|
||||||
.textTheme
|
? 'No Password '
|
||||||
.bodySmall!
|
: 'No Devices',
|
||||||
.copyWith(color: ColorsManager.grayColor),
|
style: Theme.of(context)
|
||||||
)
|
.textTheme
|
||||||
],
|
.bodySmall!
|
||||||
),
|
.copyWith(color: ColorsManager.grayColor),
|
||||||
],
|
)
|
||||||
),
|
],
|
||||||
],
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
SizedBox(height: widget.size.height * 0.5),
|
||||||
|
],
|
||||||
|
),
|
||||||
);
|
);
|
||||||
Widget _buildSelectAllCheckbox() {
|
Widget _buildSelectAllCheckbox() {
|
||||||
return Container(
|
return Container(
|
||||||
|
|||||||
@ -7,21 +7,22 @@ class SpacesConnectionsArrowPainter extends CustomPainter {
|
|||||||
final Map<String, Offset> positions;
|
final Map<String, Offset> positions;
|
||||||
final double cardWidth = 150.0;
|
final double cardWidth = 150.0;
|
||||||
final double cardHeight = 90.0;
|
final double cardHeight = 90.0;
|
||||||
final String? selectedSpaceUuid;
|
final Set<String> highlightedUuids;
|
||||||
|
|
||||||
SpacesConnectionsArrowPainter({
|
SpacesConnectionsArrowPainter({
|
||||||
required this.connections,
|
required this.connections,
|
||||||
required this.positions,
|
required this.positions,
|
||||||
this.selectedSpaceUuid,
|
required this.highlightedUuids,
|
||||||
});
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void paint(Canvas canvas, Size size) {
|
void paint(Canvas canvas, Size size) {
|
||||||
for (final connection in connections) {
|
for (final connection in connections) {
|
||||||
final isSelected = connection.to == selectedSpaceUuid;
|
final isSelected = highlightedUuids.contains(connection.from) ||
|
||||||
|
highlightedUuids.contains(connection.to);
|
||||||
final paint = Paint()
|
final paint = Paint()
|
||||||
..color = isSelected
|
..color = isSelected
|
||||||
? ColorsManager.primaryColor
|
? ColorsManager.blackColor
|
||||||
: ColorsManager.blackColor.withValues(alpha: 0.5)
|
: ColorsManager.blackColor.withValues(alpha: 0.5)
|
||||||
..strokeWidth = 2.0
|
..strokeWidth = 2.0
|
||||||
..style = PaintingStyle.stroke;
|
..style = PaintingStyle.stroke;
|
||||||
@ -36,7 +37,7 @@ class SpacesConnectionsArrowPainter extends CustomPainter {
|
|||||||
|
|
||||||
final path = Path()..moveTo(startPoint.dx, startPoint.dy);
|
final path = Path()..moveTo(startPoint.dx, startPoint.dy);
|
||||||
|
|
||||||
final controlPoint1 = Offset(startPoint.dx, startPoint.dy + 60);
|
final controlPoint1 = Offset(startPoint.dx, startPoint.dy + 20);
|
||||||
final controlPoint2 = Offset(endPoint.dx, endPoint.dy - 60);
|
final controlPoint2 = Offset(endPoint.dx, endPoint.dy - 60);
|
||||||
|
|
||||||
path.cubicTo(controlPoint1.dx, controlPoint1.dy, controlPoint2.dx,
|
path.cubicTo(controlPoint1.dx, controlPoint1.dy, controlPoint2.dx,
|
||||||
@ -46,7 +47,7 @@ class SpacesConnectionsArrowPainter extends CustomPainter {
|
|||||||
|
|
||||||
final circlePaint = Paint()
|
final circlePaint = Paint()
|
||||||
..color = isSelected
|
..color = isSelected
|
||||||
? ColorsManager.primaryColor
|
? ColorsManager.blackColor
|
||||||
: ColorsManager.blackColor.withValues(alpha: 0.5)
|
: ColorsManager.blackColor.withValues(alpha: 0.5)
|
||||||
..style = PaintingStyle.fill
|
..style = PaintingStyle.fill
|
||||||
..blendMode = BlendMode.srcIn;
|
..blendMode = BlendMode.srcIn;
|
||||||
|
|||||||
@ -1,21 +1,26 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
import 'package:syncrow_web/pages/space_management_v2/main_module/models/space_connection_model.dart';
|
import 'package:syncrow_web/pages/space_management_v2/main_module/models/space_connection_model.dart';
|
||||||
import 'package:syncrow_web/pages/space_management_v2/main_module/painters/spaces_connections_arrow_painter.dart';
|
import 'package:syncrow_web/pages/space_management_v2/main_module/painters/spaces_connections_arrow_painter.dart';
|
||||||
import 'package:syncrow_web/pages/space_management_v2/main_module/widgets/space_card_widget.dart';
|
import 'package:syncrow_web/pages/space_management_v2/main_module/widgets/space_card_widget.dart';
|
||||||
import 'package:syncrow_web/pages/space_management_v2/main_module/widgets/space_cell.dart';
|
import 'package:syncrow_web/pages/space_management_v2/main_module/widgets/space_cell.dart';
|
||||||
import 'package:syncrow_web/pages/space_management_v2/modules/communities/domain/models/community_model.dart';
|
import 'package:syncrow_web/pages/space_management_v2/modules/communities/domain/models/community_model.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/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/presentation/helpers/space_details_dialog_helper.dart';
|
||||||
|
|
||||||
class CommunityStructureCanvas extends StatefulWidget {
|
class CommunityStructureCanvas extends StatefulWidget {
|
||||||
const CommunityStructureCanvas({
|
const CommunityStructureCanvas({
|
||||||
required this.community,
|
required this.community,
|
||||||
|
required this.selectedSpace,
|
||||||
super.key,
|
super.key,
|
||||||
});
|
});
|
||||||
|
|
||||||
final CommunityModel community;
|
final CommunityModel community;
|
||||||
|
final SpaceModel? selectedSpace;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<CommunityStructureCanvas> createState() =>_CommunityStructureCanvasState();
|
State<CommunityStructureCanvas> createState() => _CommunityStructureCanvasState();
|
||||||
}
|
}
|
||||||
|
|
||||||
class _CommunityStructureCanvasState extends State<CommunityStructureCanvas>
|
class _CommunityStructureCanvasState extends State<CommunityStructureCanvas>
|
||||||
@ -25,19 +30,30 @@ class _CommunityStructureCanvasState extends State<CommunityStructureCanvas>
|
|||||||
final double _cardHeight = 90.0;
|
final double _cardHeight = 90.0;
|
||||||
final double _horizontalSpacing = 150.0;
|
final double _horizontalSpacing = 150.0;
|
||||||
final double _verticalSpacing = 120.0;
|
final double _verticalSpacing = 120.0;
|
||||||
String? _selectedSpaceUuid;
|
|
||||||
|
|
||||||
late TransformationController _transformationController;
|
late TransformationController _transformationController;
|
||||||
late AnimationController _animationController;
|
late AnimationController _animationController;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
|
||||||
_transformationController = TransformationController();
|
_transformationController = TransformationController();
|
||||||
_animationController = AnimationController(
|
_animationController = AnimationController(
|
||||||
vsync: this,
|
vsync: this,
|
||||||
duration: const Duration(milliseconds: 100),
|
duration: const Duration(milliseconds: 150),
|
||||||
);
|
);
|
||||||
|
super.initState();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void didUpdateWidget(covariant CommunityStructureCanvas oldWidget) {
|
||||||
|
super.didUpdateWidget(oldWidget);
|
||||||
|
if (widget.selectedSpace?.uuid != oldWidget.selectedSpace?.uuid) {
|
||||||
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||||
|
if (mounted) {
|
||||||
|
_animateToSpace(widget.selectedSpace);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -47,6 +63,15 @@ class _CommunityStructureCanvasState extends State<CommunityStructureCanvas>
|
|||||||
super.dispose();
|
super.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Set<String> _getAllDescendantUuids(SpaceModel space) {
|
||||||
|
final uuids = <String>{};
|
||||||
|
for (final child in space.children) {
|
||||||
|
uuids.add(child.uuid);
|
||||||
|
uuids.addAll(_getAllDescendantUuids(child));
|
||||||
|
}
|
||||||
|
return uuids;
|
||||||
|
}
|
||||||
|
|
||||||
void _runAnimation(Matrix4 target) {
|
void _runAnimation(Matrix4 target) {
|
||||||
final animation = Matrix4Tween(
|
final animation = Matrix4Tween(
|
||||||
begin: _transformationController.value,
|
begin: _transformationController.value,
|
||||||
@ -63,15 +88,16 @@ class _CommunityStructureCanvasState extends State<CommunityStructureCanvas>
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void _onSpaceTapped(String spaceUuid) {
|
void _animateToSpace(SpaceModel? space) {
|
||||||
setState(() {
|
if (space == null) {
|
||||||
_selectedSpaceUuid = spaceUuid;
|
_runAnimation(Matrix4.identity());
|
||||||
});
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
final position = _positions[spaceUuid];
|
final position = _positions[space.uuid];
|
||||||
if (position == null) return;
|
if (position == null) return;
|
||||||
|
|
||||||
const scale = 2.0;
|
const scale = 1.5;
|
||||||
final viewSize = context.size;
|
final viewSize = context.size;
|
||||||
if (viewSize == null) return;
|
if (viewSize == null) return;
|
||||||
|
|
||||||
@ -86,11 +112,19 @@ class _CommunityStructureCanvasState extends State<CommunityStructureCanvas>
|
|||||||
_runAnimation(matrix);
|
_runAnimation(matrix);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void _onSpaceTapped(SpaceModel? space) {
|
||||||
|
context.read<CommunitiesTreeSelectionBloc>().add(
|
||||||
|
SelectSpaceEvent(community: widget.community, space: space),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
void _resetSelectionAndZoom() {
|
void _resetSelectionAndZoom() {
|
||||||
setState(() {
|
context.read<CommunitiesTreeSelectionBloc>().add(
|
||||||
_selectedSpaceUuid = null;
|
SelectSpaceEvent(
|
||||||
});
|
community: widget.community,
|
||||||
_runAnimation(Matrix4.identity());
|
space: null,
|
||||||
|
),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
void _calculateLayout(
|
void _calculateLayout(
|
||||||
@ -150,16 +184,23 @@ class _CommunityStructureCanvasState extends State<CommunityStructureCanvas>
|
|||||||
|
|
||||||
_calculateLayout(community.spaces, 0, {});
|
_calculateLayout(community.spaces, 0, {});
|
||||||
|
|
||||||
|
final selectedSpace = widget.selectedSpace;
|
||||||
|
final highlightedUuids = <String>{};
|
||||||
|
if (selectedSpace != null) {
|
||||||
|
highlightedUuids.add(selectedSpace.uuid);
|
||||||
|
highlightedUuids.addAll(_getAllDescendantUuids(selectedSpace));
|
||||||
|
}
|
||||||
|
|
||||||
final widgets = <Widget>[];
|
final widgets = <Widget>[];
|
||||||
final connections = <SpaceConnectionModel>[];
|
final connections = <SpaceConnectionModel>[];
|
||||||
_generateWidgets(community.spaces, widgets, connections);
|
_generateWidgets(community.spaces, widgets, connections, highlightedUuids);
|
||||||
|
|
||||||
return [
|
return [
|
||||||
CustomPaint(
|
CustomPaint(
|
||||||
painter: SpacesConnectionsArrowPainter(
|
painter: SpacesConnectionsArrowPainter(
|
||||||
connections: connections,
|
connections: connections,
|
||||||
positions: _positions,
|
positions: _positions,
|
||||||
selectedSpaceUuid: _selectedSpaceUuid,
|
highlightedUuids: highlightedUuids,
|
||||||
),
|
),
|
||||||
child: Stack(alignment: AlignmentDirectional.center, children: widgets),
|
child: Stack(alignment: AlignmentDirectional.center, children: widgets),
|
||||||
),
|
),
|
||||||
@ -170,11 +211,15 @@ class _CommunityStructureCanvasState extends State<CommunityStructureCanvas>
|
|||||||
List<SpaceModel> spaces,
|
List<SpaceModel> spaces,
|
||||||
List<Widget> widgets,
|
List<Widget> widgets,
|
||||||
List<SpaceConnectionModel> connections,
|
List<SpaceConnectionModel> connections,
|
||||||
|
Set<String> highlightedUuids,
|
||||||
) {
|
) {
|
||||||
for (final space in spaces) {
|
for (final space in spaces) {
|
||||||
final position = _positions[space.uuid];
|
final position = _positions[space.uuid];
|
||||||
if (position == null) continue;
|
if (position == null) continue;
|
||||||
|
|
||||||
|
final isHighlighted = highlightedUuids.contains(space.uuid);
|
||||||
|
final hasNoSelectedSpace = widget.selectedSpace == null;
|
||||||
|
|
||||||
widgets.add(
|
widgets.add(
|
||||||
Positioned(
|
Positioned(
|
||||||
left: position.dx,
|
left: position.dx,
|
||||||
@ -182,32 +227,31 @@ class _CommunityStructureCanvasState extends State<CommunityStructureCanvas>
|
|||||||
width: _cardWidth,
|
width: _cardWidth,
|
||||||
height: _cardHeight,
|
height: _cardHeight,
|
||||||
child: SpaceCardWidget(
|
child: SpaceCardWidget(
|
||||||
index: spaces.indexOf(space),
|
buildSpaceContainer: () {
|
||||||
onPositionChanged: (newPosition) {},
|
|
||||||
buildSpaceContainer: (index) {
|
|
||||||
return Opacity(
|
return Opacity(
|
||||||
opacity: 1.0,
|
opacity: hasNoSelectedSpace || isHighlighted ? 1.0 : 0.5,
|
||||||
child: SpaceCell(
|
child: Tooltip(
|
||||||
index: index,
|
message: space.spaceName,
|
||||||
onTap: () => _onSpaceTapped(space.uuid),
|
preferBelow: false,
|
||||||
icon: space.icon,
|
child: SpaceCell(
|
||||||
name: space.spaceName,
|
onTap: () => _onSpaceTapped(space),
|
||||||
|
icon: space.icon,
|
||||||
|
name: space.spaceName,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
screenSize: MediaQuery.sizeOf(context),
|
onTap: () => SpaceDetailsDialogHelper.showCreate(context),
|
||||||
position: position,
|
|
||||||
isHovered: false,
|
|
||||||
onHoverChanged: (int index, bool isHovered) {},
|
|
||||||
onButtonTap: (int index, Offset newPosition) {},
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
for (final child in space.children) {
|
for (final child in space.children) {
|
||||||
connections.add(SpaceConnectionModel(from: space.uuid, to: child.uuid));
|
connections.add(
|
||||||
|
SpaceConnectionModel(from: space.uuid, to: child.uuid),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
_generateWidgets(space.children, widgets, connections);
|
_generateWidgets(space.children, widgets, connections, highlightedUuids);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -218,7 +262,7 @@ class _CommunityStructureCanvasState extends State<CommunityStructureCanvas>
|
|||||||
transformationController: _transformationController,
|
transformationController: _transformationController,
|
||||||
boundaryMargin: EdgeInsets.symmetric(
|
boundaryMargin: EdgeInsets.symmetric(
|
||||||
horizontal: MediaQuery.sizeOf(context).width * 0.3,
|
horizontal: MediaQuery.sizeOf(context).width * 0.3,
|
||||||
vertical: MediaQuery.sizeOf(context).height * 0.2,
|
vertical: MediaQuery.sizeOf(context).height * 0.3,
|
||||||
),
|
),
|
||||||
minScale: 0.5,
|
minScale: 0.5,
|
||||||
maxScale: 3.0,
|
maxScale: 3.0,
|
||||||
@ -226,8 +270,8 @@ class _CommunityStructureCanvasState extends State<CommunityStructureCanvas>
|
|||||||
child: GestureDetector(
|
child: GestureDetector(
|
||||||
onTap: _resetSelectionAndZoom,
|
onTap: _resetSelectionAndZoom,
|
||||||
child: SizedBox(
|
child: SizedBox(
|
||||||
width: MediaQuery.sizeOf(context).width * 2,
|
width: MediaQuery.sizeOf(context).width * 5,
|
||||||
height: MediaQuery.sizeOf(context).height * 2,
|
height: MediaQuery.sizeOf(context).height * 5,
|
||||||
child: Stack(children: treeWidgets),
|
child: Stack(children: treeWidgets),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.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/color_manager.dart';
|
||||||
|
|
||||||
class CreateSpaceButton extends StatelessWidget {
|
class CreateSpaceButton extends StatelessWidget {
|
||||||
@ -7,7 +8,7 @@ class CreateSpaceButton extends StatelessWidget {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return GestureDetector(
|
return GestureDetector(
|
||||||
onTap: () {},
|
onTap: () => SpaceDetailsDialogHelper.showCreate(context),
|
||||||
child: Container(
|
child: Container(
|
||||||
height: 60,
|
height: 60,
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
|
|||||||
@ -2,15 +2,11 @@ import 'package:flutter/material.dart';
|
|||||||
import 'package:syncrow_web/utils/color_manager.dart';
|
import 'package:syncrow_web/utils/color_manager.dart';
|
||||||
|
|
||||||
class PlusButtonWidget extends StatelessWidget {
|
class PlusButtonWidget extends StatelessWidget {
|
||||||
final int index;
|
|
||||||
final String direction;
|
|
||||||
final Offset offset;
|
final Offset offset;
|
||||||
final void Function(int index, Offset newPosition) onButtonTap;
|
final void Function() onButtonTap;
|
||||||
|
|
||||||
const PlusButtonWidget({
|
const PlusButtonWidget({
|
||||||
super.key,
|
super.key,
|
||||||
required this.index,
|
|
||||||
required this.direction,
|
|
||||||
required this.offset,
|
required this.offset,
|
||||||
required this.onButtonTap,
|
required this.onButtonTap,
|
||||||
});
|
});
|
||||||
@ -18,13 +14,7 @@ class PlusButtonWidget extends StatelessWidget {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return GestureDetector(
|
return GestureDetector(
|
||||||
onTap: () {
|
onTap: onButtonTap,
|
||||||
if (direction == 'down') {
|
|
||||||
onButtonTap(index, const Offset(0, 150));
|
|
||||||
} else {
|
|
||||||
onButtonTap(index, const Offset(150, 0));
|
|
||||||
}
|
|
||||||
},
|
|
||||||
child: Container(
|
child: Container(
|
||||||
width: 30,
|
width: 30,
|
||||||
height: 30,
|
height: 30,
|
||||||
|
|||||||
@ -1,60 +1,39 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:syncrow_web/pages/space_management_v2/main_module/widgets/plus_button_widget.dart';
|
import 'package:syncrow_web/pages/space_management_v2/main_module/widgets/plus_button_widget.dart';
|
||||||
|
|
||||||
class SpaceCardWidget extends StatelessWidget {
|
class SpaceCardWidget extends StatefulWidget {
|
||||||
final int index;
|
final void Function() onTap;
|
||||||
final Size screenSize;
|
final Widget Function() buildSpaceContainer;
|
||||||
final Offset position;
|
|
||||||
final bool isHovered;
|
|
||||||
final void Function(int index, bool isHovered) onHoverChanged;
|
|
||||||
final void Function(int index, Offset newPosition) onButtonTap;
|
|
||||||
final Widget Function(int index) buildSpaceContainer;
|
|
||||||
final ValueChanged<Offset> onPositionChanged;
|
|
||||||
|
|
||||||
const SpaceCardWidget({
|
const SpaceCardWidget({
|
||||||
super.key,
|
required this.onTap,
|
||||||
required this.index,
|
|
||||||
required this.onPositionChanged,
|
|
||||||
required this.screenSize,
|
|
||||||
required this.position,
|
|
||||||
required this.isHovered,
|
|
||||||
required this.onHoverChanged,
|
|
||||||
required this.onButtonTap,
|
|
||||||
required this.buildSpaceContainer,
|
required this.buildSpaceContainer,
|
||||||
|
super.key,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<SpaceCardWidget> createState() => _SpaceCardWidgetState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _SpaceCardWidgetState extends State<SpaceCardWidget> {
|
||||||
|
bool isHovered = false;
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return MouseRegion(
|
return MouseRegion(
|
||||||
onEnter: (_) => onHoverChanged(index, true),
|
onEnter: (_) => setState(() => isHovered = true),
|
||||||
onExit: (_) => onHoverChanged(index, false),
|
onExit: (_) => setState(() => isHovered = false),
|
||||||
child: SizedBox(
|
child: SizedBox(
|
||||||
width: 150,
|
|
||||||
height: 90,
|
|
||||||
child: Stack(
|
child: Stack(
|
||||||
clipBehavior: Clip.none,
|
clipBehavior: Clip.none,
|
||||||
alignment: Alignment.center,
|
alignment: Alignment.center,
|
||||||
children: [
|
children: [
|
||||||
buildSpaceContainer(index),
|
widget.buildSpaceContainer(),
|
||||||
|
|
||||||
if (isHovered)
|
if (isHovered)
|
||||||
Positioned(
|
Positioned(
|
||||||
bottom: 0,
|
bottom: 0,
|
||||||
child: PlusButtonWidget(
|
child: PlusButtonWidget(
|
||||||
index: index,
|
|
||||||
direction: 'down',
|
|
||||||
offset: Offset.zero,
|
offset: Offset.zero,
|
||||||
onButtonTap: onButtonTap,
|
onButtonTap: widget.onTap,
|
||||||
),
|
|
||||||
),
|
|
||||||
if (isHovered)
|
|
||||||
Positioned(
|
|
||||||
right: -15,
|
|
||||||
child: PlusButtonWidget(
|
|
||||||
index: index,
|
|
||||||
direction: 'right',
|
|
||||||
offset: Offset.zero,
|
|
||||||
onButtonTap: onButtonTap,
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
|||||||
@ -1,29 +1,23 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_svg/svg.dart';
|
import 'package:flutter_svg/svg.dart';
|
||||||
import 'package:syncrow_web/utils/color_manager.dart';
|
import 'package:syncrow_web/utils/color_manager.dart';
|
||||||
|
import 'package:syncrow_web/utils/extension/build_context_x.dart';
|
||||||
|
|
||||||
class SpaceCell extends StatelessWidget {
|
class SpaceCell extends StatelessWidget {
|
||||||
final int index;
|
|
||||||
final String icon;
|
final String icon;
|
||||||
final String name;
|
final String name;
|
||||||
final VoidCallback? onDoubleTap;
|
|
||||||
final VoidCallback? onTap;
|
final VoidCallback? onTap;
|
||||||
|
|
||||||
const SpaceCell({
|
const SpaceCell({
|
||||||
super.key,
|
super.key,
|
||||||
required this.index,
|
|
||||||
required this.icon,
|
required this.icon,
|
||||||
required this.name,
|
required this.name,
|
||||||
this.onTap,
|
required this.onTap,
|
||||||
this.onDoubleTap,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final theme = Theme.of(context);
|
|
||||||
|
|
||||||
return GestureDetector(
|
return GestureDetector(
|
||||||
onDoubleTap: onDoubleTap,
|
|
||||||
onTap: onTap,
|
onTap: onTap,
|
||||||
child: Container(
|
child: Container(
|
||||||
width: 150,
|
width: 150,
|
||||||
@ -36,7 +30,7 @@ class SpaceCell extends StatelessWidget {
|
|||||||
Expanded(
|
Expanded(
|
||||||
child: Text(
|
child: Text(
|
||||||
name,
|
name,
|
||||||
style: theme.textTheme.bodyLarge?.copyWith(
|
style: context.textTheme.bodyLarge?.copyWith(
|
||||||
fontWeight: FontWeight.bold,
|
fontWeight: FontWeight.bold,
|
||||||
color: ColorsManager.blackColor,
|
color: ColorsManager.blackColor,
|
||||||
),
|
),
|
||||||
@ -63,7 +57,10 @@ class SpaceCell extends StatelessWidget {
|
|||||||
child: Center(
|
child: Center(
|
||||||
child: SvgPicture.asset(
|
child: SvgPicture.asset(
|
||||||
icon,
|
icon,
|
||||||
color: ColorsManager.whiteColors,
|
colorFilter: const ColorFilter.mode(
|
||||||
|
ColorsManager.whiteColors,
|
||||||
|
BlendMode.srcIn,
|
||||||
|
),
|
||||||
width: 24,
|
width: 24,
|
||||||
height: 24,
|
height: 24,
|
||||||
),
|
),
|
||||||
|
|||||||
@ -9,14 +9,19 @@ class SpaceManagementCommunityStructure extends StatelessWidget {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final selectedCommunity =
|
final selectionBloc = context.watch<CommunitiesTreeSelectionBloc>().state;
|
||||||
context.watch<CommunitiesTreeSelectionBloc>().state.selectedCommunity!;
|
final selectedCommunity = selectionBloc.selectedCommunity;
|
||||||
|
final selectedSpace = selectionBloc.selectedSpace;
|
||||||
const spacer = Spacer(flex: 10);
|
const spacer = Spacer(flex: 10);
|
||||||
return Visibility(
|
return Visibility(
|
||||||
visible: selectedCommunity.spaces.isNotEmpty,
|
visible: selectedCommunity!.spaces.isNotEmpty,
|
||||||
replacement: const Row(
|
replacement: const Row(
|
||||||
children: [spacer, Expanded(child: CreateSpaceButton()), spacer]),
|
children: [spacer, Expanded(child: CreateSpaceButton()), spacer],
|
||||||
child: CommunityStructureCanvas(community: selectedCommunity),
|
),
|
||||||
|
child: CommunityStructureCanvas(
|
||||||
|
community: selectedCommunity,
|
||||||
|
selectedSpace: selectedSpace,
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -7,26 +7,24 @@ class SpaceManagementTemplatesView extends StatelessWidget {
|
|||||||
const SpaceManagementTemplatesView({super.key});
|
const SpaceManagementTemplatesView({super.key});
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Expanded(
|
return ColoredBox(
|
||||||
child: ColoredBox(
|
color: ColorsManager.whiteColors,
|
||||||
color: ColorsManager.whiteColors,
|
child: GridView.builder(
|
||||||
child: GridView.builder(
|
padding: const EdgeInsets.symmetric(horizontal: 40, vertical: 20),
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 40, vertical: 20),
|
gridDelegate: const SliverGridDelegateWithMaxCrossAxisExtent(
|
||||||
gridDelegate: const SliverGridDelegateWithMaxCrossAxisExtent(
|
maxCrossAxisExtent: 400,
|
||||||
maxCrossAxisExtent: 400,
|
mainAxisSpacing: 10,
|
||||||
mainAxisSpacing: 10,
|
crossAxisSpacing: 10,
|
||||||
crossAxisSpacing: 10,
|
childAspectRatio: 2.0,
|
||||||
childAspectRatio: 2.0,
|
|
||||||
),
|
|
||||||
itemCount: _gridItems(context).length,
|
|
||||||
itemBuilder: (context, index) {
|
|
||||||
final model = _gridItems(context)[index];
|
|
||||||
return CommunityTemplateCell(
|
|
||||||
onTap: model.onTap,
|
|
||||||
title: model.title,
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
|
itemCount: _gridItems(context).length,
|
||||||
|
itemBuilder: (context, index) {
|
||||||
|
final model = _gridItems(context)[index];
|
||||||
|
return CommunityTemplateCell(
|
||||||
|
onTap: model.onTap,
|
||||||
|
title: model.title,
|
||||||
|
);
|
||||||
|
},
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -16,7 +16,7 @@ final class SelectCommunityEvent extends CommunitiesTreeSelectionEvent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
final class SelectSpaceEvent extends CommunitiesTreeSelectionEvent {
|
final class SelectSpaceEvent extends CommunitiesTreeSelectionEvent {
|
||||||
final SpaceModel space;
|
final SpaceModel? space;
|
||||||
final CommunityModel community;
|
final CommunityModel community;
|
||||||
|
|
||||||
const SelectSpaceEvent({required this.space, required this.community});
|
const SelectSpaceEvent({required this.space, required this.community});
|
||||||
|
|||||||
@ -0,0 +1,11 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:syncrow_web/pages/space_management_v2/modules/space_details/presentation/widgets/space_details_dialog.dart';
|
||||||
|
|
||||||
|
abstract final class SpaceDetailsDialogHelper {
|
||||||
|
static void showCreate(BuildContext context) {
|
||||||
|
showDialog<void>(
|
||||||
|
context: context,
|
||||||
|
builder: (context) => const SpaceDetailsDialog(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,12 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
class SpaceDetailsDialog extends StatelessWidget {
|
||||||
|
const SpaceDetailsDialog({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return const Dialog(
|
||||||
|
child: Text('Create Space'),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user