Compare commits

..

33 Commits

Author SHA1 Message Date
a5a37f3841 append space id to list 2025-03-14 14:10:03 +04:00
5b2822f973 Merge pull request #117 from SyncrowIOT/SP-1318
Sp 1318
2025-03-13 14:51:54 +04:00
70d31f5351 removed print 2025-03-13 14:51:09 +04:00
c39c693755 remove unused import 2025-03-13 14:37:39 +04:00
8ed6980264 resposnive text length of space model name 2025-03-13 13:51:52 +04:00
16df75eb49 fixed space update 2025-03-13 13:14:31 +04:00
80c294f09c fix real time listenToChanges 2025-03-12 16:03:01 +03:00
c806b5f59d Added a background color for the space tree 2025-03-11 23:54:39 +03:00
dd01a7fddb Pulled latest changes 2025-03-11 23:42:42 +03:00
b563cc378e Added current temp to AC functions 2025-03-11 23:41:21 +03:00
2140b7eee3 Merge pull request #114 from SyncrowIOT:bugfix/update-space
update space with a space model
2025-03-11 22:08:06 +04:00
7de9e25ed5 Merge branch 'dev' of https://github.com/SyncrowIOT/web into bugfix/update-space 2025-03-11 22:07:50 +04:00
123949aa86 update space with a space model 2025-03-11 22:05:36 +04:00
6d554c5953 Merge pull request #113 from SyncrowIOT/bugfix/remove-link
fixed issue in removing link
2025-03-10 16:55:02 +04:00
5ed87a5794 fixed issue in removing link 2025-03-10 11:47:00 +04:00
168c997240 Merge pull request #112 from SyncrowIOT:bugfix/empty-space-model
fixed issue in update
2025-03-09 17:32:38 +04:00
de65f79ccf Merge branch 'dev' of https://github.com/SyncrowIOT/web into bugfix/empty-space-model 2025-03-09 17:32:06 +04:00
eb0490fb16 fixed issue in update 2025-03-09 17:31:36 +04:00
a73fc04712 Merge pull request #110 from SyncrowIOT/SP-1297
fix model and SpaceModelCardWidget and LinkSpaceModelSpacesDialog
2025-03-09 16:11:18 +03:00
dee07ebb06 fix LinkingSuccessful 2025-03-09 16:01:04 +03:00
5237f3ae5b Merge pull request #111 from SyncrowIOT/bugfix/empty-space-model
fix the circular progress on empty space model
2025-03-09 16:49:36 +04:00
e017633b9b fixed space model 2025-03-09 16:48:42 +04:00
e8e5e9bcb7 fix LoadedSpaceView 2025-03-09 14:54:13 +03:00
a1d15c9cea fix the circular progress on empty space model 2025-03-09 15:53:55 +04:00
16c006e7a9 Merge pull request #109 from SyncrowIOT/feat/fix-update-on-community-tree
Feat/fix-update-on-community-tree
2025-03-09 14:06:48 +04:00
24280549ec revert back color change 2025-03-09 14:06:33 +04:00
8359642a1a Merge branch 'dev' into SP-1297 2025-03-09 12:03:34 +03:00
ada19a5992 fetch communities and spaces on space model update 2025-03-09 10:47:09 +04:00
c6702d4d5f Merge branch 'dev' of https://github.com/SyncrowIOT/web into dev 2025-03-09 10:29:05 +04:00
f78e2e2d36 Merge branch 'feat/update-space-model' of https://github.com/SyncrowIOT/web into dev 2025-03-09 10:26:34 +04:00
83b9555920 added padding to space tree on community structure 2025-03-09 10:26:30 +04:00
8af24e575c fix model and SpaceModelCardWidget and LinkSpaceModelSpacesDialog 2025-03-08 09:57:35 +03:00
4301d7f62f Merge pull request #108 from SyncrowIOT/feat/update-space-model
Feat/update space model
2025-03-07 11:20:29 +04:00
27 changed files with 343 additions and 312 deletions

View File

@ -101,7 +101,7 @@ class CustomExpansionTileState extends State<CustomExpansionTile> {
widget.children != null &&
widget.children!.isNotEmpty)
Padding(
padding: const EdgeInsets.only(left: 48.0), // Indented children
padding: const EdgeInsets.only(left: 24.0), // Indented children
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: widget.children!,

View File

@ -244,6 +244,7 @@ SOS
SwitchFunction(deviceId: uuid ?? '', deviceName: name ?? ''),
ModeFunction(deviceId: uuid ?? '', deviceName: name ?? ''),
TempSetFunction(deviceId: uuid ?? '', deviceName: name ?? ''),
CurrentTempFunction(deviceId: uuid ?? '', deviceName: name ?? ''),
LevelFunction(deviceId: uuid ?? '', deviceName: name ?? ''),
ChildLockFunction(deviceId: uuid ?? '', deviceName: name ?? ''),
];

View File

