Compare commits

..

2 Commits

Author SHA1 Message Date
6ef0b2f9d1 fixed the save issue 2025-04-28 00:36:58 +04:00
52608b1f35 fixed search query 2025-04-27 22:42:57 +04:00
11 changed files with 178 additions and 141 deletions

View File

@ -1,8 +1,8 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:syncrow_web/pages/common/buttons/default_button.dart'; import 'package:syncrow_web/pages/common/buttons/default_button.dart';
import 'package:syncrow_web/utils/color_manager.dart';
import 'package:syncrow_web/utils/extension/build_context_x.dart'; import 'package:syncrow_web/utils/extension/build_context_x.dart';
import 'package:syncrow_web/utils/style.dart'; import 'package:syncrow_web/utils/style.dart';
import 'package:syncrow_web/utils/color_manager.dart';
class SearchResetButtons extends StatelessWidget { class SearchResetButtons extends StatelessWidget {
const SearchResetButtons({ const SearchResetButtons({
@ -17,10 +17,8 @@ class SearchResetButtons extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Row( return Row(
mainAxisSize: MainAxisSize.min,
children: [ children: [
Column( Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center,
children: [ children: [
const SizedBox(height: 25), const SizedBox(height: 25),

View File

@ -72,7 +72,6 @@ class DeviceManagementBody extends StatelessWidget with HelperResponsiveLayout {
child: state is DeviceManagementLoading child: state is DeviceManagementLoading
? const Center(child: CircularProgressIndicator()) ? const Center(child: CircularProgressIndicator())
: Column( : Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Container( Container(
padding: isLargeScreenSize(context) padding: isLargeScreenSize(context)

View File

@ -14,29 +14,29 @@ class DeviceSearchFilters extends StatefulWidget {
class _DeviceSearchFiltersState extends State<DeviceSearchFilters> class _DeviceSearchFiltersState extends State<DeviceSearchFilters>
with HelperResponsiveLayout { with HelperResponsiveLayout {
late final TextEditingController _unitNameController; final _unitNameController = TextEditingController();
late final TextEditingController _productNameController; final _productNameController = TextEditingController();
@override List<Widget> get _widgets => [
void initState() {
_unitNameController = TextEditingController();
_productNameController = TextEditingController();
super.initState();
}
@override
Widget build(BuildContext context) {
return Wrap(
alignment: WrapAlignment.start,
runAlignment: WrapAlignment.start,
crossAxisAlignment: WrapCrossAlignment.center,
spacing: 20,
runSpacing: 10,
children: [
_buildSearchField("Space Name", _unitNameController, 200), _buildSearchField("Space Name", _unitNameController, 200),
_buildSearchField("Device Name / Product Name", _productNameController, 300), _buildSearchField("Device Name / Product Name", _productNameController, 300),
_buildSearchResetButtons(), _buildSearchResetButtons(),
], ];
@override
Widget build(BuildContext context) {
if (isExtraLargeScreenSize(context)) {
return Row(
children: _widgets
.map((e) => Padding(padding: const EdgeInsets.all(10), child: e))
.toList(),
);
}
return Wrap(
spacing: 20,
runSpacing: 10,
children: _widgets,
); );
} }

View File

@ -217,31 +217,29 @@ class SmartPowerBloc extends Bloc<SmartPowerEvent, SmartPowerState> {
try { try {
var status = var status =
await DevicesManagementApi().getPowerClampInfo(event.deviceId); await DevicesManagementApi().getPowerClampInfo(event.deviceId);
deviceStatus = PowerClampModel.fromJson(status as Map<String, Object?>? ??{}); deviceStatus = PowerClampModel.fromJson(status);
final phaseADataPoints = deviceStatus.status.phaseA.dataPoints;
final phaseBDataPoints = deviceStatus.status.phaseB.dataPoints;
final phaseCDataPoints = deviceStatus.status.phaseC.dataPoints;
phaseData = [ phaseData = [
{ {
'name': 'Phase A', 'name': 'Phase A',
'voltage': '${(phaseADataPoints.elementAtOrNull(0)?.value as num? ?? 0) / 10} V', 'voltage': '${deviceStatus.status.phaseA.dataPoints[0].value / 10} V',
'current': '${(phaseADataPoints.elementAtOrNull(1)?.value as num? ?? 0) / 10} A', 'current': '${deviceStatus.status.phaseA.dataPoints[1].value / 10} A',
'activePower': '${phaseADataPoints.elementAtOrNull(2)?.value??'N/A'} W', 'activePower': '${deviceStatus.status.phaseA.dataPoints[2].value} W',
'powerFactor': '${phaseADataPoints.elementAtOrNull(3)?.value??'N/A'}', 'powerFactor': '${deviceStatus.status.phaseA.dataPoints[3].value}',
}, },
{ {
'name': 'Phase B', 'name': 'Phase B',
'voltage': '${(phaseBDataPoints .elementAtOrNull(0)?.value as num? ?? 0) / 10} V', 'voltage': '${deviceStatus.status.phaseB.dataPoints[0].value / 10} V',
'current': '${(phaseBDataPoints .elementAtOrNull(1)?.value as num? ?? 0) / 10} A', 'current': '${deviceStatus.status.phaseB.dataPoints[1].value / 10} A',
'activePower': '${phaseBDataPoints.elementAtOrNull(2)?.value??'N/A'} W', 'activePower': '${deviceStatus.status.phaseB.dataPoints[2].value} W',
'powerFactor': '${phaseBDataPoints.elementAtOrNull(3)?.value??'N/A'}', 'powerFactor': '${deviceStatus.status.phaseB.dataPoints[3].value}',
}, },
{ {
'name': 'Phase C', 'name': 'Phase C',
'voltage': '${(phaseCDataPoints.elementAtOrNull(0)?.value as num? ?? 0) / 10} V', 'voltage': '${deviceStatus.status.phaseC.dataPoints[0].value / 10} V',
'current': '${(phaseCDataPoints.elementAtOrNull(1)?.value as num? ?? 0) / 10} A', 'current': '${deviceStatus.status.phaseC.dataPoints[1].value / 10} A',
'activePower': '${phaseCDataPoints.elementAtOrNull(2)?.value ?? 'N/A'} W', 'activePower': '${deviceStatus.status.phaseC.dataPoints[2].value} W',
'powerFactor': '${phaseCDataPoints.elementAtOrNull(3)?.value ?? 'N/A'}', 'powerFactor': '${deviceStatus.status.phaseC.dataPoints[3].value}',
}, },
]; ];
emit(GetDeviceStatus()); emit(GetDeviceStatus());
@ -787,7 +785,7 @@ class SmartPowerBloc extends Bloc<SmartPowerEvent, SmartPowerState> {
void selectDateRange() async { void selectDateRange() async {
DateTime startDate = dateTime!; DateTime startDate = dateTime!;
DateTime endDate = DateTime(startDate.year, startDate.month + 1, 1) DateTime endDate = DateTime(startDate.year, startDate.month + 1, 1)
.subtract(const Duration(days: 1)); .subtract(Duration(days: 1));
String formattedEndDate = DateFormat('dd/MM/yyyy').format(endDate); String formattedEndDate = DateFormat('dd/MM/yyyy').format(endDate);
endChartDate = ' - $formattedEndDate'; endChartDate = ' - $formattedEndDate';
} }

View File

@ -12,9 +12,9 @@ class PowerClampModel {
factory PowerClampModel.fromJson(Map<String, dynamic> json) { factory PowerClampModel.fromJson(Map<String, dynamic> json) {
return PowerClampModel( return PowerClampModel(
productUuid: json['productUuid'] as String? ?? '', productUuid: json['productUuid'],
productType: json['productType'] as String? ?? '', productType: json['productType'],
status: PowerStatus.fromJson(json['status'] as Map<String, dynamic>? ?? {}), status: PowerStatus.fromJson(json['status']),
); );
} }
@ -26,7 +26,7 @@ class PowerClampModel {
return PowerClampModel( return PowerClampModel(
productUuid: productUuid ?? this.productUuid, productUuid: productUuid ?? this.productUuid,
productType: productType ?? this.productType, productType: productType ?? this.productType,
status: statusPower ?? status, status: statusPower ?? this.status,
); );
} }
} }
@ -46,10 +46,12 @@ class PowerStatus {
factory PowerStatus.fromJson(Map<String, dynamic> json) { factory PowerStatus.fromJson(Map<String, dynamic> json) {
return PowerStatus( return PowerStatus(
phaseA: Phase.fromJson(json['phaseA']as List<dynamic>? ?? []), phaseA: Phase.fromJson(json['phaseA']),
phaseB: Phase.fromJson(json['phaseB']as List<dynamic>? ?? []), phaseB: Phase.fromJson(json['phaseB']),
phaseC: Phase.fromJson(json['phaseC']as List<dynamic>? ?? []), phaseC: Phase.fromJson(json['phaseC']),
general: Phase.fromJson(json['general']as List<dynamic>? ?? [] general: Phase.fromJson(json['general']
// List<DataPoint>.from(
// json['general'].map((x) => DataPoint.fromJson(x))),
)); ));
} }
} }
@ -67,30 +69,30 @@ class Phase {
} }
class DataPoint { class DataPoint {
final String? code; dynamic code;
final String? customName; dynamic customName;
final int? dpId; dynamic dpId;
final int? time; dynamic time;
final String? type; dynamic type;
final dynamic value; dynamic value;
DataPoint({ DataPoint({
this.code, required this.code,
this.customName, required this.customName,
this.dpId, required this.dpId,
this.time, required this.time,
this.type, required this.type,
this.value, required this.value,
}); });
factory DataPoint.fromJson(Map<String, dynamic> json) { factory DataPoint.fromJson(Map<String, dynamic> json) {
return DataPoint( return DataPoint(
code: json['code'] as String?, code: json['code'],
customName: json['customName'] as String?, customName: json['customName'],
dpId: json['dpId'] as int?, dpId: json['dpId'],
time: json['time'] as int?, time: json['time'],
type: json['type'] as String?, type: json['type'],
value: json['value'] as dynamic, value: json['value'],
); );
} }
} }

View File

@ -1,5 +1,5 @@
import 'package:fl_chart/fl_chart.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:fl_chart/fl_chart.dart';
import 'package:syncrow_web/utils/color_manager.dart'; import 'package:syncrow_web/utils/color_manager.dart';
class EnergyConsumptionPage extends StatefulWidget { class EnergyConsumptionPage extends StatefulWidget {
@ -10,8 +10,7 @@ class EnergyConsumptionPage extends StatefulWidget {
final Widget widget; final Widget widget;
final Function()? onTap; final Function()? onTap;
const EnergyConsumptionPage({ EnergyConsumptionPage({
super.key,
required this.chartData, required this.chartData,
required this.totalConsumption, required this.totalConsumption,
required this.date, required this.date,
@ -92,12 +91,11 @@ class _EnergyConsumptionPageState extends State<EnergyConsumptionPage> {
], ],
), ),
Column( Column(
mainAxisSize: MainAxisSize.min,
children: [ children: [
Padding( Padding(
padding: const EdgeInsets.only(top: 10), padding: const EdgeInsets.only(top: 10),
child: SizedBox( child: SizedBox(
height: MediaQuery.sizeOf(context).height * 0.09, height: MediaQuery.of(context).size.height * 0.11,
child: LineChart( child: LineChart(
LineChartData( LineChartData(
lineTouchData: LineTouchData( lineTouchData: LineTouchData(
@ -153,7 +151,7 @@ class _EnergyConsumptionPageState extends State<EnergyConsumptionPage> {
child: RotatedBox( child: RotatedBox(
quarterTurns: -1, quarterTurns: -1,
child: Text(_chartData[index].time, child: Text(_chartData[index].time,
style: const TextStyle(fontSize: 10)), style: TextStyle(fontSize: 10)),
), ),
); );
} }
@ -192,8 +190,8 @@ class _EnergyConsumptionPageState extends State<EnergyConsumptionPage> {
spots: _chartData spots: _chartData
.asMap() .asMap()
.entries .entries
.map((entry) => FlSpot( .map((entry) => FlSpot(entry.key.toDouble(),
entry.key.toDouble(), entry.value.consumption)) entry.value.consumption))
.toList(), .toList(),
isCurved: true, isCurved: true,
color: ColorsManager.primaryColor.withOpacity(0.6), color: ColorsManager.primaryColor.withOpacity(0.6),
@ -220,7 +218,7 @@ class _EnergyConsumptionPageState extends State<EnergyConsumptionPage> {
borderData: FlBorderData( borderData: FlBorderData(
show: false, show: false,
border: Border.all( border: Border.all(
color: const Color(0xff023DFE).withOpacity(0.7), color: Color(0xff023DFE).withOpacity(0.7),
width: 10, width: 10,
), ),
), ),
@ -255,9 +253,11 @@ class _EnergyConsumptionPageState extends State<EnergyConsumptionPage> {
child: InkWell( child: InkWell(
onTap: widget.onTap, onTap: widget.onTap,
child: Center( child: Center(
child: Padding( child: SizedBox(
padding: const EdgeInsets.all(5), child: Padding(
child: Text(widget.date), padding: const EdgeInsets.all(5),
child: Text(widget.date),
),
), ),
), ),
), ),

View File

@ -12,7 +12,8 @@ import 'package:syncrow_web/utils/constants/assets.dart';
import 'package:syncrow_web/utils/helpers/responsice_layout_helper/responsive_layout_helper.dart'; import 'package:syncrow_web/utils/helpers/responsice_layout_helper/responsive_layout_helper.dart';
//Smart Power Clamp //Smart Power Clamp
class SmartPowerDeviceControl extends StatelessWidget with HelperResponsiveLayout { class SmartPowerDeviceControl extends StatelessWidget
with HelperResponsiveLayout {
final String deviceId; final String deviceId;
const SmartPowerDeviceControl({super.key, required this.deviceId}); const SmartPowerDeviceControl({super.key, required this.deviceId});
@ -24,27 +25,27 @@ class SmartPowerDeviceControl extends StatelessWidget with HelperResponsiveLayou
..add(SmartPowerFetchDeviceEvent(deviceId)), ..add(SmartPowerFetchDeviceEvent(deviceId)),
child: BlocBuilder<SmartPowerBloc, SmartPowerState>( child: BlocBuilder<SmartPowerBloc, SmartPowerState>(
builder: (context, state) { builder: (context, state) {
final blocProvider = BlocProvider.of<SmartPowerBloc>(context); final _blocProvider = BlocProvider.of<SmartPowerBloc>(context);
if (state is SmartPowerLoading) { if (state is SmartPowerLoading) {
return const Center(child: CircularProgressIndicator()); return const Center(child: CircularProgressIndicator());
} else if (state is FakeState) { } else if (state is FakeState) {
return _buildStatusControls( return _buildStatusControls(
currentPage: blocProvider.currentPage, currentPage: _blocProvider.currentPage,
context: context, context: context,
blocProvider: blocProvider, blocProvider: _blocProvider,
); );
} else if (state is GetDeviceStatus) { } else if (state is GetDeviceStatus) {
return _buildStatusControls( return _buildStatusControls(
currentPage: blocProvider.currentPage, currentPage: _blocProvider.currentPage,
context: context, context: context,
blocProvider: blocProvider, blocProvider: _blocProvider,
); );
} else if (state is FilterRecordsState) { } else if (state is FilterRecordsState) {
return _buildStatusControls( return _buildStatusControls(
currentPage: blocProvider.currentPage, currentPage: _blocProvider.currentPage,
context: context, context: context,
blocProvider: blocProvider, blocProvider: _blocProvider,
); );
} }
return const Center(child: CircularProgressIndicator()); return const Center(child: CircularProgressIndicator());
@ -59,7 +60,7 @@ class SmartPowerDeviceControl extends StatelessWidget with HelperResponsiveLayou
required SmartPowerBloc blocProvider, required SmartPowerBloc blocProvider,
required int currentPage, required int currentPage,
}) { }) {
PageController pageController = PageController(initialPage: currentPage); PageController _pageController = PageController(initialPage: currentPage);
return Container( return Container(
padding: const EdgeInsets.symmetric(horizontal: 50), padding: const EdgeInsets.symmetric(horizontal: 50),
child: DeviceControlsContainer( child: DeviceControlsContainer(
@ -84,31 +85,25 @@ class SmartPowerDeviceControl extends StatelessWidget with HelperResponsiveLayou
PowerClampInfoCard( PowerClampInfoCard(
iconPath: Assets.powerActiveIcon, iconPath: Assets.powerActiveIcon,
title: 'Active', title: 'Active',
value: blocProvider.deviceStatus.status.general.dataPoints value: blocProvider
.elementAtOrNull(2) .deviceStatus.status.general.dataPoints[2].value
?.value .toString(),
.toString() ??
'',
unit: '', unit: '',
), ),
PowerClampInfoCard( PowerClampInfoCard(
iconPath: Assets.voltMeterIcon, iconPath: Assets.voltMeterIcon,
title: 'Current', title: 'Current',
value: blocProvider.deviceStatus.status.general.dataPoints value: blocProvider
.elementAtOrNull(1) .deviceStatus.status.general.dataPoints[1].value
?.value .toString(),
.toString() ??
'',
unit: ' A', unit: ' A',
), ),
PowerClampInfoCard( PowerClampInfoCard(
iconPath: Assets.frequencyIcon, iconPath: Assets.frequencyIcon,
title: 'Frequency', title: 'Frequency',
value: blocProvider.deviceStatus.status.general.dataPoints value: blocProvider
.elementAtOrNull(4) .deviceStatus.status.general.dataPoints[4].value
?.value .toString(),
.toString() ??
'',
unit: ' Hz', unit: ' Hz',
), ),
], ],
@ -147,7 +142,7 @@ class SmartPowerDeviceControl extends StatelessWidget with HelperResponsiveLayou
icon: const Icon(Icons.arrow_left), icon: const Icon(Icons.arrow_left),
onPressed: () { onPressed: () {
blocProvider.add(SmartPowerArrowPressedEvent(-1)); blocProvider.add(SmartPowerArrowPressedEvent(-1));
pageController.previousPage( _pageController.previousPage(
duration: const Duration(milliseconds: 300), duration: const Duration(milliseconds: 300),
curve: Curves.easeInOut, curve: Curves.easeInOut,
); );
@ -167,7 +162,7 @@ class SmartPowerDeviceControl extends StatelessWidget with HelperResponsiveLayou
icon: const Icon(Icons.arrow_right), icon: const Icon(Icons.arrow_right),
onPressed: () { onPressed: () {
blocProvider.add(SmartPowerArrowPressedEvent(1)); blocProvider.add(SmartPowerArrowPressedEvent(1));
pageController.nextPage( _pageController.nextPage(
duration: const Duration(milliseconds: 300), duration: const Duration(milliseconds: 300),
curve: Curves.easeInOut, curve: Curves.easeInOut,
); );
@ -182,7 +177,7 @@ class SmartPowerDeviceControl extends StatelessWidget with HelperResponsiveLayou
Expanded( Expanded(
flex: 2, flex: 2,
child: PageView( child: PageView(
controller: pageController, controller: _pageController,
onPageChanged: (int page) { onPageChanged: (int page) {
blocProvider.add(SmartPowerPageChangedEvent(page)); blocProvider.add(SmartPowerPageChangedEvent(page));
}, },
@ -195,8 +190,8 @@ class SmartPowerDeviceControl extends StatelessWidget with HelperResponsiveLayou
blocProvider.add(SelectDateEvent(context: context)); blocProvider.add(SelectDateEvent(context: context));
blocProvider.add(FilterRecordsByDateEvent( blocProvider.add(FilterRecordsByDateEvent(
selectedDate: blocProvider.dateTime!, selectedDate: blocProvider.dateTime!,
viewType: viewType: blocProvider
blocProvider.views[blocProvider.currentIndex])); .views[blocProvider.currentIndex]));
}, },
widget: blocProvider.dateSwitcher(), widget: blocProvider.dateSwitcher(),
chartData: blocProvider.energyDataList.isNotEmpty chartData: blocProvider.energyDataList.isNotEmpty

View File

@ -4,6 +4,7 @@ import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:syncrow_web/pages/common/bloc/project_manager.dart'; import 'package:syncrow_web/pages/common/bloc/project_manager.dart';
import 'package:syncrow_web/pages/space_tree/bloc/space_tree_bloc.dart'; import 'package:syncrow_web/pages/space_tree/bloc/space_tree_bloc.dart';
import 'package:syncrow_web/pages/space_tree/bloc/space_tree_event.dart'; import 'package:syncrow_web/pages/space_tree/bloc/space_tree_event.dart';
import 'package:syncrow_web/pages/space_tree/bloc/space_tree_state.dart';
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/community_model.dart'; import 'package:syncrow_web/pages/spaces_management/all_spaces/model/community_model.dart';
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/create_subspace_model.dart'; import 'package:syncrow_web/pages/spaces_management/all_spaces/model/create_subspace_model.dart';
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/product_model.dart'; import 'package:syncrow_web/pages/spaces_management/all_spaces/model/product_model.dart';
@ -246,7 +247,9 @@ class SpaceManagementBloc extends Bloc<SpaceManagementEvent, SpaceManagementStat
final previousState = state; final previousState = state;
final projectUuid = await ProjectManager.getProjectUUID() ?? ''; final projectUuid = await ProjectManager.getProjectUUID() ?? '';
var spaceBloc = event.context.read<SpaceTreeBloc>(); var spaceBloc = event.context.read<SpaceTreeBloc>();
List<CommunityModel> communities = await _waitForCommunityList(spaceBloc); var spaceTreeState = event.context.read<SpaceTreeBloc>().state;
List<CommunityModel> communities = await _waitForCommunityList(spaceBloc, spaceTreeState);
await fetchSpaceModels(); await fetchSpaceModels();
await fetchTags(); await fetchTags();
@ -277,11 +280,13 @@ class SpaceManagementBloc extends Bloc<SpaceManagementEvent, SpaceManagementStat
LoadCommunityAndSpacesEvent event, LoadCommunityAndSpacesEvent event,
Emitter<SpaceManagementState> emit, Emitter<SpaceManagementState> emit,
) async { ) async {
var spaceTreeState = event.context.read<SpaceTreeBloc>().state;
var spaceBloc = event.context.read<SpaceTreeBloc>(); var spaceBloc = event.context.read<SpaceTreeBloc>();
_onloadProducts(); _onloadProducts();
await fetchTags(); await fetchTags();
// Wait until `communityList` is loaded // Wait until `communityList` is loaded
List<CommunityModel> communities = await _waitForCommunityList(spaceBloc); List<CommunityModel> communities = await _waitForCommunityList(spaceBloc, spaceTreeState);
// Fetch space models after communities are available // Fetch space models after communities are available
final prevSpaceModels = await fetchSpaceModels(); final prevSpaceModels = await fetchSpaceModels();
@ -292,23 +297,38 @@ class SpaceManagementBloc extends Bloc<SpaceManagementEvent, SpaceManagementStat
allTags: _cachedTags ?? [])); allTags: _cachedTags ?? []));
} }
Future<List<CommunityModel>> _waitForCommunityList(SpaceTreeBloc spaceBloc) async { Future<List<CommunityModel>> _waitForCommunityList(
SpaceTreeBloc spaceBloc, SpaceTreeState spaceTreeState) async {
// Check if communityList is already populated // Check if communityList is already populated
if (spaceBloc.state.communityList.isNotEmpty) { final filteredCommunities = spaceTreeState.searchQuery.isNotEmpty
return spaceBloc.state.communityList; ? spaceTreeState.filteredCommunity
: spaceTreeState.communityList;
if (filteredCommunities.isNotEmpty) {
return filteredCommunities;
} }
final completer = Completer<List<CommunityModel>>(); final completer = Completer<List<CommunityModel>>();
final subscription = spaceBloc.stream.listen((state) { final subscription = spaceBloc.stream.listen((state) {
if (state.communityList.isNotEmpty) { if (!completer.isCompleted && state.communityList.isNotEmpty) {
completer.complete(state.communityList); completer
.complete(state.searchQuery.isNotEmpty ? state.filteredCommunity : state.communityList);
} }
}); });
try {
final communities = await completer.future.timeout(
const Duration(seconds: 10),
onTimeout: () {
if (!completer.isCompleted) {
completer.complete([]);
}
return [];
},
);
// Return the list once available, then cancel the listener return communities;
final communities = await completer.future; } finally {
await subscription.cancel(); await subscription.cancel();
return communities; }
} }
void _onCommunityDelete( void _onCommunityDelete(
@ -491,12 +511,21 @@ class SpaceManagementBloc extends Bloc<SpaceManagementEvent, SpaceManagementStat
Future<List<SpaceModel>> saveSpacesHierarchically( Future<List<SpaceModel>> saveSpacesHierarchically(
BuildContext context, List<SpaceModel> spaces, String communityUuid) async { BuildContext context, List<SpaceModel> spaces, String communityUuid) async {
final orderedSpaces = flattenHierarchy(spaces); final orderedSpaces = flattenHierarchy(spaces);
final projectUuid = await ProjectManager.getProjectUUID() ?? ''; final projectUuid = await ProjectManager.getProjectUUID() ?? '';
var spaceBloc = context.read<SpaceTreeBloc>(); CommunityModel? selectedCommunity;
List<CommunityModel> communities = spaceBloc.state.communityList; try {
CommunityModel? selectedCommunity = communities.firstWhere( final spaceTreeState = context.read<SpaceTreeBloc>().state;
(community) => community.uuid == communityUuid, final filteredCommunities = spaceTreeState.searchQuery.isNotEmpty
); ? spaceTreeState.filteredCommunity
: spaceTreeState.communityList;
selectedCommunity = filteredCommunities.firstWhere(
(community) => community.uuid == communityUuid,
);
} catch (e) {
return [];
}
final parentsToDelete = orderedSpaces.where((space) => final parentsToDelete = orderedSpaces.where((space) =>
space.status == SpaceStatus.deleted && space.status == SpaceStatus.deleted &&
@ -669,9 +698,12 @@ class SpaceManagementBloc extends Bloc<SpaceManagementEvent, SpaceManagementStat
try { try {
await fetchTags(); await fetchTags();
final projectUuid = await ProjectManager.getProjectUUID() ?? ''; final spaceTreeState = event.context.read<SpaceTreeBloc>().state;
var spaceBloc = event.context.read<SpaceTreeBloc>(); final filteredCommunities = spaceTreeState.searchQuery.isNotEmpty
List<CommunityModel> communities = spaceBloc.state.communityList; ? spaceTreeState.filteredCommunity
: spaceTreeState.communityList;
List<CommunityModel> communities = filteredCommunities;
var prevSpaceModels = await fetchSpaceModels(); var prevSpaceModels = await fetchSpaceModels();

View File

@ -467,7 +467,6 @@ class _CommunityStructureAreaState extends State<CommunityStructureArea> {
} }
String communityUuid = widget.selectedCommunity!.uuid; String communityUuid = widget.selectedCommunity!.uuid;
context.read<SpaceManagementBloc>().add(SaveSpacesEvent( context.read<SpaceManagementBloc>().add(SaveSpacesEvent(
context, context,
spaces: spacesToSave, spaces: spacesToSave,

View File

@ -1,3 +1,5 @@
import 'dart:async';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:syncrow_web/common/widgets/empty_search_result_widget.dart'; import 'package:syncrow_web/common/widgets/empty_search_result_widget.dart';
@ -36,6 +38,7 @@ class SidebarWidget extends StatefulWidget {
class _SidebarWidgetState extends State<SidebarWidget> { class _SidebarWidgetState extends State<SidebarWidget> {
late final ScrollController _scrollController; late final ScrollController _scrollController;
Timer? _debounce;
String _searchQuery = ''; String _searchQuery = '';
String? _selectedSpaceUuid; String? _selectedSpaceUuid;
@ -43,10 +46,10 @@ class _SidebarWidgetState extends State<SidebarWidget> {
@override @override
void initState() { void initState() {
super.initState();
_scrollController = ScrollController(); _scrollController = ScrollController();
_scrollController.addListener(_onScroll); _scrollController.addListener(_onScroll);
_selectedId = widget.selectedSpaceUuid; _selectedId = widget.selectedSpaceUuid;
super.initState();
} }
void _onScroll() { void _onScroll() {
@ -67,11 +70,22 @@ class _SidebarWidgetState extends State<SidebarWidget> {
@override @override
void dispose() { void dispose() {
_scrollController.removeListener(_onScroll); _scrollController.removeListener(_onScroll);
_scrollController.dispose(); _scrollController.dispose();
_debounce?.cancel();
super.dispose(); super.dispose();
} }
void _onSearchChanged(String query) {
if (_debounce?.isActive ?? false) _debounce?.cancel();
_debounce = Timer(const Duration(milliseconds: 500), () {
setState(() {
_searchQuery = query;
});
context.read<SpaceTreeBloc>().add(SearchQueryEvent(query));
});
}
@override @override
void didUpdateWidget(covariant SidebarWidget oldWidget) { void didUpdateWidget(covariant SidebarWidget oldWidget) {
if (widget.selectedSpaceUuid != oldWidget.selectedSpaceUuid) { if (widget.selectedSpaceUuid != oldWidget.selectedSpaceUuid) {
@ -91,7 +105,7 @@ class _SidebarWidgetState extends State<SidebarWidget> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final spaceTreeState = context.watch<SpaceTreeBloc>().state; final spaceTreeState = context.watch<SpaceTreeBloc>().state;
final filteredCommunities = spaceTreeState.isSearching final filteredCommunities = spaceTreeState.searchQuery.isNotEmpty
? spaceTreeState.filteredCommunity ? spaceTreeState.filteredCommunity
: spaceTreeState.communityList; : spaceTreeState.communityList;
@ -104,13 +118,7 @@ class _SidebarWidgetState extends State<SidebarWidget> {
children: [ children: [
SidebarHeader(onAddCommunity: _onAddCommunity), SidebarHeader(onAddCommunity: _onAddCommunity),
CustomSearchBar( CustomSearchBar(
onSearchChanged: (query) { onSearchChanged: _onSearchChanged,
setState(() {
_searchQuery = query;
});
context.read<SpaceTreeBloc>().add(SearchQueryEvent(query));
},
), ),
const SizedBox(height: 16), const SizedBox(height: 16),
Expanded( Expanded(
@ -123,10 +131,15 @@ class _SidebarWidgetState extends State<SidebarWidget> {
communities: filteredCommunities, communities: filteredCommunities,
itemBuilder: (context, index) { itemBuilder: (context, index) {
if (index == filteredCommunities.length) { if (index == filteredCommunities.length) {
return const Padding( final spaceTreeState = context.read<SpaceTreeBloc>().state;
padding: EdgeInsets.all(8.0), if (spaceTreeState.paginationIsLoading) {
child: Center(child: CircularProgressIndicator()), return const Padding(
); padding: EdgeInsets.all(8.0),
child: Center(child: CircularProgressIndicator()),
);
} else {
return const SizedBox.shrink();
}
} }
return _buildCommunityTile(context, filteredCommunities[index]); return _buildCommunityTile(context, filteredCommunities[index]);
}), }),

View File

@ -281,6 +281,7 @@ class CommunitySpaceManagementApi {
return json['success'] ?? false; return json['success'] ?? false;
}, },
); );
return response; return response;
} catch (e) { } catch (e) {
debugPrint('Error updating space: $e'); debugPrint('Error updating space: $e');