mirror of
https://github.com/SyncrowIOT/web.git
synced 2025-07-10 07:07:19 +00:00
Merge branch 'dev' of https://github.com/SyncrowIOT/web into feat/update-space-model
This commit is contained in:
@ -20,7 +20,6 @@ class TreeView extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final _blocRole = BlocProvider.of<UsersBloc>(context);
|
||||
debugPrint('TreeView constructed with userId = $userId');
|
||||
return BlocProvider(
|
||||
create: (_) => UsersBloc(),
|
||||
// ..add(const LoadCommunityAndSpacesEvent()),
|
||||
|
@ -1,5 +1,6 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:flutter_svg/svg.dart';
|
||||
import 'package:syncrow_web/common/widgets/search_bar.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';
|
||||
@ -8,11 +9,14 @@ import 'package:syncrow_web/pages/space_tree/view/custom_expansion.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/community_model.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/space_model.dart';
|
||||
import 'package:syncrow_web/utils/color_manager.dart';
|
||||
import 'package:syncrow_web/utils/constants/assets.dart';
|
||||
import 'package:syncrow_web/utils/extension/build_context_x.dart';
|
||||
import 'package:syncrow_web/utils/style.dart';
|
||||
|
||||
class SpaceTreeView extends StatefulWidget {
|
||||
final bool? isSide;
|
||||
final Function onSelect;
|
||||
const SpaceTreeView({required this.onSelect, super.key});
|
||||
const SpaceTreeView({required this.onSelect, this.isSide, super.key});
|
||||
|
||||
@override
|
||||
State<SpaceTreeView> createState() => _SpaceTreeViewState();
|
||||
@ -29,21 +33,77 @@ 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: subSectionContainerDecoration,
|
||||
decoration:
|
||||
widget.isSide == true ? subSectionContainerDecoration : null,
|
||||
child: state is SpaceTreeLoadingState
|
||||
? const Center(child: CircularProgressIndicator())
|
||||
: Column(
|
||||
children: [
|
||||
CustomSearchBar(
|
||||
searchQuery: state.searchQuery,
|
||||
onSearchChanged: (query) {
|
||||
context.read<SpaceTreeBloc>().add(SearchQueryEvent(query));
|
||||
},
|
||||
),
|
||||
widget.isSide == true
|
||||
? Container(
|
||||
decoration: const BoxDecoration(
|
||||
color: ColorsManager.circleRolesBackground,
|
||||
borderRadius: BorderRadius.only(
|
||||
topRight: Radius.circular(20),
|
||||
topLeft: Radius.circular(20)),
|
||||
),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: const BorderRadius.all(
|
||||
Radius.circular(20)),
|
||||
border: Border.all(
|
||||
color: ColorsManager.grayBorder)),
|
||||
child: TextFormField(
|
||||
style:
|
||||
const TextStyle(color: Colors.black),
|
||||
onChanged: (value) {
|
||||
context
|
||||
.read<SpaceTreeBloc>()
|
||||
.add(SearchQueryEvent(value));
|
||||
},
|
||||
decoration: textBoxDecoration(radios: 20)!
|
||||
.copyWith(
|
||||
fillColor: Colors.white,
|
||||
suffixIcon: Padding(
|
||||
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),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
)
|
||||
: CustomSearchBar(
|
||||
onSearchChanged: (query) {
|
||||
context
|
||||
.read<SpaceTreeBloc>()
|
||||
.add(SearchQueryEvent(query));
|
||||
},
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
Expanded(
|
||||
child: ListView(
|
||||
@ -57,14 +117,18 @@ 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(
|
||||
@ -74,30 +138,39 @@ 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 ?? '',
|
||||
@ -105,14 +178,20 @@ 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),
|
||||
);
|
||||
@ -200,8 +279,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),
|
||||
|
@ -1,12 +0,0 @@
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/link_space_model/bloc/link_space_model_event.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/link_space_model/bloc/link_space_model_state.dart';
|
||||
|
||||
|
||||
class SpaceModelBloc extends Bloc<SpaceModelEvent, SpaceModelState> {
|
||||
SpaceModelBloc() : super(SpaceModelInitial()) {
|
||||
on<SpaceModelSelectedEvent>((event, emit) {
|
||||
emit(SpaceModelSelectedState(event.selectedIndex));
|
||||
});
|
||||
}
|
||||
}
|
@ -1,7 +0,0 @@
|
||||
abstract class SpaceModelEvent {}
|
||||
|
||||
class SpaceModelSelectedEvent extends SpaceModelEvent {
|
||||
final int selectedIndex;
|
||||
|
||||
SpaceModelSelectedEvent(this.selectedIndex);
|
||||
}
|
@ -1,9 +0,0 @@
|
||||
abstract class SpaceModelState {}
|
||||
|
||||
class SpaceModelInitial extends SpaceModelState {}
|
||||
|
||||
class SpaceModelSelectedState extends SpaceModelState {
|
||||
final int selectedIndex;
|
||||
|
||||
SpaceModelSelectedState(this.selectedIndex);
|
||||
}
|
@ -0,0 +1,104 @@
|
||||
import 'package:dio/dio.dart';
|
||||
import 'package:flutter/material.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';
|
||||
import 'package:syncrow_web/pages/spaces_management/link_space_model/bloc/link_space_to_model_event.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/link_space_model/bloc/link_space_to_model_state.dart';
|
||||
import 'package:syncrow_web/services/space_model_mang_api.dart';
|
||||
import 'package:syncrow_web/utils/navigation_service.dart';
|
||||
|
||||
class LinkSpaceToModelBloc
|
||||
extends Bloc<LinkSpaceToModelEvent, LinkSpaceToModelState> {
|
||||
LinkSpaceToModelBloc() : super(SpaceModelInitial()) {
|
||||
on<LinkSpaceModelSelectedIdsEvent>(_getSpaceIds);
|
||||
on<LinkSpaceModelEvent>(_handleLinkSpaceModel);
|
||||
on<ValidateSpaceModelEvent>(_validateLinkSpaceModel);
|
||||
on<LinkSpaceModelSelectedEvent>((event, emit) {
|
||||
emit(SpaceModelSelectedState(event.selectedIndex));
|
||||
});
|
||||
}
|
||||
|
||||
List<String> spacesListIds = [];
|
||||
bool hasSelectedSpaces = false;
|
||||
String validate = '';
|
||||
|
||||
Future<void> _getSpaceIds(LinkSpaceModelSelectedIdsEvent event,
|
||||
Emitter<LinkSpaceToModelState> emit) async {
|
||||
try {
|
||||
BuildContext context = NavigationService.navigatorKey.currentContext!;
|
||||
var spaceBloc = context.read<SpaceTreeBloc>();
|
||||
for (var communityId in spaceBloc.state.selectedCommunities) {
|
||||
List<String> spacesList =
|
||||
spaceBloc.state.selectedCommunityAndSpaces[communityId] ?? [];
|
||||
spacesListIds = spacesList;
|
||||
}
|
||||
hasSelectedSpaces =
|
||||
spaceBloc.state.selectedCommunities.any((communityId) {
|
||||
List<String> spacesList =
|
||||
spaceBloc.state.selectedCommunityAndSpaces[communityId] ?? [];
|
||||
return spacesList.isNotEmpty;
|
||||
});
|
||||
if (hasSelectedSpaces) {
|
||||
debugPrint("At least one space is selected.");
|
||||
} else {
|
||||
debugPrint("No spaces selected.");
|
||||
}
|
||||
} catch (e) {
|
||||
debugPrint("Error in _getSpaceIds: $e");
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _handleLinkSpaceModel(
|
||||
LinkSpaceModelEvent event,
|
||||
Emitter<LinkSpaceToModelState> emit,
|
||||
) async {
|
||||
emit(SpaceModelLoading());
|
||||
try {
|
||||
final projectUuid = await ProjectManager.getProjectUUID() ?? '';
|
||||
await SpaceModelManagementApi().linkSpaceModel(
|
||||
spaceModelUuid: event.selectedSpaceMode!,
|
||||
projectId: projectUuid,
|
||||
spaceUuids: spacesListIds,
|
||||
isOverWrite: event.isOverWrite);
|
||||
emit(SpaceModelLinkSuccess());
|
||||
} on DioException catch (e) {
|
||||
final errorMessage = _parseDioError(e);
|
||||
emit(SpaceModelOperationFailure(errorMessage));
|
||||
} catch (e) {
|
||||
emit(SpaceModelOperationFailure('Unexpected error: $e'));
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _validateLinkSpaceModel(
|
||||
ValidateSpaceModelEvent event,
|
||||
Emitter<LinkSpaceToModelState> emit,
|
||||
) async {
|
||||
emit(SpaceModelLoading());
|
||||
try {
|
||||
final projectUuid = await ProjectManager.getProjectUUID() ?? '';
|
||||
await SpaceModelManagementApi().validateSpaceModel(
|
||||
projectUuid,
|
||||
spacesListIds,
|
||||
);
|
||||
emit(SpaceValidationSuccess());
|
||||
} on DioException catch (e) {
|
||||
final errorMessage = _parseDioError(e);
|
||||
if (errorMessage ==
|
||||
'Selected spaces already have linked space model / sub-spaces and devices') {
|
||||
emit(const AlreadyHaveLinkedState());
|
||||
} else {
|
||||
emit(SpaceModelOperationFailure(errorMessage));
|
||||
}
|
||||
} catch (e) {
|
||||
emit(SpaceModelOperationFailure('Unexpected error: $e'));
|
||||
}
|
||||
}
|
||||
|
||||
String _parseDioError(DioException e) {
|
||||
if (e.response?.data is Map<String, dynamic>) {
|
||||
return e.response!.data['error']['message'] ?? 'Unknown error occurred';
|
||||
}
|
||||
return e.message ?? 'Network request failed';
|
||||
}
|
||||
}
|
@ -0,0 +1,22 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
abstract class LinkSpaceToModelEvent {}
|
||||
|
||||
class LinkSpaceModelSelectedEvent extends LinkSpaceToModelEvent {
|
||||
final int selectedIndex;
|
||||
|
||||
LinkSpaceModelSelectedEvent(this.selectedIndex);
|
||||
}
|
||||
|
||||
class LinkSpaceModelSelectedIdsEvent extends LinkSpaceToModelEvent {}
|
||||
|
||||
class LinkSpaceModelEvent extends LinkSpaceToModelEvent {
|
||||
final String? selectedSpaceMode;
|
||||
final bool isOverWrite;
|
||||
LinkSpaceModelEvent({this.selectedSpaceMode, this.isOverWrite = false});
|
||||
}
|
||||
|
||||
class ValidateSpaceModelEvent extends LinkSpaceToModelEvent {
|
||||
BuildContext? context;
|
||||
ValidateSpaceModelEvent({this.context});
|
||||
}
|
@ -0,0 +1,35 @@
|
||||
abstract class LinkSpaceToModelState {
|
||||
const LinkSpaceToModelState();
|
||||
}
|
||||
|
||||
class SpaceModelInitial extends LinkSpaceToModelState {}
|
||||
|
||||
class SpaceModelLoading extends LinkSpaceToModelState {}
|
||||
|
||||
class SpaceModelSelectedState extends LinkSpaceToModelState {
|
||||
final int selectedIndex;
|
||||
const SpaceModelSelectedState(this.selectedIndex);
|
||||
}
|
||||
|
||||
class SpaceModelSelectionUpdated extends LinkSpaceToModelState {
|
||||
final bool hasSelectedSpaces;
|
||||
const SpaceModelSelectionUpdated(this.hasSelectedSpaces);
|
||||
}
|
||||
|
||||
class SpaceValidationSuccess extends LinkSpaceToModelState {}
|
||||
|
||||
class SpaceModelLinkSuccess extends LinkSpaceToModelState {}
|
||||
|
||||
class ValidationError extends LinkSpaceToModelState {
|
||||
final String message;
|
||||
const ValidationError(this.message);
|
||||
}
|
||||
|
||||
class SpaceModelOperationFailure extends LinkSpaceToModelState {
|
||||
final String message;
|
||||
const SpaceModelOperationFailure(this.message);
|
||||
}
|
||||
|
||||
class AlreadyHaveLinkedState extends LinkSpaceToModelState {
|
||||
const AlreadyHaveLinkedState();
|
||||
}
|
@ -2,9 +2,9 @@ import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:syncrow_web/pages/common/buttons/cancel_button.dart';
|
||||
import 'package:syncrow_web/pages/common/buttons/default_button.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/link_space_model/bloc/link_space_model_bloc.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/link_space_model/bloc/link_space_model_event.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/link_space_model/bloc/link_space_model_state.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';
|
||||
import 'package:syncrow_web/pages/spaces_management/link_space_model/bloc/link_space_to_model_state.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/space_model/models/space_template_model.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/space_model/widgets/space_model_card_widget.dart';
|
||||
import 'package:syncrow_web/utils/color_manager.dart';
|
||||
@ -24,13 +24,13 @@ class LinkSpaceModelDialog extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return BlocProvider(
|
||||
create: (context) => SpaceModelBloc()
|
||||
create: (context) => LinkSpaceToModelBloc()
|
||||
..add(
|
||||
SpaceModelSelectedEvent(initialSelectedIndex ?? -1),
|
||||
LinkSpaceModelSelectedEvent(initialSelectedIndex ?? -1),
|
||||
),
|
||||
child: Builder(
|
||||
builder: (context) {
|
||||
final bloc = context.read<SpaceModelBloc>();
|
||||
final bloc = context.read<LinkSpaceToModelBloc>();
|
||||
return AlertDialog(
|
||||
backgroundColor: ColorsManager.whiteColors,
|
||||
title: const Text('Link a space model'),
|
||||
@ -39,7 +39,7 @@ class LinkSpaceModelDialog extends StatelessWidget {
|
||||
color: ColorsManager.textFieldGreyColor,
|
||||
width: MediaQuery.of(context).size.width * 0.7,
|
||||
height: MediaQuery.of(context).size.height * 0.6,
|
||||
child: BlocBuilder<SpaceModelBloc, SpaceModelState>(
|
||||
child: BlocBuilder<LinkSpaceToModelBloc, LinkSpaceToModelState>(
|
||||
builder: (context, state) {
|
||||
int selectedIndex = -1;
|
||||
if (state is SpaceModelSelectedState) {
|
||||
@ -59,7 +59,7 @@ class LinkSpaceModelDialog extends StatelessWidget {
|
||||
final isSelected = selectedIndex == index;
|
||||
return GestureDetector(
|
||||
onTap: () {
|
||||
bloc.add(SpaceModelSelectedEvent(index));
|
||||
bloc.add(LinkSpaceModelSelectedEvent(index));
|
||||
},
|
||||
child: Container(
|
||||
margin: const EdgeInsets.all(10.0),
|
||||
@ -93,7 +93,7 @@ class LinkSpaceModelDialog extends StatelessWidget {
|
||||
label: 'Cancel',
|
||||
),
|
||||
const SizedBox(width: 10),
|
||||
BlocBuilder<SpaceModelBloc, SpaceModelState>(
|
||||
BlocBuilder<LinkSpaceToModelBloc, LinkSpaceToModelState>(
|
||||
builder: (context, state) {
|
||||
final isEnabled = state is SpaceModelSelectedState &&
|
||||
state.selectedIndex >= 0;
|
||||
|
@ -11,6 +11,7 @@ class SpaceTemplateModel extends Equatable {
|
||||
List<SubspaceTemplateModel>? subspaceModels;
|
||||
final List<Tag>? tags;
|
||||
String internalId;
|
||||
String? createdAt;
|
||||
|
||||
@override
|
||||
List<Object?> get props => [modelName, subspaceModels, tags];
|
||||
@ -21,6 +22,7 @@ class SpaceTemplateModel extends Equatable {
|
||||
required this.modelName,
|
||||
this.subspaceModels,
|
||||
this.tags,
|
||||
this.createdAt,
|
||||
}) : internalId = internalId ?? const Uuid().v4();
|
||||
|
||||
factory SpaceTemplateModel.fromJson(Map<String, dynamic> json) {
|
||||
@ -28,6 +30,7 @@ class SpaceTemplateModel extends Equatable {
|
||||
|
||||
return SpaceTemplateModel(
|
||||
uuid: json['uuid'] ?? '',
|
||||
createdAt: json['createdAt'] ?? '',
|
||||
internalId: internalId,
|
||||
modelName: json['modelName'] ?? '',
|
||||
subspaceModels: (json['subspaceModels'] as List<dynamic>?)
|
||||
|
@ -0,0 +1,84 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:syncrow_web/utils/color_manager.dart';
|
||||
|
||||
class ConfirmMergeDialog extends StatelessWidget {
|
||||
const ConfirmMergeDialog({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return AlertDialog(
|
||||
backgroundColor: ColorsManager.whiteColors,
|
||||
title: Center(
|
||||
child: Text(
|
||||
'Merge',
|
||||
style: Theme.of(context)
|
||||
.textTheme
|
||||
.bodyMedium
|
||||
?.copyWith(fontWeight: FontWeight.w400, fontSize: 30),
|
||||
)),
|
||||
content: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
Text(
|
||||
'Are you sure you want to merge?',
|
||||
style: Theme.of(context)
|
||||
.textTheme
|
||||
.bodyMedium
|
||||
?.copyWith(fontWeight: FontWeight.w400, fontSize: 18),
|
||||
),
|
||||
const SizedBox(height: 25),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||
children: [
|
||||
Expanded(
|
||||
child: ElevatedButton(
|
||||
onPressed: () {
|
||||
Navigator.pop(context);
|
||||
},
|
||||
style: ElevatedButton.styleFrom(
|
||||
padding: const EdgeInsets.symmetric(vertical: 14),
|
||||
backgroundColor: ColorsManager.boxColor,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
),
|
||||
elevation: 3,
|
||||
),
|
||||
child: Text(
|
||||
"Cancel",
|
||||
style: Theme.of(context)
|
||||
.textTheme
|
||||
.bodyMedium
|
||||
?.copyWith(fontWeight: FontWeight.w400, fontSize: 16),
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 10),
|
||||
Expanded(
|
||||
child: ElevatedButton(
|
||||
onPressed: () {},
|
||||
style: ElevatedButton.styleFrom(
|
||||
padding: const EdgeInsets.symmetric(vertical: 14),
|
||||
backgroundColor: ColorsManager.secondaryColor,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
),
|
||||
elevation: 3,
|
||||
),
|
||||
child: Text(
|
||||
"Ok",
|
||||
style: Theme.of(context).textTheme.bodyMedium?.copyWith(
|
||||
fontWeight: FontWeight.w400,
|
||||
fontSize: 16,
|
||||
color: ColorsManager.whiteColors,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
@ -0,0 +1,104 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/space_model/widgets/dialog/linking_successful.dart';
|
||||
import 'package:syncrow_web/utils/color_manager.dart';
|
||||
|
||||
class ConfirmOverwriteDialog extends StatelessWidget {
|
||||
const ConfirmOverwriteDialog({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return AlertDialog(
|
||||
backgroundColor: ColorsManager.whiteColors,
|
||||
title: Center(
|
||||
child: Text(
|
||||
'Overwrite',
|
||||
style: Theme.of(context)
|
||||
.textTheme
|
||||
.bodyMedium
|
||||
?.copyWith(fontWeight: FontWeight.w400, fontSize: 30),
|
||||
)),
|
||||
content: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
Text(
|
||||
'Are you sure you want to overwrite?',
|
||||
style: Theme.of(context)
|
||||
.textTheme
|
||||
.bodyMedium
|
||||
?.copyWith(fontWeight: FontWeight.w400, fontSize: 18),
|
||||
),
|
||||
Center(
|
||||
child: Text(
|
||||
'Selected spaces already have linked space \nmodel / sub-spaces and devices',
|
||||
style: Theme.of(context).textTheme.bodyMedium?.copyWith(
|
||||
fontWeight: FontWeight.w400,
|
||||
fontSize: 14,
|
||||
color: ColorsManager.grayColor),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 25),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||
children: [
|
||||
Expanded(
|
||||
child: ElevatedButton(
|
||||
onPressed: () {
|
||||
Navigator.pop(context);
|
||||
},
|
||||
style: ElevatedButton.styleFrom(
|
||||
padding: const EdgeInsets.symmetric(vertical: 14),
|
||||
backgroundColor: ColorsManager.boxColor,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
),
|
||||
elevation: 3,
|
||||
),
|
||||
child: Text(
|
||||
"Cancel",
|
||||
style: Theme.of(context)
|
||||
.textTheme
|
||||
.bodyMedium
|
||||
?.copyWith(fontWeight: FontWeight.w400, fontSize: 16),
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 10),
|
||||
Expanded(
|
||||
child: ElevatedButton(
|
||||
onPressed: () {
|
||||
Navigator.pop(context);
|
||||
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (BuildContext dialogContext) {
|
||||
return const LinkingSuccessful();
|
||||
},
|
||||
);
|
||||
},
|
||||
style: ElevatedButton.styleFrom(
|
||||
padding: const EdgeInsets.symmetric(vertical: 14),
|
||||
backgroundColor: ColorsManager.secondaryColor,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
),
|
||||
elevation: 3,
|
||||
),
|
||||
child: Text(
|
||||
"Ok",
|
||||
style: Theme.of(context).textTheme.bodyMedium?.copyWith(
|
||||
fontWeight: FontWeight.w400,
|
||||
fontSize: 16,
|
||||
color: ColorsManager.whiteColors,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
@ -0,0 +1,75 @@
|
||||
import 'dart:math';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/space_model/widgets/dialog/linking_successful.dart';
|
||||
|
||||
|
||||
class CustomLoadingIndicator extends StatefulWidget {
|
||||
@override
|
||||
_CustomLoadingIndicatorState createState() => _CustomLoadingIndicatorState();
|
||||
}
|
||||
|
||||
class _CustomLoadingIndicatorState extends State<CustomLoadingIndicator>
|
||||
with SingleTickerProviderStateMixin {
|
||||
late AnimationController _controller;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_controller = AnimationController(
|
||||
vsync: this,
|
||||
duration: const Duration(seconds: 1),
|
||||
)..repeat();
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_controller.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return SizedBox(
|
||||
width: 50,
|
||||
height: 50,
|
||||
child: AnimatedBuilder(
|
||||
animation: _controller,
|
||||
builder: (context, child) {
|
||||
return Transform.rotate(
|
||||
angle: _controller.value * 2 * pi,
|
||||
child: CustomPaint(
|
||||
painter: LoadingPainter(),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class LoadingPainter extends CustomPainter {
|
||||
@override
|
||||
void paint(Canvas canvas, Size size) {
|
||||
final Paint paint = Paint()
|
||||
..strokeWidth = 5
|
||||
..strokeCap = StrokeCap.round
|
||||
..style = PaintingStyle.stroke;
|
||||
|
||||
final double radius = size.width / 2;
|
||||
final Offset center = Offset(size.width / 2, size.height / 2);
|
||||
|
||||
for (int i = 0; i < 12; i++) {
|
||||
final double angle = (i * 30) * (pi / 180);
|
||||
final double startX = center.dx + radius * cos(angle);
|
||||
final double startY = center.dy + radius * sin(angle);
|
||||
final double endX = center.dx + (radius - 8) * cos(angle);
|
||||
final double endY = center.dy + (radius - 8) * sin(angle);
|
||||
|
||||
paint.color = Colors.blue.withOpacity(i / 12); // Gradient effect
|
||||
canvas.drawLine(Offset(startX, startY), Offset(endX, endY), paint);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
bool shouldRepaint(CustomPainter oldDelegate) => true;
|
||||
}
|
@ -0,0 +1,221 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.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';
|
||||
import 'package:syncrow_web/pages/spaces_management/space_model/models/space_template_model.dart';
|
||||
import 'package:syncrow_web/utils/color_manager.dart';
|
||||
|
||||
class LinkSpaceModelSpacesDialog extends StatefulWidget {
|
||||
final SpaceTemplateModel spaceModel;
|
||||
|
||||
const LinkSpaceModelSpacesDialog({super.key, required this.spaceModel});
|
||||
|
||||
@override
|
||||
State<LinkSpaceModelSpacesDialog> createState() =>
|
||||
_LinkSpaceModelSpacesDialogState();
|
||||
}
|
||||
|
||||
class _LinkSpaceModelSpacesDialogState
|
||||
extends State<LinkSpaceModelSpacesDialog> {
|
||||
@override
|
||||
void initState() {
|
||||
context.read<LinkSpaceToModelBloc>().add(LinkSpaceModelSelectedIdsEvent());
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return AlertDialog(
|
||||
contentPadding: EdgeInsets.zero,
|
||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(20)),
|
||||
backgroundColor: Colors.white,
|
||||
content: SizedBox(
|
||||
width: MediaQuery.of(context).size.width * 0.4,
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
_buildDialogContent(),
|
||||
_buildActionButtons(),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildDialogContent() {
|
||||
return Expanded(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(15.0),
|
||||
child: SizedBox(
|
||||
width: MediaQuery.of(context).size.width * 0.4,
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Expanded(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(15.0),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
const Center(
|
||||
child: Text(
|
||||
"Link Space Model to Spaces",
|
||||
style: TextStyle(
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Colors.blueAccent,
|
||||
),
|
||||
),
|
||||
),
|
||||
const Divider(),
|
||||
const SizedBox(height: 16),
|
||||
_buildDetailRow(
|
||||
"Space model name:", widget.spaceModel.modelName),
|
||||
_buildDetailRow("Creation date and time:",
|
||||
widget.spaceModel.createdAt.toString()),
|
||||
_buildDetailRow("Created by:", "Admin"),
|
||||
const SizedBox(height: 12),
|
||||
const Text(
|
||||
"Link to:",
|
||||
style: TextStyle(fontWeight: FontWeight.bold),
|
||||
),
|
||||
const Text(
|
||||
"Please select all the spaces where you would like to link the Routine.",
|
||||
style: TextStyle(fontSize: 12, color: Colors.grey),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
Expanded(
|
||||
child: SizedBox(
|
||||
child: Column(
|
||||
children: [
|
||||
Expanded(
|
||||
flex: 7,
|
||||
child: Container(
|
||||
color: ColorsManager.whiteColors,
|
||||
child: SpaceTreeView(
|
||||
isSide: true,
|
||||
onSelect: () {
|
||||
context
|
||||
.read<LinkSpaceToModelBloc>()
|
||||
.add(
|
||||
LinkSpaceModelSelectedIdsEvent());
|
||||
})))
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(
|
||||
height: 20,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildActionButtons() {
|
||||
return Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: Container(
|
||||
height: 50,
|
||||
decoration: const BoxDecoration(
|
||||
border: Border(
|
||||
right: BorderSide(
|
||||
color: ColorsManager.grayColor,
|
||||
width: 0.5,
|
||||
),
|
||||
top: BorderSide(
|
||||
color: ColorsManager.grayColor,
|
||||
width: 1,
|
||||
),
|
||||
),
|
||||
),
|
||||
child: _buildButton("Cancel", Colors.grey, () {
|
||||
Navigator.of(context).pop();
|
||||
}),
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: Container(
|
||||
height: 50,
|
||||
decoration: const BoxDecoration(
|
||||
border: Border(
|
||||
left: BorderSide(
|
||||
color: ColorsManager.grayColor,
|
||||
width: 0.5,
|
||||
),
|
||||
top: BorderSide(
|
||||
color: ColorsManager.grayColor,
|
||||
width: 1.0,
|
||||
),
|
||||
),
|
||||
),
|
||||
child: _buildButton(
|
||||
"Confirm",
|
||||
ColorsManager.onSecondaryColor,
|
||||
() {
|
||||
final spaceModelBloc = context.read<LinkSpaceToModelBloc>();
|
||||
if (!spaceModelBloc.hasSelectedSpaces) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
const SnackBar(
|
||||
content: Text("Please select at least one space")),
|
||||
);
|
||||
return;
|
||||
} else {
|
||||
spaceModelBloc.add(ValidateSpaceModelEvent(context: context));
|
||||
}
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Widget _buildDetailRow(String label, String value) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 4),
|
||||
child: Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: Text(
|
||||
label,
|
||||
style: const TextStyle(fontWeight: FontWeight.bold),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
Expanded(
|
||||
child: Text(
|
||||
value,
|
||||
style: const TextStyle(
|
||||
fontWeight: FontWeight.bold, color: Colors.black),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildButton(String text, Color color, VoidCallback onPressed) {
|
||||
return InkWell(
|
||||
onTap: onPressed,
|
||||
child: Center(
|
||||
child: Text(
|
||||
text,
|
||||
style:
|
||||
TextStyle(color: color, fontWeight: FontWeight.w400, fontSize: 14),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
@ -0,0 +1,110 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/space_model/widgets/dialog/confirm_merge_dialog.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/space_model/widgets/dialog/confirm_overwrite_dialog.dart';
|
||||
import 'package:syncrow_web/utils/color_manager.dart';
|
||||
|
||||
class LinkingAttentionDialog extends StatelessWidget {
|
||||
const LinkingAttentionDialog({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return AlertDialog(
|
||||
backgroundColor: ColorsManager.whiteColors,
|
||||
title: Center(
|
||||
child: Text(
|
||||
'Linking Attention',
|
||||
style: Theme.of(context)
|
||||
.textTheme
|
||||
.bodyMedium
|
||||
?.copyWith(fontWeight: FontWeight.w400, fontSize: 30),
|
||||
)),
|
||||
content: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
Text(
|
||||
'Do you want to merge or overwrite?',
|
||||
style: Theme.of(context)
|
||||
.textTheme
|
||||
.bodyMedium
|
||||
?.copyWith(fontWeight: FontWeight.w400, fontSize: 18),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
Text(
|
||||
'Selected spaces already have commissioned Devices',
|
||||
style: Theme.of(context)
|
||||
.textTheme
|
||||
.bodyMedium
|
||||
?.copyWith(fontWeight: FontWeight.w400, fontSize: 14),
|
||||
),
|
||||
const SizedBox(height: 25),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||
children: [
|
||||
// Cancel Button
|
||||
Expanded(
|
||||
child: ElevatedButton(
|
||||
onPressed: () {
|
||||
Navigator.pop(context);
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (BuildContext dialogContext) {
|
||||
return const ConfirmOverwriteDialog();
|
||||
},
|
||||
);
|
||||
},
|
||||
style: ElevatedButton.styleFrom(
|
||||
padding: const EdgeInsets.symmetric(vertical: 14),
|
||||
backgroundColor: ColorsManager.boxColor,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
),
|
||||
elevation: 3,
|
||||
),
|
||||
child: Text(
|
||||
"Overwrite",
|
||||
style: Theme.of(context)
|
||||
.textTheme
|
||||
.bodyMedium
|
||||
?.copyWith(fontWeight: FontWeight.w400, fontSize: 16),
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 10),
|
||||
|
||||
// OK Button
|
||||
Expanded(
|
||||
child: ElevatedButton(
|
||||
onPressed: () {
|
||||
Navigator.pop(context);
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (BuildContext dialogContext) {
|
||||
return const ConfirmMergeDialog();
|
||||
},
|
||||
);
|
||||
},
|
||||
style: ElevatedButton.styleFrom(
|
||||
padding: const EdgeInsets.symmetric(vertical: 14),
|
||||
backgroundColor: ColorsManager.boxColor,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
),
|
||||
elevation: 3,
|
||||
),
|
||||
child: Text(
|
||||
"Merge",
|
||||
style: Theme.of(context)
|
||||
.textTheme
|
||||
.bodyMedium
|
||||
?.copyWith(fontWeight: FontWeight.w400, fontSize: 16),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
@ -0,0 +1,33 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_svg/flutter_svg.dart';
|
||||
import 'package:syncrow_web/utils/color_manager.dart';
|
||||
import 'package:syncrow_web/utils/constants/assets.dart';
|
||||
|
||||
class LinkingSuccessful extends StatelessWidget {
|
||||
const LinkingSuccessful({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return AlertDialog(
|
||||
backgroundColor: ColorsManager.whiteColors,
|
||||
title: Center(
|
||||
child: SvgPicture.asset(
|
||||
Assets.successIcon,
|
||||
)),
|
||||
content: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
Text(
|
||||
'Linking successful',
|
||||
style: Theme.of(context)
|
||||
.textTheme
|
||||
.bodyMedium
|
||||
?.copyWith(fontWeight: FontWeight.w400, fontSize: 18),
|
||||
),
|
||||
const SizedBox(height: 25),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
@ -0,0 +1,110 @@
|
||||
import 'package:flutter/material.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';
|
||||
import 'package:syncrow_web/pages/spaces_management/space_model/models/space_template_model.dart';
|
||||
|
||||
void showOverwriteDialog(
|
||||
BuildContext context, LinkSpaceToModelBloc bloc, SpaceTemplateModel model) {
|
||||
showDialog(
|
||||
context: context,
|
||||
barrierDismissible: false,
|
||||
builder: (BuildContext context) {
|
||||
return SizedBox(
|
||||
child: Dialog(
|
||||
shape:
|
||||
RoundedRectangleBorder(borderRadius: BorderRadius.circular(20)),
|
||||
elevation: 10,
|
||||
backgroundColor: Colors.white,
|
||||
child: SizedBox(
|
||||
width: MediaQuery.of(context).size.width * 0.3,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 30, horizontal: 20),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
const Text(
|
||||
"Overwrite",
|
||||
style: TextStyle(
|
||||
fontSize: 22,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Colors.black,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 15),
|
||||
const Text(
|
||||
"Are you sure you want to overwrite?",
|
||||
style: TextStyle(fontSize: 16, fontWeight: FontWeight.w500),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
const SizedBox(height: 5),
|
||||
Text(
|
||||
"Selected spaces already have linked space model / sub-spaces and devices",
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
color: Colors.grey[600],
|
||||
),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
const SizedBox(height: 25),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||
children: [
|
||||
Expanded(
|
||||
child: ElevatedButton(
|
||||
onPressed: () => Navigator.of(context).pop(),
|
||||
style: ElevatedButton.styleFrom(
|
||||
padding: const EdgeInsets.symmetric(vertical: 14),
|
||||
backgroundColor: Colors.grey[200],
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
),
|
||||
elevation: 0,
|
||||
),
|
||||
child: const Text(
|
||||
"Cancel",
|
||||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
color: Colors.black,
|
||||
fontWeight: FontWeight.w500,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 10),
|
||||
Expanded(
|
||||
child: ElevatedButton(
|
||||
onPressed: () {
|
||||
bloc.add(LinkSpaceModelEvent(
|
||||
isOverWrite: true,
|
||||
selectedSpaceMode: model.uuid));
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
style: ElevatedButton.styleFrom(
|
||||
padding: const EdgeInsets.symmetric(vertical: 14),
|
||||
backgroundColor: Colors.blue,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
),
|
||||
elevation: 3,
|
||||
),
|
||||
child: const Text(
|
||||
"OK",
|
||||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
color: Colors.white,
|
||||
fontWeight: FontWeight.w500,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
@ -1,13 +1,18 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
|
||||
import 'package:flutter_svg/svg.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';
|
||||
import 'package:syncrow_web/pages/spaces_management/link_space_model/bloc/link_space_to_model_state.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/bloc/space_management_bloc.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/bloc/space_management_event.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/space_model/bloc/space_model_bloc.dart';
|
||||
|
||||
import 'package:syncrow_web/pages/spaces_management/space_model/bloc/space_model_event.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/space_model/models/space_template_model.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/space_model/widgets/dialog/custom_loading_dialog.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/space_model/widgets/dialog/link_space_model_spaces_dialog.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/space_model/widgets/dialog/linking_successful.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/space_model/widgets/dialog/overwrite_dialog.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/space_model/widgets/dialog/delete_space_model_dialog.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/space_model/widgets/dynamic_product_widget.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/space_model/widgets/dynamic_room_widget.dart';
|
||||
@ -69,48 +74,174 @@ class SpaceModelCardWidget extends StatelessWidget {
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
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),
|
||||
),
|
||||
],
|
||||
Text(
|
||||
model.modelName,
|
||||
style: Theme.of(context).textTheme.headlineMedium?.copyWith(
|
||||
color: Colors.black,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
child: Center(
|
||||
child: SvgPicture.asset(
|
||||
Assets.deleteSpaceModel, // Your actual SVG path
|
||||
width: 20,
|
||||
height: 20,
|
||||
colorFilter: const ColorFilter.mode(
|
||||
Colors.grey, BlendMode.srcIn),
|
||||
),
|
||||
maxLines: 1,
|
||||
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,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
} 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), () {
|
||||
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,
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
child: SvgPicture.asset(
|
||||
Assets.spaceLinkIcon,
|
||||
fit: BoxFit.contain,
|
||||
),
|
||||
),
|
||||
),
|
||||
if (!topActionsDisabled)
|
||||
InkWell(
|
||||
onTap: () {
|
||||
_showDeleteDialog(context);
|
||||
},
|
||||
child: SvgPicture.asset(
|
||||
Assets.deleteSpaceLinkIcon,
|
||||
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) ...[
|
||||
|
@ -60,6 +60,36 @@ class SpaceModelManagementApi {
|
||||
return response;
|
||||
}
|
||||
|
||||
Future linkSpaceModel(
|
||||
{required String spaceModelUuid,
|
||||
required String projectId,
|
||||
required List<String> spaceUuids,
|
||||
required bool isOverWrite}) async {
|
||||
final response = await HTTPService().post(
|
||||
path: ApiEndpoints.linkSpaceModel
|
||||
.replaceAll('{projectId}', projectId)
|
||||
.replaceAll('{spaceModelUuid}', spaceModelUuid),
|
||||
showServerMessage: true,
|
||||
body: {"spaceUuids": spaceUuids, "overwrite": isOverWrite},
|
||||
expectedResponseModel: (json) {
|
||||
return json;
|
||||
},
|
||||
);
|
||||
return response;
|
||||
}
|
||||
|
||||
Future validateSpaceModel(String projectId, List<String> spaceUuids) async {
|
||||
final response = await HTTPService().post(
|
||||
path: ApiEndpoints.validateSpaceModel
|
||||
.replaceAll('{projectId}', projectId),
|
||||
showServerMessage: true,
|
||||
body: {"spacesUuids": spaceUuids},
|
||||
expectedResponseModel: (json) {
|
||||
return json;
|
||||
});
|
||||
return response;
|
||||
}
|
||||
|
||||
Future<bool> deleteSpaceModel(String spaceModelUuid, String projectId) async {
|
||||
final response = await HTTPService().delete(
|
||||
path: ApiEndpoints.getSpaceModel
|
||||
|
@ -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(0xCCF4F4F4);
|
||||
static const Color counterBackgroundColor = Color.fromARGB(204, 105, 2, 2);
|
||||
static const Color neutralGray = Color(0xFFE5E5E5);
|
||||
static const Color warningRed = Color(0xFFFF6465);
|
||||
static const Color borderColor = Color(0xFFE5E5E5);
|
||||
|
@ -9,15 +9,19 @@ abstract class ApiEndpoints {
|
||||
static const String sendOtp = '/authentication/user/send-otp';
|
||||
static const String verifyOtp = '/authentication/user/verify-otp';
|
||||
static const String getRegion = '/region';
|
||||
static const String visitorPassword = '/projects/{projectId}/visitor-password';
|
||||
static const String getDevices = '/projects/{projectId}/visitor-password/devices';
|
||||
static const String visitorPassword =
|
||||
'/projects/{projectId}/visitor-password';
|
||||
static const String getDevices =
|
||||
'/projects/{projectId}/visitor-password/devices';
|
||||
|
||||
static const String sendOnlineOneTime = '/visitor-password/temporary-password/online/one-time';
|
||||
static const String sendOnlineOneTime =
|
||||
'/visitor-password/temporary-password/online/one-time';
|
||||
static const String sendOnlineMultipleTime =
|
||||
'/visitor-password/temporary-password/online/multiple-time';
|
||||
|
||||
//offline Password
|
||||
static const String sendOffLineOneTime = '/visitor-password/temporary-password/offline/one-time';
|
||||
static const String sendOffLineOneTime =
|
||||
'/visitor-password/temporary-password/offline/one-time';
|
||||
static const String sendOffLineMultipleTime =
|
||||
'/visitor-password/temporary-password/offline/multiple-time';
|
||||
|
||||
@ -39,32 +43,45 @@ abstract class ApiEndpoints {
|
||||
static const String getDeviceLogs = '/device/report-logs/{uuid}?code={code}';
|
||||
|
||||
// Space Module
|
||||
static const String createSpace = '/projects/{projectId}/communities/{communityId}/spaces';
|
||||
static const String listSpaces = '/projects/{projectId}/communities/{communityId}/spaces';
|
||||
static const String createSpace =
|
||||
'/projects/{projectId}/communities/{communityId}/spaces';
|
||||
static const String listSpaces =
|
||||
'/projects/{projectId}/communities/{communityId}/spaces';
|
||||
static const String deleteSpace =
|
||||
'/projects/{projectId}/communities/{communityId}/spaces/{spaceId}';
|
||||
static const String updateSpace =
|
||||
'/projects/{projectId}/communities/{communityId}/spaces/{spaceId}';
|
||||
static const String getSpace = '/projects/{projectId}/communities/{communityId}/spaces/{spaceId}';
|
||||
static const String getSpaceHierarchy = '/projects/{projectId}/communities/{communityId}/spaces';
|
||||
static const String getSpace =
|
||||
'/projects/{projectId}/communities/{communityId}/spaces/{spaceId}';
|
||||
static const String getSpaceHierarchy =
|
||||
'/projects/{projectId}/communities/{communityId}/spaces';
|
||||
|
||||
// Community Module
|
||||
static const String createCommunity = '/projects/{projectId}/communities';
|
||||
static const String getCommunityList = '/projects/{projectId}/communities';
|
||||
static const String getCommunityById = '/projects/{projectId}/communities/{communityId}';
|
||||
static const String updateCommunity = '/projects/{projectId}/communities/{communityId}';
|
||||
static const String deleteCommunity = '/projects/{projectId}communities/{communityId}';
|
||||
static const String getUserCommunities = '/projects/{projectId}/communities/user/{userUuid}';
|
||||
static const String createUserCommunity = '/projects/{projectId}/communities/user';
|
||||
static const String getCommunityById =
|
||||
'/projects/{projectId}/communities/{communityId}';
|
||||
static const String updateCommunity =
|
||||
'/projects/{projectId}/communities/{communityId}';
|
||||
static const String deleteCommunity =
|
||||
'/projects/{projectId}communities/{communityId}';
|
||||
static const String getUserCommunities =
|
||||
'/projects/{projectId}/communities/user/{userUuid}';
|
||||
static const String createUserCommunity =
|
||||
'/projects/{projectId}/communities/user';
|
||||
static const String getDeviceLogsByDate =
|
||||
'/device/report-logs/{uuid}?code={code}&startTime={startTime}&endTime={endTime}';
|
||||
|
||||
static const String scheduleByDeviceId = '/schedule/{deviceUuid}';
|
||||
static const String getScheduleByDeviceId = '/schedule/{deviceUuid}?category={category}';
|
||||
static const String deleteScheduleByDeviceId = '/schedule/{deviceUuid}/{scheduleUuid}';
|
||||
static const String updateScheduleByDeviceId = '/schedule/enable/{deviceUuid}';
|
||||
static const String getScheduleByDeviceId =
|
||||
'/schedule/{deviceUuid}?category={category}';
|
||||
static const String deleteScheduleByDeviceId =
|
||||
'/schedule/{deviceUuid}/{scheduleUuid}';
|
||||
static const String updateScheduleByDeviceId =
|
||||
'/schedule/enable/{deviceUuid}';
|
||||
static const String factoryReset = '/device/factory/reset/{deviceUuid}';
|
||||
static const String powerClamp = '/device/{powerClampUuid}/power-clamp/status';
|
||||
static const String powerClamp =
|
||||
'/device/{powerClampUuid}/power-clamp/status';
|
||||
|
||||
//product
|
||||
static const String listProducts = '/products';
|
||||
@ -75,7 +92,8 @@ abstract class ApiEndpoints {
|
||||
static const String createAutomation = '/automation';
|
||||
static const String getUnitScenes =
|
||||
'/projects/{projectId}/communities/{communityUuid}/spaces/{spaceUuid}/scenes';
|
||||
static const String getAutomationDetails = '/automation/details/{automationId}';
|
||||
static const String getAutomationDetails =
|
||||
'/automation/details/{automationId}';
|
||||
static const String getScene = '/scene/tap-to-run/{sceneId}';
|
||||
static const String deleteScene = '/scene/tap-to-run/{sceneId}';
|
||||
|
||||
@ -91,6 +109,12 @@ abstract class ApiEndpoints {
|
||||
static const String updateSpaceModel = '/projects/{projectId}/space-models/{spaceModelUuid}';
|
||||
//tag
|
||||
static const String listTags = '/projects/{projectId}/tags';
|
||||
|
||||
static const String linkSpaceModel =
|
||||
'/projects/{projectId}/space-models/{spaceModelUuid}/spaces/link';
|
||||
|
||||
static const String validateSpaceModel =
|
||||
'/projects/{projectId}/spaces/validate';
|
||||
|
||||
static const String roleTypes = '/role/types';
|
||||
static const String permission = '/permission/{roleUuid}';
|
||||
@ -101,7 +125,8 @@ abstract class ApiEndpoints {
|
||||
static const String getUserById = '/projects/{projectId}/user/{userUuid}';
|
||||
static const String editUser = '/invite-user/{inviteUserUuid}';
|
||||
static const String deleteUser = '/invite-user/{inviteUserUuid}';
|
||||
static const String changeUserStatus = '/invite-user/{invitedUserUuid}/disable';
|
||||
static const String changeUserStatus =
|
||||
'/invite-user/{invitedUserUuid}/disable';
|
||||
static const String terms = '/terms';
|
||||
static const String policy = '/policy';
|
||||
static const String userAgreements = '/user/agreements/web/{userUuid}';
|
||||
|
@ -402,5 +402,10 @@ class Assets {
|
||||
static const String link = 'assets/icons/link.svg';
|
||||
static const String duplicate = 'assets/icons/duplicate.svg';
|
||||
static const String spaceDelete = 'assets/icons/space_delete.svg';
|
||||
|
||||
static const String deleteSpaceLinkIcon =
|
||||
'assets/icons/delete_space_link_icon.svg';
|
||||
static const String spaceLinkIcon = 'assets/icons/space_link_icon.svg';
|
||||
static const String successIcon = 'assets/icons/success_icon.svg';
|
||||
|
||||
}
|
||||
//user_management.svg
|
||||
|
Reference in New Issue
Block a user