@ -29,7 +29,7 @@ class TwoGangSwitchBloc extends Bloc<TwoGangSwitchEvent, TwoGangSwitchState> {
final status =
await DevicesManagementApi().getDeviceStatus(event.deviceId);
deviceStatus = TwoGangStatusModel.fromJson(event.deviceId, status.status);
_listenToChanges(emit);
_listenToChanges(event.deviceId);
emit(TwoGangSwitchStatusLoaded(deviceStatus));
} catch (e) {
emit(TwoGangSwitchError(e.toString()));

View File

@ -30,7 +30,7 @@ class WallSensorBloc extends Bloc<WallSensorEvent, WallSensorState> {
var response = await DevicesManagementApi().getDeviceStatus(deviceId);
deviceStatus = WallSensorModel.fromJson(response.status);
emit(WallSensorUpdateState(wallSensorModel: deviceStatus));
_listenToChanges(emit);
_listenToChanges(emit, deviceId);
} catch (e) {
emit(WallSensorFailedState(error: e.toString()));
return;
@ -52,7 +52,7 @@ class WallSensorBloc extends Bloc<WallSensorEvent, WallSensorState> {
}
}
_listenToChanges(Emitter<WallSensorState> emit) {
_listenToChanges(Emitter<WallSensorState> emit, deviceId) {
try {
DatabaseReference ref =
FirebaseDatabase.instance.ref('device-status/$deviceId');

View File

@ -41,6 +41,11 @@ class DeviceDialogHelper {
final deviceSelectedFunctions =
routineBloc.state.selectedFunctions[data['uniqueCustomId']] ?? [];
if (removeComparetors) {
//remove the current temp function in the 'if container'
functions.removeAt(3);
}
switch (productType) {
case 'AC':
return ACHelper.showACFunctionsDialog(context, functions, data['device'],

View File

@ -151,3 +151,32 @@ class ChildLockFunction extends ACFunction {
),
];
}
class CurrentTempFunction extends ACFunction {
final int min;
final int max;
final int step;
CurrentTempFunction({required super.deviceId, required super.deviceName})
: min = -100,
max = 990,
step = 1,
super(
code: 'temp_current',
operationName: 'Current Temperature',
icon: Assets.currentTemp,
);
@override
List<ACOperationalValue> getOperationalValues() {
List<ACOperationalValue> values = [];
for (int temp = min; temp <= max; temp += step) {
values.add(ACOperationalValue(
icon: Assets.currentTemp,
description: "${temp / 10}°C",
value: temp,
));
}
return values;
}
}

View File

@ -329,8 +329,8 @@ class ACHelper {
) {
return Slider(
value: initialValue is int ? initialValue.toDouble() : 200.0,
min: 200,
max: 300,
min: selectCode == 'temp_current' ? -100 : 200,
max: selectCode == 'temp_current' ? 900 : 300,
divisions: 10,
label: '${((initialValue ?? 160) / 10).toInt()}°C',
onChanged: (value) {

View File

@ -83,7 +83,7 @@ class CustomExpansionTileSpaceTree extends StatelessWidget {
),
if (isExpanded && children != null && children!.isNotEmpty)
Padding(
padding: const EdgeInsets.only(left: 48.0),
padding: const EdgeInsets.only(left: 24.0),
child: Column(
children: children ?? [],
),

View File

@ -33,14 +33,13 @@ class _SpaceTreeViewState extends State<SpaceTreeView> {
@override
Widget build(BuildContext context) {
return BlocBuilder<SpaceTreeBloc, SpaceTreeState>(
builder: (context, state) {
List<CommunityModel> list =
state.isSearching ? state.filteredCommunity : state.communityList;
return BlocBuilder<SpaceTreeBloc, SpaceTreeState>(builder: (context, state) {
List<CommunityModel> list = state.isSearching ? state.filteredCommunity : state.communityList;
return Container(
height: MediaQuery.sizeOf(context).height,
decoration:
widget.isSide == true ? subSectionContainerDecoration : null,
decoration: widget.isSide == true
? subSectionContainerDecoration.copyWith(color: ColorsManager.whiteColors)
: const BoxDecoration(color: ColorsManager.whiteColors),
child: state is SpaceTreeLoadingState
? const Center(child: CircularProgressIndicator())
: Column(
@ -50,8 +49,7 @@ class _SpaceTreeViewState extends State<SpaceTreeView> {
decoration: const BoxDecoration(
color: ColorsManager.circleRolesBackground,
borderRadius: BorderRadius.only(
topRight: Radius.circular(20),
topLeft: Radius.circular(20)),
topRight: Radius.circular(20), topLeft: Radius.circular(20)),
),
child: Padding(
padding: const EdgeInsets.all(8.0),
@ -60,35 +58,27 @@ class _SpaceTreeViewState extends State<SpaceTreeView> {
Expanded(
child: Container(
decoration: BoxDecoration(
borderRadius: const BorderRadius.all(
Radius.circular(20)),
border: Border.all(
color: ColorsManager.grayBorder)),
borderRadius: const BorderRadius.all(Radius.circular(20)),
border: Border.all(color: ColorsManager.grayBorder)),
child: TextFormField(
style:
const TextStyle(color: Colors.black),
style: const TextStyle(color: Colors.black),
onChanged: (value) {
context
.read<SpaceTreeBloc>()
.add(SearchQueryEvent(value));
context.read<SpaceTreeBloc>().add(SearchQueryEvent(value));
},
decoration: textBoxDecoration(radios: 20)!
.copyWith(
decoration: textBoxDecoration(radios: 20)!.copyWith(
fillColor: Colors.white,
suffixIcon: Padding(
padding:
const EdgeInsets.only(right: 16),
padding: const EdgeInsets.only(right: 16),
child: SvgPicture.asset(
Assets.textFieldSearch,
width: 24,
height: 24,
),
),
hintStyle: context.textTheme.bodyMedium
?.copyWith(
fontWeight: FontWeight.w400,
fontSize: 12,
color: ColorsManager.textGray),
hintStyle: context.textTheme.bodyMedium?.copyWith(
fontWeight: FontWeight.w400,
fontSize: 12,
color: ColorsManager.textGray),
),
),
),
@ -99,9 +89,7 @@ class _SpaceTreeViewState extends State<SpaceTreeView> {
)
: CustomSearchBar(
onSearchChanged: (query) {
context
.read<SpaceTreeBloc>()
.add(SearchQueryEvent(query));
context.read<SpaceTreeBloc>().add(SearchQueryEvent(query));
},
),
const SizedBox(height: 16),
@ -117,18 +105,14 @@ class _SpaceTreeViewState extends State<SpaceTreeView> {
? Center(
child: Text(
'No results found',
style: Theme.of(context)
.textTheme
.bodySmall!
.copyWith(
style: Theme.of(context).textTheme.bodySmall!.copyWith(
color: ColorsManager.lightGrayColor,
fontWeight: FontWeight.w400,
),
),
)
: Scrollbar(
scrollbarOrientation:
ScrollbarOrientation.left,
scrollbarOrientation: ScrollbarOrientation.left,
thumbVisibility: true,
controller: _scrollController,
child: Padding(
@ -138,39 +122,30 @@ class _SpaceTreeViewState extends State<SpaceTreeView> {
shrinkWrap: true,
children: list
.map(
(community) =>
CustomExpansionTileSpaceTree(
(community) => CustomExpansionTileSpaceTree(
title: community.name,
isSelected: state
.selectedCommunities
isSelected: state.selectedCommunities
.contains(community.uuid),
isSoldCheck: state
.selectedCommunities
isSoldCheck: state.selectedCommunities
.contains(community.uuid),
onExpansionChanged: () {
context
.read<SpaceTreeBloc>()
.add(OnCommunityExpanded(
community.uuid));
.add(OnCommunityExpanded(community.uuid));
},
isExpanded: state
.expandedCommunities
isExpanded: state.expandedCommunities
.contains(community.uuid),
onItemSelected: () {
context
.read<SpaceTreeBloc>()
.add(OnCommunitySelected(
community.uuid,
community.spaces));
context.read<SpaceTreeBloc>().add(
OnCommunitySelected(
community.uuid, community.spaces));
widget.onSelect();
},
children:
community.spaces.map((space) {
children: community.spaces.map((space) {
return CustomExpansionTileSpaceTree(
title: space.name,
isExpanded: state
.expandedSpaces
.contains(space.uuid),
isExpanded:
state.expandedSpaces.contains(space.uuid),
onItemSelected: () {
context.read<SpaceTreeBloc>().add(
OnSpaceSelected(community, space.uuid ?? '',
@ -178,20 +153,14 @@ class _SpaceTreeViewState extends State<SpaceTreeView> {
widget.onSelect();
},
onExpansionChanged: () {
context
.read<SpaceTreeBloc>()
.add(OnSpaceExpanded(
community.uuid,
space.uuid ?? ''));
context.read<SpaceTreeBloc>().add(
OnSpaceExpanded(
community.uuid, space.uuid ?? ''));
},
isSelected: state
.selectedSpaces
.contains(
space.uuid) ||
state.soldCheck
.contains(space.uuid),
isSoldCheck: state.soldCheck
.contains(space.uuid),
isSelected:
state.selectedSpaces.contains(space.uuid) ||
state.soldCheck.contains(space.uuid),
isSoldCheck: state.soldCheck.contains(space.uuid),
children: _buildNestedSpaces(
context, state, space, community),
);
@ -279,8 +248,8 @@ class _SpaceTreeViewState extends State<SpaceTreeView> {
BuildContext context, SpaceTreeState state, SpaceModel space, CommunityModel community) {
return space.children.map((child) {
return CustomExpansionTileSpaceTree(
isSelected: state.selectedSpaces.contains(child.uuid) ||
state.soldCheck.contains(child.uuid),
isSelected:
state.selectedSpaces.contains(child.uuid) || state.soldCheck.contains(child.uuid),
isSoldCheck: state.soldCheck.contains(child.uuid),
title: child.name,
isExpanded: state.expandedSpaces.contains(child.uuid),

View File

@ -1,4 +1,5 @@
import 'dart:async';
import 'package:flutter/widgets.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:syncrow_web/pages/common/bloc/project_manager.dart';
import 'package:syncrow_web/pages/space_tree/bloc/space_tree_bloc.dart';
@ -52,14 +53,24 @@ class SpaceManagementBloc extends Bloc<SpaceManagementEvent, SpaceManagementStat
Future<void> _updateSpaceModelCache(
UpdateSpaceModelCache event, Emitter<SpaceManagementState> emit) async {
if (_cachedSpaceModels != null) {
_cachedSpaceModels = _cachedSpaceModels!.map((model) {
return model.uuid == event.updatedModel.uuid ? event.updatedModel : model;
}).toList();
} else {
_cachedSpaceModels = await fetchSpaceModels();
final projectUuid = await ProjectManager.getProjectUUID() ?? '';
List<SpaceTemplateModel> allSpaceModels = [];
bool hasNext = true;
int page = 1;
while (hasNext) {
final spaceModels = await _spaceModelApi.listSpaceModels(page: page, projectId: projectUuid);
if (spaceModels.isNotEmpty) {
allSpaceModels.addAll(spaceModels);
page++;
} else {
hasNext = false;
}
}
_cachedSpaceModels = allSpaceModels;
await fetchTags();
emit(SpaceModelLoaded(
@ -447,7 +458,8 @@ class SpaceManagementBloc extends Bloc<SpaceManagementEvent, SpaceManagementStat
emit(SpaceManagementLoading());
try {
final updatedSpaces = await saveSpacesHierarchically(event.spaces, event.communityUuid);
final updatedSpaces =
await saveSpacesHierarchically(event.context, event.spaces, event.communityUuid);
final allSpaces = await _fetchSpacesForCommunity(event.communityUuid);
@ -498,9 +510,14 @@ class SpaceManagementBloc extends Bloc<SpaceManagementEvent, SpaceManagementStat
}
Future<List<SpaceModel>> saveSpacesHierarchically(
List<SpaceModel> spaces, String communityUuid) async {
BuildContext context, List<SpaceModel> spaces, String communityUuid) async {
final orderedSpaces = flattenHierarchy(spaces);
final projectUuid = await ProjectManager.getProjectUUID() ?? '';
var spaceBloc = context.read<SpaceTreeBloc>();
List<CommunityModel> communities = spaceBloc.state.communityList;
CommunityModel? selectedCommunity = communities.firstWhere(
(community) => community.uuid == communityUuid,
);
final parentsToDelete = orderedSpaces.where((space) =>
space.status == SpaceStatus.deleted &&
@ -522,7 +539,13 @@ class SpaceManagementBloc extends Bloc<SpaceManagementEvent, SpaceManagementStat
if (space.uuid != null && space.uuid!.isNotEmpty) {
List<TagModelUpdate> tagUpdates = [];
final prevSpace = await _api.getSpace(communityUuid, space.uuid!, projectUuid);
List<SpaceModel> matchedSpaces =
selectedCommunity.spaces.where((space) => space.uuid == space.uuid).toList();
if (matchedSpaces.isEmpty) continue;
final prevSpace = matchedSpaces[0];
final List<UpdateSubspaceTemplateModel> subspaceUpdates = [];
final List<SubspaceModel>? prevSubspaces = prevSpace?.subspaces;
final List<SubspaceModel>? newSubspaces = space.subspaces;
@ -599,6 +622,7 @@ class SpaceManagementBloc extends Bloc<SpaceManagementEvent, SpaceManagementStat
subspaces: subspaceUpdates,
tags: tagUpdates,
direction: space.incomingConnection?.direction,
spaceModelUuid: space.spaceModel?.uuid,
projectId: projectUuid);
} else {
// Call create if the space does not have a UUID

View File

@ -58,14 +58,16 @@ class CreateSpaceEvent extends SpaceManagementEvent {
class SaveSpacesEvent extends SpaceManagementEvent {
final List<SpaceModel> spaces;
final String communityUuid;
final BuildContext context;
const SaveSpacesEvent({
const SaveSpacesEvent(
this.context, {
required this.spaces,
required this.communityUuid,
});
@override
List<Object> get props => [spaces, communityUuid];
List<Object> get props => [spaces, communityUuid, context];
}
class UpdateSpacePositionEvent extends SpaceManagementEvent {
@ -170,4 +172,4 @@ class UpdateSpaceModelCache extends SpaceManagementEvent {
class DeleteSpaceModelFromCache extends SpaceManagementEvent {
final String deletedUuid;
DeleteSpaceModelFromCache(this.deletedUuid);
}
}

View File

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

View File

@ -22,7 +22,7 @@ class CommunityTile extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.only(left: 16.0),
padding: const EdgeInsets.all(8.0),
child: CustomExpansionTile(
title: title,
initiallyExpanded: isExpanded,

View File

@ -297,6 +297,8 @@ class CreateSpaceDialogState extends State<CreateSpaceDialog> {
),
onDeleted: () => setState(() {
this.selectedSpaceModel = null;
subspaces = widget.subspaces ?? [];
tags = widget.tags ?? [];
})),
],
),

View File

@ -1,5 +1,6 @@
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:syncrow_web/pages/space_tree/bloc/space_tree_bloc.dart';
import 'package:syncrow_web/pages/space_tree/view/space_tree_view.dart';
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/community_model.dart';
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/product_model.dart';
@ -80,32 +81,32 @@ class _LoadedSpaceViewState extends State<LoadedSpaceView> {
clipBehavior: Clip.none,
children: [
widget.shouldNavigateToSpaceModelPage
? _spaceModels.isNotEmpty
? Row(
children: [
SizedBox(width: 300, child: SpaceTreeView(onSelect: () {})),
Expanded(
child: BlocProvider(
create: (context) => SpaceModelBloc(
api: SpaceModelManagementApi(),
initialSpaceModels: _spaceModels,
),
child: SpaceModelPage(
products: widget.products,
onSpaceModelsUpdated: _onSpaceModelsUpdated,
projectTags: widget.projectTags,
),
),
? Row(
children: [
SizedBox(width: 300, child: SpaceTreeView(onSelect: () {})),
Expanded(
child: BlocProvider(
create: (context) => SpaceModelBloc(
BlocProvider.of<SpaceTreeBloc>(context),
api: SpaceModelManagementApi(),
initialSpaceModels: widget.spaceModels ?? [],
),
],
)
: const Center(child: CircularProgressIndicator())
child: SpaceModelPage(
products: widget.products,
onSpaceModelsUpdated: _onSpaceModelsUpdated,
projectTags: widget.projectTags,
),
),
),
],
)
: Row(
children: [
SidebarWidget(
communities: widget.communities,
selectedSpaceUuid:
widget.selectedSpace?.uuid ?? widget.selectedCommunity?.uuid ?? '',
selectedSpaceUuid: widget.selectedSpace?.uuid ??
widget.selectedCommunity?.uuid ??
'',
),
CommunityStructureArea(
selectedCommunity: widget.selectedCommunity,

View File

@ -205,30 +205,32 @@ class _SidebarWidgetState extends State<SidebarWidget> {
);
}
Widget _buildSpaceTile(SpaceModel space, CommunityModel community) {
Widget _buildSpaceTile(SpaceModel space, CommunityModel community, {int depth = 1}) {
bool isExpandedSpace = _isSpaceOrChildSelected(space);
return SpaceTile(
title: space.name,
key: ValueKey(space.uuid),
isSelected: _selectedId == space.uuid,
initiallyExpanded: isExpandedSpace,
onExpansionChanged: (bool expanded) {
_handleExpansionChange(space.uuid ?? '', expanded);
},
onItemSelected: () {
setState(() {
_selectedId = space.uuid;
_selectedSpaceUuid = space.uuid;
});
return Padding(
padding: EdgeInsets.only(left: depth * 16.0),
child: SpaceTile(
title: space.name,
key: ValueKey(space.uuid),
isSelected: _selectedId == space.uuid,
initiallyExpanded: isExpandedSpace,
onExpansionChanged: (bool expanded) {
_handleExpansionChange(space.uuid ?? '', expanded);
},
onItemSelected: () {
setState(() {
_selectedId = space.uuid;
_selectedSpaceUuid = space.uuid;
});
context.read<SpaceManagementBloc>().add(
SelectSpaceEvent(selectedCommunity: community, selectedSpace: space),
);
},
children: space.children.isNotEmpty
? space.children.map((childSpace) => _buildSpaceTile(childSpace, community)).toList()
: [], // Recursively render child spaces if available
);
context.read<SpaceManagementBloc>().add(
SelectSpaceEvent(selectedCommunity: community, selectedSpace: space),
);
},
children: space.children.isNotEmpty
? space.children.map((childSpace) => _buildSpaceTile(childSpace, community)).toList()
: [], // Recursively render child spaces if available
));
}
void _handleExpansionChange(String uuid, bool expanded) {}

View File

@ -35,18 +35,20 @@ class _SpaceTileState extends State<SpaceTile> {
@override
Widget build(BuildContext context) {
return CustomExpansionTile(
isSelected: widget.isSelected,
title: widget.title,
initiallyExpanded: _isExpanded,
onItemSelected: widget.onItemSelected,
onExpansionChanged: (bool expanded) {
setState(() {
_isExpanded = expanded;
});
widget.onExpansionChanged(expanded);
},
children: widget.children ?? [],
);
return Padding(
padding: const EdgeInsets.only(left: 8.0, right: 8.0, top: 8.0),
child: CustomExpansionTile(
isSelected: widget.isSelected,
title: widget.title,
initiallyExpanded: _isExpanded,
onItemSelected: widget.onItemSelected,
onExpansionChanged: (bool expanded) {
setState(() {
_isExpanded = expanded;
});
widget.onExpansionChanged(expanded);
},
children: widget.children ?? [],
));
}
}

View File

@ -28,10 +28,11 @@ class LinkSpaceToModelBloc
try {
BuildContext context = NavigationService.navigatorKey.currentContext!;
var spaceBloc = context.read<SpaceTreeBloc>();
spacesListIds.clear();
for (var communityId in spaceBloc.state.selectedCommunities) {
List<String> spacesList =
spaceBloc.state.selectedCommunityAndSpaces[communityId] ?? [];
spacesListIds = spacesList;
spacesListIds.addAll(spacesList);
}
hasSelectedSpaces =
spaceBloc.state.selectedCommunities.any((communityId) {

View File

@ -5,6 +5,8 @@ abstract class LinkSpaceToModelState {
class SpaceModelInitial extends LinkSpaceToModelState {}
class SpaceModelLoading extends LinkSpaceToModelState {}
class LinkSpaceModelLoading extends LinkSpaceToModelState {}
class SpaceModelSelectedState extends LinkSpaceToModelState {
final int selectedIndex;

View File

@ -1,7 +1,7 @@
import 'dart:developer';
import 'package:flutter_bloc/flutter_bloc.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_event.dart';
import 'package:syncrow_web/pages/spaces_management/space_model/bloc/space_model_event.dart';
import 'package:syncrow_web/pages/spaces_management/space_model/bloc/space_model_state.dart';
import 'package:syncrow_web/pages/spaces_management/space_model/models/space_template_model.dart';
@ -9,8 +9,10 @@ import 'package:syncrow_web/services/space_model_mang_api.dart';
class SpaceModelBloc extends Bloc<SpaceModelEvent, SpaceModelState> {
final SpaceModelManagementApi api;
final SpaceTreeBloc _spaceTreeBloc;
SpaceModelBloc({
SpaceModelBloc(
this._spaceTreeBloc, {
required this.api,
required List<SpaceTemplateModel> initialSpaceModels,
}) : super(SpaceModelLoaded(spaceModels: initialSpaceModels)) {
@ -50,6 +52,7 @@ class SpaceModelBloc extends Bloc<SpaceModelEvent, SpaceModelState> {
final updatedSpaceModels = currentState.spaceModels.map((model) {
return model.uuid == event.spaceModelUuid ? newSpaceModel : model;
}).toList();
_spaceTreeBloc.add(InitialEvent());
emit(SpaceModelLoaded(spaceModels: updatedSpaceModels));
}
} catch (e) {
@ -71,7 +74,7 @@ class SpaceModelBloc extends Bloc<SpaceModelEvent, SpaceModelState> {
final updatedSpaceModels = currentState.spaceModels
.where((model) => model.uuid != event.spaceModelUuid)
.toList();
_spaceTreeBloc.add(InitialEvent());
emit(SpaceModelLoaded(spaceModels: updatedSpaceModels));
}
} catch (e) {

View File

@ -11,7 +11,7 @@ class SpaceTemplateModel extends Equatable {
List<SubspaceTemplateModel>? subspaceModels;
final List<Tag>? tags;
String internalId;
String? createdAt;
DateTime? createdAt;
@override
List<Object?> get props => [modelName, subspaceModels, tags];
@ -24,17 +24,18 @@ class SpaceTemplateModel extends Equatable {
this.tags,
this.createdAt,
}) : internalId = internalId ?? const Uuid().v4();
factory SpaceTemplateModel.fromJson(Map<String, dynamic> json) {
final String internalId = json['internalId'] ?? const Uuid().v4();
return SpaceTemplateModel(
uuid: json['uuid'] ?? '',
createdAt: json['createdAt'] ?? '',
createdAt: json['createdAt'] != null
? DateTime.tryParse(json['createdAt'])
: null,
internalId: internalId,
modelName: json['modelName'] ?? '',
subspaceModels: (json['subspaceModels'] as List<dynamic>?)
?.where((e) => e is Map<String, dynamic>) // Validate type
?.where((e) => e is Map<String, dynamic>)
.map((e) =>
SubspaceTemplateModel.fromJson(e as Map<String, dynamic>))
.toList() ??

View File

@ -1,5 +1,6 @@
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:intl/intl.dart';
import 'package:syncrow_web/pages/space_tree/view/space_tree_view.dart';
import 'package:syncrow_web/pages/spaces_management/link_space_model/bloc/link_space_to_model_bloc.dart';
import 'package:syncrow_web/pages/spaces_management/link_space_model/bloc/link_space_to_model_event.dart';
@ -45,6 +46,12 @@ class _LinkSpaceModelSpacesDialogState
}
Widget _buildDialogContent() {
widget.spaceModel.createdAt.toString();
String formattedDate =
DateFormat('yyyy-MM-dd').format(widget.spaceModel.createdAt!);
String formattedTime =
DateFormat('HH:mm:ss').format(widget.spaceModel.createdAt!);
return Expanded(
child: Padding(
padding: const EdgeInsets.all(15.0),
@ -75,8 +82,31 @@ class _LinkSpaceModelSpacesDialogState
const SizedBox(height: 16),
_buildDetailRow(
"Space model name:", widget.spaceModel.modelName),
_buildDetailRow("Creation date and time:",
widget.spaceModel.createdAt.toString()),
Padding(
padding: const EdgeInsets.symmetric(vertical: 4),
child: Row(
children: [
const Expanded(
child: Text(
"Creation date and time:",
style: const TextStyle(
fontWeight: FontWeight.bold),
),
),
const SizedBox(width: 8),
Expanded(
child: Text(
"$formattedDate $formattedTime",
style: const TextStyle(
fontWeight: FontWeight.bold,
color: Colors.black),
),
),
],
),
),
// _buildDetailRow("Creation date and time:",
// widget.spaceModel.createdAt.toString()),
_buildDetailRow("Created by:", "Admin"),
const SizedBox(height: 12),
const Text(

View File

@ -18,8 +18,10 @@ import 'package:syncrow_web/pages/spaces_management/space_model/widgets/dynamic_
import 'package:syncrow_web/pages/spaces_management/space_model/widgets/dynamic_room_widget.dart';
import 'package:syncrow_web/utils/color_manager.dart';
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/string_utils.dart';
class SpaceModelCardWidget extends StatelessWidget {
class SpaceModelCardWidget extends StatelessWidget with HelperResponsiveLayout {
final SpaceTemplateModel model;
final BuildContext? pageContext;
final bool topActionsDisabled;
@ -76,120 +78,109 @@ class SpaceModelCardWidget extends StatelessWidget {
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
model.modelName,
style: Theme.of(context).textTheme.headlineMedium?.copyWith(
color: Colors.black,
fontWeight: FontWeight.bold,
),
maxLines: 1,
overflow: TextOverflow.ellipsis,
Expanded(
child: Text(
StringUtils.capitalizeFirstLetter(model.modelName),
style: Theme.of(context).textTheme.headlineMedium?.copyWith(
color: ColorsManager.blackColor,
fontWeight: FontWeight.bold,
fontSize: isSmallScreenSize(context) ? 13 : 20,
),
maxLines: 2,
overflow: TextOverflow.ellipsis,
),
),
Row(
children: [
InkWell(
onTap: () {
showDialog(
context: context,
builder: (BuildContext dialogContext) {
return BlocProvider<LinkSpaceToModelBloc>(
create: (_) => LinkSpaceToModelBloc(),
child: BlocListener<LinkSpaceToModelBloc,
LinkSpaceToModelState>(
listener: (context, state) {
final _bloc =
BlocProvider.of<LinkSpaceToModelBloc>(
context);
if (state is SpaceModelLoading) {
showDialog(
context: context,
barrierDismissible: false,
builder: (BuildContext context) {
return Dialog(
shape: RoundedRectangleBorder(
borderRadius:
BorderRadius.circular(20)),
elevation: 10,
backgroundColor: Colors.white,
child: Padding(
padding:
const EdgeInsets.symmetric(
vertical: 30,
horizontal: 50),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
CustomLoadingIndicator(),
const SizedBox(height: 20),
const Text(
"Linking in progress",
style: TextStyle(
fontSize: 16,
fontWeight:
FontWeight.w500,
color: Colors.black87,
),
),
],
if (!topActionsDisabled)
Row(
children: [
InkWell(
onTap: () {
showDialog(
context: context,
builder: (BuildContext dialogContext) {
return BlocProvider<LinkSpaceToModelBloc>(
create: (_) => LinkSpaceToModelBloc(),
child: BlocListener<LinkSpaceToModelBloc, LinkSpaceToModelState>(
listenWhen: (previous, current) {
return previous != current;
},
listener: (context, state) {
final _bloc = BlocProvider.of<LinkSpaceToModelBloc>(context);
if (state is SpaceModelLoading) {
showDialog(
context: context,
barrierDismissible: false,
builder: (BuildContext context) {
return Dialog(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(20)),
elevation: 10,
backgroundColor: Colors.white,
child: Padding(
padding: const EdgeInsets.symmetric(
vertical: 30, horizontal: 50),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
CustomLoadingIndicator(),
],
),
),
),
);
},
);
} else if (state
is AlreadyHaveLinkedState) {
Navigator.of(dialogContext).pop();
showOverwriteDialog(
context, _bloc, model);
} else if (state
is SpaceValidationSuccess) {
_bloc.add(LinkSpaceModelEvent(
isOverWrite: false,
selectedSpaceMode: model.uuid));
);
},
);
} else if (state is AlreadyHaveLinkedState) {
Navigator.of(dialogContext).pop();
showOverwriteDialog(context, _bloc, model);
} else if (state is SpaceValidationSuccess) {
_bloc.add(LinkSpaceModelEvent(
isOverWrite: false, selectedSpaceMode: model.uuid));
Future.delayed(const Duration(seconds: 1),
() {
Navigator.of(dialogContext).pop();
Navigator.of(dialogContext).pop();
Navigator.of(dialogContext).pop();
});
showDialog(
context: context,
builder: (BuildContext dialogContext) {
return const LinkingSuccessful();
},
).then((v) {
Future.delayed(
const Duration(seconds: 2), () {
Future.delayed(const Duration(seconds: 1), () {
Navigator.of(dialogContext).pop();
Navigator.of(dialogContext).pop();
Navigator.of(dialogContext).pop();
});
});
} else if (state is SpaceModelLinkSuccess) {
Navigator.of(dialogContext).pop();
Navigator.of(dialogContext).pop();
showDialog(
context: context,
builder: (BuildContext dialogContext) {
return const LinkingSuccessful();
},
);
}
},
child: LinkSpaceModelSpacesDialog(
spaceModel: model,
showDialog(
context: context,
builder: (BuildContext dialogContext) {
return const LinkingSuccessful();
},
).then((v) {
Future.delayed(const Duration(seconds: 2), () {
Navigator.of(dialogContext).pop();
});
});
} else if (state is SpaceModelLinkSuccess) {
Navigator.of(dialogContext).pop();
Navigator.of(dialogContext).pop();
showDialog(
context: context,
barrierDismissible: false,
builder: (BuildContext successDialogContext) {
Future.delayed(const Duration(seconds: 2), () {
Navigator.of(successDialogContext).pop();
});
return const LinkingSuccessful();
},
);
}
},
child: LinkSpaceModelSpacesDialog(
spaceModel: model,
),
),
),
);
},
);
},
child: SvgPicture.asset(
Assets.spaceLinkIcon,
fit: BoxFit.contain,
);
},
);
},
child: SvgPicture.asset(
Assets.spaceLinkIcon,
fit: BoxFit.contain,
),
),
),
if (!topActionsDisabled)
InkWell(
onTap: () {
_showDeleteDialog(context);
@ -199,49 +190,8 @@ class SpaceModelCardWidget extends StatelessWidget {
fit: BoxFit.contain,
),
),
],
),
// Expanded(
// child: Text(
// model.modelName,
// style:
// Theme.of(context).textTheme.headlineMedium?.copyWith(
// color: Colors.black,
// fontWeight: FontWeight.bold,
// ),
// maxLines: 1,
// overflow: TextOverflow.ellipsis,
// ),
// ),
// if (!topActionsDisabled)
// GestureDetector(
// onTap: () => _showDeleteDialog(context),
// child: Container(
// width: 36, // Adjust size as needed
// height: 36,
// decoration: BoxDecoration(
// shape: BoxShape.circle,
// color: Colors.white,
// boxShadow: [
// BoxShadow(
// color: Colors.black.withOpacity(0.1),
// spreadRadius: 2,
// blurRadius: 5,
// offset: const Offset(0, 2),
// ),
// ],
// ),
// child: Center(
// child: SvgPicture.asset(
// Assets.deleteSpaceModel, // Your actual SVG path
// width: 20,
// height: 20,
// colorFilter: const ColorFilter.mode(
// Colors.grey, BlendMode.srcIn),
// ),
// ),
// ),
// ),
],
),
],
),
if (!showOnlyName) ...[
@ -268,8 +218,7 @@ class SpaceModelCardWidget extends StatelessWidget {
),
),
),
if (productTagCount.isNotEmpty &&
model.subspaceModels != null)
if (productTagCount.isNotEmpty && model.subspaceModels != null)
Container(
width: 1.0,
color: ColorsManager.softGray,

View File

@ -6,7 +6,6 @@ import 'package:syncrow_web/pages/routines/models/routine_details_model.dart';
import 'package:syncrow_web/pages/routines/models/routine_model.dart';
import 'package:syncrow_web/services/api/http_service.dart';
import 'package:syncrow_web/utils/constants/api_const.dart';
import 'package:syncrow_web/utils/constants/temp_const.dart';
class SceneApi {
static final HTTPService _httpService = HTTPService();

View File

@ -227,6 +227,7 @@ class CommunitySpaceManagementApi {
required Offset position,
List<TagModelUpdate>? tags,
List<UpdateSubspaceTemplateModel>? subspaces,
String? spaceModelUuid,
required String projectId}) async {
try {
final body = {
@ -238,6 +239,7 @@ class CommunitySpaceManagementApi {
'icon': icon,
'subspace': subspaces,
'tags': tags,
'spaceModelUuid': spaceModelUuid,
};
if (parentId != null) {
body['parentUuid'] = parentId;

View File

@ -52,7 +52,7 @@ abstract class ColorsManager {
static const Color semiTransparentBlackColor = Color(0x3F000000);
static const Color transparentColor = Color(0x00000000);
static const Color spaceColor = Color(0xB2023DFE);
static const Color counterBackgroundColor = Color.fromARGB(204, 105, 2, 2);
static const Color counterBackgroundColor = Color(0xCCF4F4F4);
static const Color neutralGray = Color(0xFFE5E5E5);
static const Color warningRed = Color(0xFFFF6465);
static const Color borderColor = Color(0xFFE5E5E5);

View File

@ -0,0 +1,6 @@
class StringUtils {
static String capitalizeFirstLetter(String text) {
if (text.isEmpty) return text;
return text[0].toUpperCase() + text.substring(1);
}
}