mirror of
https://github.com/SyncrowIOT/web.git
synced 2026-10-27 00:07:57 +00:00
Compare commits
7
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bc4af6a237 | ||
|
|
513175ed1e | ||
|
|
59eafc99a5 | ||
|
|
db7eaa53af | ||
|
|
20a9f19480 | ||
|
|
eb7eeebf18 | ||
|
|
15023e5882 |
@@ -10,6 +10,10 @@ class UserModel {
|
|||||||
final String? phoneNumber;
|
final String? phoneNumber;
|
||||||
final bool? isEmailVerified;
|
final bool? isEmailVerified;
|
||||||
final bool? isAgreementAccepted;
|
final bool? isAgreementAccepted;
|
||||||
|
final bool? hasAcceptedWebAgreement;
|
||||||
|
final DateTime? webAgreementAcceptedAt;
|
||||||
|
final UserRole? role;
|
||||||
|
|
||||||
UserModel({
|
UserModel({
|
||||||
required this.uuid,
|
required this.uuid,
|
||||||
required this.email,
|
required this.email,
|
||||||
@@ -19,6 +23,9 @@ class UserModel {
|
|||||||
required this.phoneNumber,
|
required this.phoneNumber,
|
||||||
required this.isEmailVerified,
|
required this.isEmailVerified,
|
||||||
required this.isAgreementAccepted,
|
required this.isAgreementAccepted,
|
||||||
|
required this.hasAcceptedWebAgreement,
|
||||||
|
required this.webAgreementAcceptedAt,
|
||||||
|
required this.role,
|
||||||
});
|
});
|
||||||
|
|
||||||
factory UserModel.fromJson(Map<String, dynamic> json) {
|
factory UserModel.fromJson(Map<String, dynamic> json) {
|
||||||
@@ -31,6 +38,11 @@ class UserModel {
|
|||||||
phoneNumber: json['phoneNumber'],
|
phoneNumber: json['phoneNumber'],
|
||||||
isEmailVerified: json['isEmailVerified'],
|
isEmailVerified: json['isEmailVerified'],
|
||||||
isAgreementAccepted: json['isAgreementAccepted'],
|
isAgreementAccepted: json['isAgreementAccepted'],
|
||||||
|
hasAcceptedWebAgreement: json['hasAcceptedWebAgreement'],
|
||||||
|
webAgreementAcceptedAt: json['webAgreementAcceptedAt'] != null
|
||||||
|
? DateTime.parse(json['webAgreementAcceptedAt'])
|
||||||
|
: null,
|
||||||
|
role: json['role'] != null ? UserRole.fromJson(json['role']) : null,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -41,6 +53,9 @@ class UserModel {
|
|||||||
Map<String, dynamic> tempJson = Token.decodeToken(token.accessToken);
|
Map<String, dynamic> tempJson = Token.decodeToken(token.accessToken);
|
||||||
|
|
||||||
return UserModel(
|
return UserModel(
|
||||||
|
hasAcceptedWebAgreement: null,
|
||||||
|
role: null,
|
||||||
|
webAgreementAcceptedAt: null,
|
||||||
uuid: tempJson['uuid'].toString(),
|
uuid: tempJson['uuid'].toString(),
|
||||||
email: tempJson['email'],
|
email: tempJson['email'],
|
||||||
firstName: null,
|
firstName: null,
|
||||||
@@ -65,3 +80,26 @@ class UserModel {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class UserRole {
|
||||||
|
final String uuid;
|
||||||
|
final DateTime createdAt;
|
||||||
|
final DateTime updatedAt;
|
||||||
|
final String type;
|
||||||
|
|
||||||
|
UserRole({
|
||||||
|
required this.uuid,
|
||||||
|
required this.createdAt,
|
||||||
|
required this.updatedAt,
|
||||||
|
required this.type,
|
||||||
|
});
|
||||||
|
|
||||||
|
factory UserRole.fromJson(Map<String, dynamic> json) {
|
||||||
|
return UserRole(
|
||||||
|
uuid: json['uuid'],
|
||||||
|
createdAt: DateTime.parse(json['createdAt']),
|
||||||
|
updatedAt: DateTime.parse(json['updatedAt']),
|
||||||
|
type: json['type'],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
|
|||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
import 'package:syncrow_web/pages/device_managment/all_devices/models/factory_reset_model.dart';
|
import 'package:syncrow_web/pages/device_managment/all_devices/models/factory_reset_model.dart';
|
||||||
import 'package:syncrow_web/pages/device_managment/shared/batch_control/factory_reset.dart';
|
import 'package:syncrow_web/pages/device_managment/shared/batch_control/factory_reset.dart';
|
||||||
|
import 'package:syncrow_web/pages/device_managment/shared/batch_control/firmware_update.dart';
|
||||||
import 'package:syncrow_web/pages/device_managment/sos/bloc/sos_device_bloc.dart';
|
import 'package:syncrow_web/pages/device_managment/sos/bloc/sos_device_bloc.dart';
|
||||||
|
|
||||||
class SOSBatchControlView extends StatelessWidget {
|
class SOSBatchControlView extends StatelessWidget {
|
||||||
|
|||||||
@@ -18,10 +18,15 @@ class HomeBloc extends Bloc<HomeEvent, HomeState> {
|
|||||||
List<Node> sourcesList = [];
|
List<Node> sourcesList = [];
|
||||||
List<Node> destinationsList = [];
|
List<Node> destinationsList = [];
|
||||||
UserModel? user;
|
UserModel? user;
|
||||||
|
String terms = '';
|
||||||
|
String policy = '';
|
||||||
|
|
||||||
HomeBloc() : super((HomeInitial())) {
|
HomeBloc() : super((HomeInitial())) {
|
||||||
on<CreateNewNode>(_createNode);
|
on<CreateNewNode>(_createNode);
|
||||||
on<FetchUserInfo>(_fetchUserInfo);
|
on<FetchUserInfo>(_fetchUserInfo);
|
||||||
|
on<FetchTermEvent>(_fetchTerms);
|
||||||
|
on<FetchPolicyEvent>(_fetchPolicy);
|
||||||
|
on<ConfirmUserAgreementEvent>(_confirmUserAgreement);
|
||||||
}
|
}
|
||||||
|
|
||||||
void _createNode(CreateNewNode event, Emitter<HomeState> emit) async {
|
void _createNode(CreateNewNode event, Emitter<HomeState> emit) async {
|
||||||
@@ -45,12 +50,45 @@ class HomeBloc extends Bloc<HomeEvent, HomeState> {
|
|||||||
var uuid =
|
var uuid =
|
||||||
await const FlutterSecureStorage().read(key: UserModel.userUuidKey);
|
await const FlutterSecureStorage().read(key: UserModel.userUuidKey);
|
||||||
user = await HomeApi().fetchUserInfo(uuid);
|
user = await HomeApi().fetchUserInfo(uuid);
|
||||||
|
add(FetchTermEvent());
|
||||||
emit(HomeInitial());
|
emit(HomeInitial());
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future _fetchTerms(FetchTermEvent event, Emitter<HomeState> emit) async {
|
||||||
|
try {
|
||||||
|
emit(LoadingHome());
|
||||||
|
terms = await HomeApi().fetchTerms();
|
||||||
|
add(FetchPolicyEvent());
|
||||||
|
} catch (e) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future _fetchPolicy(FetchPolicyEvent event, Emitter<HomeState> emit) async {
|
||||||
|
try {
|
||||||
|
emit(LoadingHome());
|
||||||
|
policy = await HomeApi().fetchPolicy();
|
||||||
|
} catch (e) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future _confirmUserAgreement(
|
||||||
|
ConfirmUserAgreementEvent event, Emitter<HomeState> emit) async {
|
||||||
|
try {
|
||||||
|
emit(LoadingHome());
|
||||||
|
var uuid =
|
||||||
|
await const FlutterSecureStorage().read(key: UserModel.userUuidKey);
|
||||||
|
policy = await HomeApi().confirmUserAgreements(uuid);
|
||||||
|
emit(PolicyAgreement());
|
||||||
|
} catch (e) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// static Future fetchUserInfo() async {
|
// static Future fetchUserInfo() async {
|
||||||
// try {
|
// try {
|
||||||
// var uuid =
|
// var uuid =
|
||||||
|
|||||||
@@ -20,4 +20,8 @@ class CreateNewNode extends HomeEvent {
|
|||||||
|
|
||||||
class FetchUserInfo extends HomeEvent {
|
class FetchUserInfo extends HomeEvent {
|
||||||
const FetchUserInfo();
|
const FetchUserInfo();
|
||||||
}
|
}class FetchTermEvent extends HomeEvent {}
|
||||||
|
|
||||||
|
class FetchPolicyEvent extends HomeEvent {}
|
||||||
|
|
||||||
|
class ConfirmUserAgreementEvent extends HomeEvent {}
|
||||||
@@ -7,8 +7,12 @@ abstract class HomeState extends Equatable {
|
|||||||
@override
|
@override
|
||||||
List<Object> get props => [];
|
List<Object> get props => [];
|
||||||
}
|
}
|
||||||
|
class LoadingHome extends HomeState {}
|
||||||
|
|
||||||
class HomeInitial extends HomeState {}
|
class HomeInitial extends HomeState {}
|
||||||
|
class TermsAgreement extends HomeState {}
|
||||||
|
|
||||||
|
class PolicyAgreement extends HomeState {}
|
||||||
|
|
||||||
class HomeCounterState extends HomeState {
|
class HomeCounterState extends HomeState {
|
||||||
final int counter;
|
final int counter;
|
||||||
@@ -24,3 +28,5 @@ class HomeUpdateTree extends HomeState {
|
|||||||
@override
|
@override
|
||||||
List<Object> get props => [graph, builder];
|
List<Object> get props => [graph, builder];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//FetchTermEvent
|
||||||
@@ -0,0 +1,176 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_html/flutter_html.dart';
|
||||||
|
import 'package:go_router/go_router.dart';
|
||||||
|
import 'package:syncrow_web/pages/auth/bloc/auth_bloc.dart';
|
||||||
|
import 'package:syncrow_web/utils/color_manager.dart';
|
||||||
|
import 'package:syncrow_web/utils/constants/routes_const.dart';
|
||||||
|
import 'package:url_launcher/url_launcher.dart';
|
||||||
|
|
||||||
|
class AgreementAndPrivacyDialog extends StatefulWidget {
|
||||||
|
final String terms;
|
||||||
|
final String policy;
|
||||||
|
|
||||||
|
const AgreementAndPrivacyDialog({
|
||||||
|
super.key,
|
||||||
|
required this.terms,
|
||||||
|
required this.policy,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
_AgreementAndPrivacyDialogState createState() =>
|
||||||
|
_AgreementAndPrivacyDialogState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _AgreementAndPrivacyDialogState extends State<AgreementAndPrivacyDialog> {
|
||||||
|
final ScrollController _scrollController = ScrollController();
|
||||||
|
bool _isAtEnd = false;
|
||||||
|
int _currentPage = 1;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
_scrollController.addListener(_onScroll);
|
||||||
|
WidgetsBinding.instance
|
||||||
|
.addPostFrameCallback((_) => _checkScrollRequirement());
|
||||||
|
}
|
||||||
|
|
||||||
|
void _checkScrollRequirement() {
|
||||||
|
final scrollPosition = _scrollController.position;
|
||||||
|
if (scrollPosition.maxScrollExtent <= 0) {
|
||||||
|
setState(() {
|
||||||
|
_isAtEnd = true;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
_scrollController.removeListener(_onScroll);
|
||||||
|
_scrollController.dispose();
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
void _onScroll() {
|
||||||
|
if (_scrollController.position.atEdge) {
|
||||||
|
final isAtBottom = _scrollController.position.pixels ==
|
||||||
|
_scrollController.position.maxScrollExtent;
|
||||||
|
if (isAtBottom && !_isAtEnd) {
|
||||||
|
setState(() {
|
||||||
|
_isAtEnd = true;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
String get _dialogTitle =>
|
||||||
|
_currentPage == 2 ? 'User Agreement' : 'Privacy Policy';
|
||||||
|
|
||||||
|
String get _dialogContent => _currentPage == 2 ? widget.terms : widget.policy;
|
||||||
|
|
||||||
|
Widget _buildScrollableContent() {
|
||||||
|
return Container(
|
||||||
|
padding: const EdgeInsets.all(40),
|
||||||
|
width: MediaQuery.of(context).size.width * 0.8,
|
||||||
|
height: MediaQuery.of(context).size.height * 0.75,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: Colors.grey[200],
|
||||||
|
borderRadius: const BorderRadius.all(Radius.circular(20)),
|
||||||
|
),
|
||||||
|
child: Scrollbar(
|
||||||
|
thumbVisibility: true,
|
||||||
|
trackVisibility: true,
|
||||||
|
interactive: true,
|
||||||
|
controller: _scrollController,
|
||||||
|
child: SingleChildScrollView(
|
||||||
|
controller: _scrollController,
|
||||||
|
padding: const EdgeInsets.all(25),
|
||||||
|
child: Html(
|
||||||
|
data: _dialogContent,
|
||||||
|
onLinkTap: (url, attributes, element) async {
|
||||||
|
if (url != null) {
|
||||||
|
final uri = Uri.parse(url);
|
||||||
|
await launchUrl(uri, mode: LaunchMode.externalApplication);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
style: {
|
||||||
|
"body": Style(
|
||||||
|
fontSize: FontSize(14),
|
||||||
|
color: Colors.black87,
|
||||||
|
lineHeight: LineHeight(1.5),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildActionButton() {
|
||||||
|
final String buttonText = _currentPage == 2 ? "I Agree" : "Next";
|
||||||
|
|
||||||
|
return InkWell(
|
||||||
|
onTap: _isAtEnd
|
||||||
|
? () {
|
||||||
|
if (_currentPage == 1) {
|
||||||
|
setState(() {
|
||||||
|
_currentPage = 2;
|
||||||
|
_isAtEnd = false;
|
||||||
|
_scrollController.jumpTo(0);
|
||||||
|
WidgetsBinding.instance
|
||||||
|
.addPostFrameCallback((_) => _checkScrollRequirement());
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
Navigator.of(context).pop(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
: null,
|
||||||
|
child: Text(
|
||||||
|
buttonText,
|
||||||
|
style: TextStyle(
|
||||||
|
color: _isAtEnd ? ColorsManager.secondaryColor : Colors.grey,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Dialog(
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.all(16.0),
|
||||||
|
child: Text(
|
||||||
|
_dialogTitle,
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
style: const TextStyle(
|
||||||
|
fontSize: 20,
|
||||||
|
fontWeight: FontWeight.w700,
|
||||||
|
color: ColorsManager.secondaryColor,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const Divider(),
|
||||||
|
_buildScrollableContent(),
|
||||||
|
const Divider(),
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.all(8.0),
|
||||||
|
child: Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceAround,
|
||||||
|
children: [
|
||||||
|
InkWell(
|
||||||
|
onTap: () {
|
||||||
|
AuthBloc.logout();
|
||||||
|
context.go(RoutesConst.auth);
|
||||||
|
},
|
||||||
|
child: const Text("Cancel"),
|
||||||
|
),
|
||||||
|
_buildActionButton(),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,6 +1,8 @@
|
|||||||
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:flutter_svg/svg.dart';
|
import 'package:flutter_svg/svg.dart';
|
||||||
|
import 'package:syncrow_web/pages/home/bloc/home_event.dart';
|
||||||
|
import 'package:syncrow_web/pages/home/view/agreement_and_privacy_dialog.dart';
|
||||||
import 'package:syncrow_web/pages/home/bloc/home_bloc.dart';
|
import 'package:syncrow_web/pages/home/bloc/home_bloc.dart';
|
||||||
import 'package:syncrow_web/pages/home/bloc/home_state.dart';
|
import 'package:syncrow_web/pages/home/bloc/home_state.dart';
|
||||||
import 'package:syncrow_web/pages/home/view/home_card.dart';
|
import 'package:syncrow_web/pages/home/view/home_card.dart';
|
||||||
@@ -9,16 +11,40 @@ import 'package:syncrow_web/web_layout/web_scaffold.dart';
|
|||||||
|
|
||||||
class HomeWebPage extends StatelessWidget {
|
class HomeWebPage extends StatelessWidget {
|
||||||
const HomeWebPage({super.key});
|
const HomeWebPage({super.key});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
Size size = MediaQuery.of(context).size;
|
Size size = MediaQuery.of(context).size;
|
||||||
|
final homeBloc = BlocProvider.of<HomeBloc>(context);
|
||||||
|
|
||||||
return PopScope(
|
return PopScope(
|
||||||
canPop: false,
|
canPop: false,
|
||||||
onPopInvoked: (didPop) => false,
|
onPopInvoked: (didPop) => false,
|
||||||
child: BlocConsumer<HomeBloc, HomeState>(
|
child: BlocConsumer<HomeBloc, HomeState>(
|
||||||
listener: (BuildContext context, state) {},
|
listener: (BuildContext context, state) {
|
||||||
|
if (state is HomeInitial) {
|
||||||
|
if (homeBloc.user!.hasAcceptedWebAgreement == false) {
|
||||||
|
Future.delayed(const Duration(seconds: 1), () {
|
||||||
|
showDialog(
|
||||||
|
context: context,
|
||||||
|
barrierDismissible: false,
|
||||||
|
builder: (BuildContext context) {
|
||||||
|
return AgreementAndPrivacyDialog(
|
||||||
|
terms: homeBloc.terms,
|
||||||
|
policy: homeBloc.policy,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
).then((v) {
|
||||||
|
if (v != null) {
|
||||||
|
homeBloc.add(ConfirmUserAgreementEvent());
|
||||||
|
homeBloc.add(const FetchUserInfo());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
builder: (context, state) {
|
builder: (context, state) {
|
||||||
final homeBloc = BlocProvider.of<HomeBloc>(context);
|
|
||||||
return WebScaffold(
|
return WebScaffold(
|
||||||
enableMenuSidebar: false,
|
enableMenuSidebar: false,
|
||||||
appBarTitle: Row(
|
appBarTitle: Row(
|
||||||
@@ -52,7 +78,8 @@ class HomeWebPage extends StatelessWidget {
|
|||||||
width: size.width * 0.68,
|
width: size.width * 0.68,
|
||||||
child: GridView.builder(
|
child: GridView.builder(
|
||||||
itemCount: 3, //8
|
itemCount: 3, //8
|
||||||
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
|
gridDelegate:
|
||||||
|
const SliverGridDelegateWithFixedCrossAxisCount(
|
||||||
crossAxisCount: 4,
|
crossAxisCount: 4,
|
||||||
crossAxisSpacing: 20.0,
|
crossAxisSpacing: 20.0,
|
||||||
mainAxisSpacing: 20.0,
|
mainAxisSpacing: 20.0,
|
||||||
@@ -64,7 +91,8 @@ class HomeWebPage extends StatelessWidget {
|
|||||||
active: homeBloc.homeItems[index].active!,
|
active: homeBloc.homeItems[index].active!,
|
||||||
name: homeBloc.homeItems[index].title!,
|
name: homeBloc.homeItems[index].title!,
|
||||||
img: homeBloc.homeItems[index].icon!,
|
img: homeBloc.homeItems[index].icon!,
|
||||||
onTap: () => homeBloc.homeItems[index].onPress(context),
|
onTap: () =>
|
||||||
|
homeBloc.homeItems[index].onPress(context),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ class RolesUserModel {
|
|||||||
invitedBy:
|
invitedBy:
|
||||||
json['invitedBy'].toString().toLowerCase().replaceAll("_", " "),
|
json['invitedBy'].toString().toLowerCase().replaceAll("_", " "),
|
||||||
phoneNumber: json['phoneNumber'],
|
phoneNumber: json['phoneNumber'],
|
||||||
jobTitle: json['jobTitle'].toString(),
|
jobTitle: json['jobTitle'] ?? "-",
|
||||||
createdDate: json['createdDate'],
|
createdDate: json['createdDate'],
|
||||||
createdTime: json['createdTime'],
|
createdTime: json['createdTime'],
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -114,7 +114,7 @@ class _AddNewUserDialogState extends State<AddNewUserDialog> {
|
|||||||
currentStep++;
|
currentStep++;
|
||||||
if (currentStep == 2) {
|
if (currentStep == 2) {
|
||||||
_blocRole.add(
|
_blocRole.add(
|
||||||
CheckStepStatus(isEditUser: false));
|
const CheckStepStatus(isEditUser: false));
|
||||||
} else if (currentStep == 3) {
|
} else if (currentStep == 3) {
|
||||||
_blocRole
|
_blocRole
|
||||||
.add(const CheckSpacesStepStatus());
|
.add(const CheckSpacesStepStatus());
|
||||||
@@ -151,11 +151,11 @@ class _AddNewUserDialogState extends State<AddNewUserDialog> {
|
|||||||
Widget _getFormContent() {
|
Widget _getFormContent() {
|
||||||
switch (currentStep) {
|
switch (currentStep) {
|
||||||
case 1:
|
case 1:
|
||||||
return BasicsView(
|
return const BasicsView(
|
||||||
userId: '',
|
userId: '',
|
||||||
);
|
);
|
||||||
case 2:
|
case 2:
|
||||||
return SpacesAccessView();
|
return const SpacesAccessView();
|
||||||
case 3:
|
case 3:
|
||||||
return const RolesAndPermission();
|
return const RolesAndPermission();
|
||||||
default:
|
default:
|
||||||
@@ -172,7 +172,7 @@ class _AddNewUserDialogState extends State<AddNewUserDialog> {
|
|||||||
bloc.add(const CheckSpacesStepStatus());
|
bloc.add(const CheckSpacesStepStatus());
|
||||||
currentStep = step;
|
currentStep = step;
|
||||||
Future.delayed(const Duration(milliseconds: 500), () {
|
Future.delayed(const Duration(milliseconds: 500), () {
|
||||||
bloc.add(ValidateBasicsStep());
|
bloc.add(const ValidateBasicsStep());
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -237,10 +237,11 @@ class _AddNewUserDialogState extends State<AddNewUserDialog> {
|
|||||||
onTap: () {
|
onTap: () {
|
||||||
setState(() {
|
setState(() {
|
||||||
currentStep = step;
|
currentStep = step;
|
||||||
bloc.add(CheckStepStatus(isEditUser: false));
|
bloc.add(const CheckStepStatus(isEditUser: false));
|
||||||
if (step3 == 3) {
|
if (step3 == 3) {
|
||||||
bloc.add(const CheckRoleStepStatus());
|
bloc.add(const CheckRoleStepStatus());
|
||||||
}
|
}
|
||||||
|
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
child: Column(
|
child: Column(
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ import 'package:intl_phone_field/countries.dart';
|
|||||||
import 'package:intl_phone_field/country_picker_dialog.dart';
|
import 'package:intl_phone_field/country_picker_dialog.dart';
|
||||||
import 'package:intl_phone_field/intl_phone_field.dart';
|
import 'package:intl_phone_field/intl_phone_field.dart';
|
||||||
import 'package:syncrow_web/pages/roles_and_permission/users_page/add_user_dialog/bloc/users_bloc.dart';
|
import 'package:syncrow_web/pages/roles_and_permission/users_page/add_user_dialog/bloc/users_bloc.dart';
|
||||||
import 'package:syncrow_web/pages/roles_and_permission/users_page/add_user_dialog/bloc/users_event.dart';
|
|
||||||
import 'package:syncrow_web/pages/roles_and_permission/users_page/add_user_dialog/bloc/users_status.dart';
|
import 'package:syncrow_web/pages/roles_and_permission/users_page/add_user_dialog/bloc/users_status.dart';
|
||||||
import 'package:syncrow_web/utils/color_manager.dart';
|
import 'package:syncrow_web/utils/color_manager.dart';
|
||||||
import 'package:syncrow_web/utils/extension/build_context_x.dart';
|
import 'package:syncrow_web/utils/extension/build_context_x.dart';
|
||||||
@@ -47,7 +46,9 @@ class BasicsView extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
Row(
|
Row(
|
||||||
children: [
|
children: [
|
||||||
Expanded(
|
SizedBox(
|
||||||
|
width: MediaQuery.of(context).size.width * 0.18,
|
||||||
|
height: MediaQuery.of(context).size.width * 0.08,
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
@@ -76,12 +77,12 @@ class BasicsView extends StatelessWidget {
|
|||||||
child: TextFormField(
|
child: TextFormField(
|
||||||
style:
|
style:
|
||||||
const TextStyle(color: ColorsManager.blackColor),
|
const TextStyle(color: ColorsManager.blackColor),
|
||||||
onChanged: (value) {
|
// onChanged: (value) {
|
||||||
Future.delayed(const Duration(milliseconds: 200),
|
// Future.delayed(const Duration(milliseconds: 200),
|
||||||
() {
|
// () {
|
||||||
_blocRole.add(ValidateBasicsStep());
|
// _blocRole.add(const ValidateBasicsStep());
|
||||||
});
|
// });
|
||||||
},
|
// },
|
||||||
controller: _blocRole.firstNameController,
|
controller: _blocRole.firstNameController,
|
||||||
decoration: inputTextFormDeco(
|
decoration: inputTextFormDeco(
|
||||||
hintText: "Enter first name",
|
hintText: "Enter first name",
|
||||||
@@ -103,7 +104,9 @@ class BasicsView extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(width: 10),
|
const SizedBox(width: 10),
|
||||||
Expanded(
|
SizedBox(
|
||||||
|
width: MediaQuery.of(context).size.width * 0.18,
|
||||||
|
height: MediaQuery.of(context).size.width * 0.08,
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
@@ -128,12 +131,12 @@ class BasicsView extends StatelessWidget {
|
|||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.all(8.0),
|
padding: const EdgeInsets.all(8.0),
|
||||||
child: TextFormField(
|
child: TextFormField(
|
||||||
onChanged: (value) {
|
// onChanged: (value) {
|
||||||
Future.delayed(const Duration(milliseconds: 200),
|
// Future.delayed(const Duration(milliseconds: 200),
|
||||||
() {
|
// () {
|
||||||
_blocRole.add(ValidateBasicsStep());
|
// _blocRole.add(ValidateBasicsStep());
|
||||||
});
|
// });
|
||||||
},
|
// },
|
||||||
controller: _blocRole.lastNameController,
|
controller: _blocRole.lastNameController,
|
||||||
style: const TextStyle(color: Colors.black),
|
style: const TextStyle(color: Colors.black),
|
||||||
decoration:
|
decoration:
|
||||||
@@ -186,13 +189,13 @@ class BasicsView extends StatelessWidget {
|
|||||||
padding: const EdgeInsets.all(8.0),
|
padding: const EdgeInsets.all(8.0),
|
||||||
child: TextFormField(
|
child: TextFormField(
|
||||||
enabled: userId != '' ? false : true,
|
enabled: userId != '' ? false : true,
|
||||||
onChanged: (value) {
|
// onChanged: (value) {
|
||||||
Future.delayed(const Duration(milliseconds: 200), () {
|
// Future.delayed(const Duration(milliseconds: 200), () {
|
||||||
_blocRole.add(CheckStepStatus(
|
// _blocRole.add(CheckStepStatus(
|
||||||
isEditUser: userId != '' ? false : true));
|
// isEditUser: userId != '' ? false : true));
|
||||||
_blocRole.add(ValidateBasicsStep());
|
// _blocRole.add(ValidateBasicsStep());
|
||||||
});
|
// });
|
||||||
},
|
// },
|
||||||
controller: _blocRole.emailController,
|
controller: _blocRole.emailController,
|
||||||
style: const TextStyle(color: ColorsManager.blackColor),
|
style: const TextStyle(color: ColorsManager.blackColor),
|
||||||
decoration: inputTextFormDeco(hintText: "[email protected]")
|
decoration: inputTextFormDeco(hintText: "[email protected]")
|
||||||
|
|||||||
+45
-10
@@ -11,7 +11,14 @@ class DeleteUserDialog extends StatefulWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class _DeleteUserDialogState extends State<DeleteUserDialog> {
|
class _DeleteUserDialogState extends State<DeleteUserDialog> {
|
||||||
int currentStep = 1;
|
bool isLoading = false;
|
||||||
|
bool _isDisposed = false;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
_isDisposed = true;
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
@@ -56,7 +63,7 @@ class _DeleteUserDialogState extends State<DeleteUserDialog> {
|
|||||||
Expanded(
|
Expanded(
|
||||||
child: InkWell(
|
child: InkWell(
|
||||||
onTap: () {
|
onTap: () {
|
||||||
Navigator.of(context).pop(true);
|
Navigator.of(context).pop(false); // Return false if canceled
|
||||||
},
|
},
|
||||||
child: Container(
|
child: Container(
|
||||||
padding: const EdgeInsets.all(10),
|
padding: const EdgeInsets.all(10),
|
||||||
@@ -76,7 +83,26 @@ class _DeleteUserDialogState extends State<DeleteUserDialog> {
|
|||||||
)),
|
)),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: InkWell(
|
child: InkWell(
|
||||||
onTap: widget.onTapDelete,
|
onTap: isLoading
|
||||||
|
? null
|
||||||
|
: () async {
|
||||||
|
setState(() {
|
||||||
|
isLoading = true;
|
||||||
|
});
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (widget.onTapDelete != null) {
|
||||||
|
await widget.onTapDelete!();
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
if (!_isDisposed) {
|
||||||
|
setState(() {
|
||||||
|
isLoading = false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Navigator.of(context).pop(true);
|
||||||
|
},
|
||||||
child: Container(
|
child: Container(
|
||||||
padding: const EdgeInsets.all(10),
|
padding: const EdgeInsets.all(10),
|
||||||
decoration: const BoxDecoration(
|
decoration: const BoxDecoration(
|
||||||
@@ -91,13 +117,22 @@ class _DeleteUserDialogState extends State<DeleteUserDialog> {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
child: const Center(
|
child: Center(
|
||||||
child: Text(
|
child: isLoading
|
||||||
'Delete',
|
? const SizedBox(
|
||||||
style: TextStyle(
|
height: 20,
|
||||||
color: ColorsManager.red,
|
width: 20,
|
||||||
),
|
child: CircularProgressIndicator(
|
||||||
))),
|
color: ColorsManager.red,
|
||||||
|
strokeWidth: 2.0,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
: const Text(
|
||||||
|
'Delete',
|
||||||
|
style: TextStyle(
|
||||||
|
color: ColorsManager.red,
|
||||||
|
),
|
||||||
|
))),
|
||||||
)),
|
)),
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|||||||
+3
-3
@@ -128,7 +128,7 @@ class _PermissionManagementState extends State<PermissionManagement> {
|
|||||||
),
|
),
|
||||||
const SizedBox(width: 8),
|
const SizedBox(width: 8),
|
||||||
Text(
|
Text(
|
||||||
option.title,
|
' ${option.title.isNotEmpty ? option.title[0].toUpperCase() : ''}${option.title.substring(1)}',
|
||||||
style: context.textTheme.bodyMedium?.copyWith(
|
style: context.textTheme.bodyMedium?.copyWith(
|
||||||
fontWeight: FontWeight.w700,
|
fontWeight: FontWeight.w700,
|
||||||
fontSize: 12,
|
fontSize: 12,
|
||||||
@@ -184,7 +184,7 @@ class _PermissionManagementState extends State<PermissionManagement> {
|
|||||||
),
|
),
|
||||||
const SizedBox(width: 8),
|
const SizedBox(width: 8),
|
||||||
Text(
|
Text(
|
||||||
subOption.title,
|
' ${subOption.title.isNotEmpty ? subOption.title[0].toUpperCase() : ''}${subOption.title.substring(1)}',
|
||||||
style: context.textTheme.bodyMedium?.copyWith(
|
style: context.textTheme.bodyMedium?.copyWith(
|
||||||
fontWeight: FontWeight.w700,
|
fontWeight: FontWeight.w700,
|
||||||
fontSize: 12,
|
fontSize: 12,
|
||||||
@@ -246,7 +246,7 @@ class _PermissionManagementState extends State<PermissionManagement> {
|
|||||||
),
|
),
|
||||||
const SizedBox(width: 8),
|
const SizedBox(width: 8),
|
||||||
Text(
|
Text(
|
||||||
child.title,
|
' ${child.title.isNotEmpty ? child.title[0].toUpperCase() : ''}${child.title.substring(1)}',
|
||||||
style: context.textTheme.bodyMedium?.copyWith(
|
style: context.textTheme.bodyMedium?.copyWith(
|
||||||
fontWeight: FontWeight.w400,
|
fontWeight: FontWeight.w400,
|
||||||
fontSize: 12,
|
fontSize: 12,
|
||||||
|
|||||||
+135
-120
@@ -5,141 +5,156 @@ import 'package:syncrow_web/utils/style.dart';
|
|||||||
|
|
||||||
Future<void> showPopUpFilterMenu({
|
Future<void> showPopUpFilterMenu({
|
||||||
required BuildContext context,
|
required BuildContext context,
|
||||||
Function()? onSortAtoZ,
|
required Function(String value) onSortAtoZ,
|
||||||
Function()? onSortZtoA,
|
required Function(String value) onSortZtoA,
|
||||||
Function()? cancelButton,
|
Function()? cancelButton,
|
||||||
required Map<String, bool> checkboxStates,
|
required Map<String, bool> checkboxStates,
|
||||||
required RelativeRect position,
|
required RelativeRect position,
|
||||||
Function()? onOkPressed,
|
Function()? onOkPressed,
|
||||||
List<String>? list,
|
List<String>? list,
|
||||||
|
String? isSelected,
|
||||||
}) async {
|
}) async {
|
||||||
|
|
||||||
|
|
||||||
await showMenu(
|
await showMenu(
|
||||||
context: context,
|
context: context,
|
||||||
position:position,
|
position: position,
|
||||||
|
|
||||||
|
|
||||||
color: ColorsManager.whiteColors,
|
color: ColorsManager.whiteColors,
|
||||||
shape: const RoundedRectangleBorder(
|
shape: const RoundedRectangleBorder(
|
||||||
borderRadius: BorderRadius.all(Radius.circular(10)),
|
borderRadius: BorderRadius.all(Radius.circular(10)),
|
||||||
),
|
),
|
||||||
items: <PopupMenuEntry>[
|
items: <PopupMenuEntry>[
|
||||||
PopupMenuItem(
|
PopupMenuItem(
|
||||||
onTap: onSortAtoZ,
|
enabled: false,
|
||||||
child: ListTile(
|
child: StatefulBuilder(
|
||||||
leading: Image.asset(
|
builder: (context, setState) {
|
||||||
Assets.AtoZIcon,
|
return Column(
|
||||||
width: 25,
|
mainAxisSize: MainAxisSize.min,
|
||||||
),
|
children: [
|
||||||
title: const Text(
|
SizedBox(
|
||||||
"Sort A to Z",
|
child: ListTile(
|
||||||
style: TextStyle(color: Colors.blueGrey),
|
onTap: () {
|
||||||
),
|
setState(() {
|
||||||
),
|
if (isSelected == 'Asc') {
|
||||||
),
|
isSelected = null;
|
||||||
PopupMenuItem(
|
onSortAtoZ.call('');
|
||||||
onTap: onSortZtoA,
|
} else {
|
||||||
child: ListTile(
|
onSortAtoZ.call('Asc');
|
||||||
leading: Image.asset(
|
isSelected = 'Asc';
|
||||||
Assets.ZtoAIcon,
|
}
|
||||||
width: 25,
|
});
|
||||||
),
|
},
|
||||||
title: const Text(
|
leading: Image.asset(
|
||||||
"Sort Z to A",
|
Assets.AtoZIcon,
|
||||||
style: TextStyle(color: Colors.blueGrey),
|
width: 25,
|
||||||
),
|
),
|
||||||
),
|
title: Text(
|
||||||
),
|
"Sort A to Z",
|
||||||
const PopupMenuDivider(),
|
style: TextStyle(
|
||||||
const PopupMenuItem(
|
color: isSelected == "Asc"
|
||||||
child: Text(
|
? ColorsManager.blackColor
|
||||||
"Filter by Status",
|
: ColorsManager.grayColor),
|
||||||
style: TextStyle(fontWeight: FontWeight.bold),
|
),
|
||||||
)
|
),
|
||||||
// Container(
|
|
||||||
// decoration: containerDecoration.copyWith(
|
|
||||||
// boxShadow: [],
|
|
||||||
// borderRadius: const BorderRadius.only(
|
|
||||||
// topLeft: Radius.circular(10), topRight: Radius.circular(10))),
|
|
||||||
// child: Padding(
|
|
||||||
// padding: const EdgeInsets.all(8.0),
|
|
||||||
// child: TextFormField(
|
|
||||||
// onChanged: onTextFieldChanged,
|
|
||||||
// style: const TextStyle(color: Colors.black),
|
|
||||||
// decoration: textBoxDecoration(radios: 15)!.copyWith(
|
|
||||||
// fillColor: ColorsManager.whiteColors,
|
|
||||||
// errorStyle: const TextStyle(height: 0),
|
|
||||||
// hintStyle: context.textTheme.titleSmall?.copyWith(
|
|
||||||
// color: Colors.grey,
|
|
||||||
// fontSize: 12,
|
|
||||||
// ),
|
|
||||||
// hintText: 'Search',
|
|
||||||
// suffixIcon: SizedBox(
|
|
||||||
// child: SvgPicture.asset(
|
|
||||||
// Assets.searchIconUser,
|
|
||||||
// fit: BoxFit.none,
|
|
||||||
// ),
|
|
||||||
// ),
|
|
||||||
// ),
|
|
||||||
// // "Filter by Status",
|
|
||||||
// // style: TextStyle(fontWeight: FontWeight.bold),
|
|
||||||
// ),
|
|
||||||
// ),
|
|
||||||
// ),
|
|
||||||
),
|
|
||||||
PopupMenuItem(
|
|
||||||
child: Container(
|
|
||||||
decoration: containerDecoration.copyWith(
|
|
||||||
boxShadow: [],
|
|
||||||
borderRadius: const BorderRadius.only(
|
|
||||||
bottomLeft: Radius.circular(10),
|
|
||||||
bottomRight: Radius.circular(10))),
|
|
||||||
padding: const EdgeInsets.all(10),
|
|
||||||
height: 200,
|
|
||||||
width: 400,
|
|
||||||
child: Container(
|
|
||||||
padding: const EdgeInsets.all(10),
|
|
||||||
color: Colors.white,
|
|
||||||
child: ListView.builder(
|
|
||||||
itemCount: list?.length ?? 0,
|
|
||||||
itemBuilder: (context, index) {
|
|
||||||
final item = list![index];
|
|
||||||
return CheckboxListTile(
|
|
||||||
dense: true,
|
|
||||||
title: Text(item),
|
|
||||||
value: checkboxStates[item],
|
|
||||||
onChanged: (bool? newValue) {
|
|
||||||
checkboxStates[item] = newValue ?? false;
|
|
||||||
(context as Element).markNeedsBuild();
|
|
||||||
},
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
PopupMenuItem(
|
|
||||||
child: Row(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.center,
|
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
||||||
children: [
|
|
||||||
GestureDetector(
|
|
||||||
onTap: () {
|
|
||||||
Navigator.of(context).pop(); // Close the menu
|
|
||||||
},
|
|
||||||
child: const Text("Cancel"),
|
|
||||||
),
|
|
||||||
GestureDetector(
|
|
||||||
onTap: onOkPressed,
|
|
||||||
child: const Text(
|
|
||||||
"OK",
|
|
||||||
style: TextStyle(
|
|
||||||
color: ColorsManager.spaceColor,
|
|
||||||
),
|
),
|
||||||
),
|
ListTile(
|
||||||
),
|
onTap: () {
|
||||||
],
|
setState(() {
|
||||||
|
if (isSelected == 'Desc') {
|
||||||
|
isSelected = null;
|
||||||
|
onSortZtoA.call('');
|
||||||
|
} else {
|
||||||
|
onSortZtoA.call('Desc');
|
||||||
|
isSelected = 'Desc';
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
leading: Image.asset(
|
||||||
|
Assets.ZtoAIcon,
|
||||||
|
width: 25,
|
||||||
|
),
|
||||||
|
title: Text(
|
||||||
|
"Sort Z to A",
|
||||||
|
style: TextStyle(
|
||||||
|
color: isSelected == "Desc"
|
||||||
|
? ColorsManager.blackColor
|
||||||
|
: ColorsManager.grayColor),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const Divider(),
|
||||||
|
const Text(
|
||||||
|
"Filter by Status",
|
||||||
|
style: TextStyle(fontWeight: FontWeight.bold),
|
||||||
|
),
|
||||||
|
Container(
|
||||||
|
decoration: containerDecoration.copyWith(
|
||||||
|
boxShadow: [],
|
||||||
|
borderRadius: const BorderRadius.only(
|
||||||
|
topLeft: Radius.circular(10),
|
||||||
|
topRight: Radius.circular(10),
|
||||||
|
bottomLeft: Radius.circular(10),
|
||||||
|
bottomRight: Radius.circular(10))),
|
||||||
|
padding: const EdgeInsets.all(10),
|
||||||
|
height: 200,
|
||||||
|
width: 400,
|
||||||
|
child: Container(
|
||||||
|
padding: const EdgeInsets.all(10),
|
||||||
|
color: Colors.white,
|
||||||
|
child: ListView.builder(
|
||||||
|
itemCount: list?.length ?? 0,
|
||||||
|
itemBuilder: (context, index) {
|
||||||
|
final item = list![index];
|
||||||
|
return Row(
|
||||||
|
children: [
|
||||||
|
Checkbox(
|
||||||
|
value: checkboxStates[item],
|
||||||
|
onChanged: (bool? newValue) {
|
||||||
|
checkboxStates[item] = newValue ?? false;
|
||||||
|
(context as Element).markNeedsBuild();
|
||||||
|
},
|
||||||
|
),
|
||||||
|
Text(
|
||||||
|
item,
|
||||||
|
style: TextStyle(color: ColorsManager.grayColor),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(
|
||||||
|
height: 10,
|
||||||
|
),
|
||||||
|
const Divider(),
|
||||||
|
const SizedBox(
|
||||||
|
height: 10,
|
||||||
|
),
|
||||||
|
Row(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
GestureDetector(
|
||||||
|
onTap: () {
|
||||||
|
Navigator.of(context).pop(); // Close the menu
|
||||||
|
},
|
||||||
|
child: const Text("Cancel"),
|
||||||
|
),
|
||||||
|
GestureDetector(
|
||||||
|
onTap: onOkPressed,
|
||||||
|
child: const Text(
|
||||||
|
"OK",
|
||||||
|
style: TextStyle(
|
||||||
|
color: ColorsManager.spaceColor,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
const SizedBox(
|
||||||
|
height: 10,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
},
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -52,14 +52,16 @@ class _RoleDropdownState extends State<RoleDropdown> {
|
|||||||
SizedBox(
|
SizedBox(
|
||||||
child: DropdownButtonFormField<String>(
|
child: DropdownButtonFormField<String>(
|
||||||
dropdownColor: ColorsManager.whiteColors,
|
dropdownColor: ColorsManager.whiteColors,
|
||||||
alignment: Alignment.center,
|
// alignment: Alignment.,
|
||||||
focusColor: Colors.white,
|
focusColor: Colors.white,
|
||||||
autofocus: true,
|
autofocus: true,
|
||||||
value: selectedRole.isNotEmpty ? selectedRole : null,
|
value: selectedRole.isNotEmpty ? selectedRole : null,
|
||||||
items: widget.bloc!.roles.map((role) {
|
items: widget.bloc!.roles.map((role) {
|
||||||
return DropdownMenuItem<String>(
|
return DropdownMenuItem<String>(
|
||||||
value: role.uuid,
|
value: role.uuid,
|
||||||
child: Text(role.type),
|
child: Text(
|
||||||
|
' ${role.type.isNotEmpty ? role.type[0].toUpperCase() : ''}${role.type.substring(1)}',
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}).toList(),
|
}).toList(),
|
||||||
onChanged: (value) {
|
onChanged: (value) {
|
||||||
|
|||||||
@@ -93,7 +93,7 @@ class UserTableBloc extends Bloc<UserTableEvent, UserTableState> {
|
|||||||
try {
|
try {
|
||||||
emit(UsersLoadingState());
|
emit(UsersLoadingState());
|
||||||
bool res = await UserPermissionApi().changeUserStatusById(
|
bool res = await UserPermissionApi().changeUserStatusById(
|
||||||
event.userId, event.newStatus == "disabled" ? true : false);
|
event.userId, event.newStatus == "disabled" ? false : true);
|
||||||
if (res == true) {
|
if (res == true) {
|
||||||
add(const GetUsers());
|
add(const GetUsers());
|
||||||
// users = users.map((user) {
|
// users = users.map((user) {
|
||||||
@@ -133,7 +133,10 @@ class UserTableBloc extends Bloc<UserTableEvent, UserTableState> {
|
|||||||
} else {
|
} else {
|
||||||
emit(UsersLoadingState());
|
emit(UsersLoadingState());
|
||||||
currentSortOrder = "Asc";
|
currentSortOrder = "Asc";
|
||||||
users.sort((a, b) => a.firstName!.compareTo(b.firstName!));
|
users.sort((a, b) => a.firstName
|
||||||
|
.toString()
|
||||||
|
.toLowerCase()
|
||||||
|
.compareTo(b.firstName.toString().toLowerCase()));
|
||||||
emit(UsersLoadedState(users: users));
|
emit(UsersLoadedState(users: users));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -164,6 +167,7 @@ class UserTableBloc extends Bloc<UserTableEvent, UserTableState> {
|
|||||||
emit(UsersLoadedState(users: users));
|
emit(UsersLoadedState(users: users));
|
||||||
} else {
|
} else {
|
||||||
emit(UsersLoadingState());
|
emit(UsersLoadingState());
|
||||||
|
currentSortOrder = "NewestToOldest";
|
||||||
users.sort((a, b) {
|
users.sort((a, b) {
|
||||||
final dateA = _parseDateTime(a.createdDate);
|
final dateA = _parseDateTime(a.createdDate);
|
||||||
final dateB = _parseDateTime(b.createdDate);
|
final dateB = _parseDateTime(b.createdDate);
|
||||||
@@ -188,6 +192,7 @@ class UserTableBloc extends Bloc<UserTableEvent, UserTableState> {
|
|||||||
final dateB = _parseDateTime(b.createdDate);
|
final dateB = _parseDateTime(b.createdDate);
|
||||||
return dateA.compareTo(dateB);
|
return dateA.compareTo(dateB);
|
||||||
});
|
});
|
||||||
|
currentSortOrder = "OldestToNewest";
|
||||||
emit(UsersLoadedState(users: users));
|
emit(UsersLoadedState(users: users));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -210,7 +215,7 @@ class UserTableBloc extends Bloc<UserTableEvent, UserTableState> {
|
|||||||
final query = event.query.toLowerCase();
|
final query = event.query.toLowerCase();
|
||||||
final filteredUsers = initialUsers.where((user) {
|
final filteredUsers = initialUsers.where((user) {
|
||||||
final fullName = "${user.firstName} ${user.lastName}".toLowerCase();
|
final fullName = "${user.firstName} ${user.lastName}".toLowerCase();
|
||||||
final email = user.email.toLowerCase() ;
|
final email = user.email.toLowerCase();
|
||||||
return fullName.contains(query) || email.contains(query);
|
return fullName.contains(query) || email.contains(query);
|
||||||
}).toList();
|
}).toList();
|
||||||
emit(UsersLoadedState(users: filteredUsers));
|
emit(UsersLoadedState(users: filteredUsers));
|
||||||
@@ -256,49 +261,96 @@ class UserTableBloc extends Bloc<UserTableEvent, UserTableState> {
|
|||||||
|
|
||||||
void _filterUsersByRole(
|
void _filterUsersByRole(
|
||||||
FilterUsersByRoleEvent event, Emitter<UserTableState> emit) {
|
FilterUsersByRoleEvent event, Emitter<UserTableState> emit) {
|
||||||
selectedRoles = event.selectedRoles.toSet();
|
selectedRoles = event.selectedRoles!.toSet();
|
||||||
|
|
||||||
final filteredUsers = initialUsers.where((user) {
|
final filteredUsers = initialUsers.where((user) {
|
||||||
if (selectedRoles.isEmpty) return true;
|
if (selectedRoles.isEmpty) return true;
|
||||||
return selectedRoles.contains(user.roleType);
|
return selectedRoles.contains(user.roleType);
|
||||||
}).toList();
|
}).toList();
|
||||||
|
|
||||||
|
if (event.sortOrder == "Asc") {
|
||||||
|
currentSortOrder = "Asc";
|
||||||
|
filteredUsers.sort((a, b) => a.firstName
|
||||||
|
.toString()
|
||||||
|
.toLowerCase()
|
||||||
|
.compareTo(b.firstName.toString().toLowerCase()));
|
||||||
|
} else if (event.sortOrder == "Desc") {
|
||||||
|
currentSortOrder = "Desc";
|
||||||
|
filteredUsers.sort((a, b) => b.firstName!.compareTo(a.firstName!));
|
||||||
|
} else {
|
||||||
|
currentSortOrder = "";
|
||||||
|
}
|
||||||
|
|
||||||
emit(UsersLoadedState(users: filteredUsers));
|
emit(UsersLoadedState(users: filteredUsers));
|
||||||
}
|
}
|
||||||
|
|
||||||
void _filterUsersByJobTitle(
|
void _filterUsersByJobTitle(
|
||||||
FilterUsersByJobEvent event, Emitter<UserTableState> emit) {
|
FilterUsersByJobEvent event, Emitter<UserTableState> emit) {
|
||||||
selectedJobTitles = event.selectedJob.toSet();
|
selectedJobTitles = event.selectedJob!.toSet();
|
||||||
|
emit(UsersLoadingState());
|
||||||
final filteredUsers = initialUsers.where((user) {
|
final filteredUsers = initialUsers.where((user) {
|
||||||
if (selectedJobTitles.isEmpty) return true;
|
if (selectedJobTitles.isEmpty) return true;
|
||||||
return selectedJobTitles.contains(user.jobTitle);
|
return selectedJobTitles.contains(user.jobTitle);
|
||||||
}).toList();
|
}).toList();
|
||||||
|
if (event.sortOrder == "Asc") {
|
||||||
|
currentSortOrder = "Asc";
|
||||||
|
filteredUsers.sort((a, b) => a.firstName
|
||||||
|
.toString()
|
||||||
|
.toLowerCase()
|
||||||
|
.compareTo(b.firstName.toString().toLowerCase()));
|
||||||
|
} else if (event.sortOrder == "Desc") {
|
||||||
|
currentSortOrder = "Desc";
|
||||||
|
filteredUsers.sort((a, b) => b.firstName!.compareTo(a.firstName!));
|
||||||
|
} else {
|
||||||
|
currentSortOrder = "";
|
||||||
|
}
|
||||||
emit(UsersLoadedState(users: filteredUsers));
|
emit(UsersLoadedState(users: filteredUsers));
|
||||||
}
|
}
|
||||||
|
|
||||||
void _filterUsersByCreated(
|
void _filterUsersByCreated(
|
||||||
FilterUsersByCreatedEvent event, Emitter<UserTableState> emit) {
|
FilterUsersByCreatedEvent event, Emitter<UserTableState> emit) {
|
||||||
selectedCreatedBy = event.selectedCreatedBy.toSet();
|
selectedCreatedBy = event.selectedCreatedBy!.toSet();
|
||||||
|
|
||||||
final filteredUsers = initialUsers.where((user) {
|
final filteredUsers = initialUsers.where((user) {
|
||||||
if (selectedCreatedBy.isEmpty) return true;
|
if (selectedCreatedBy.isEmpty) return true;
|
||||||
return selectedCreatedBy.contains(user.invitedBy);
|
return selectedCreatedBy.contains(user.invitedBy);
|
||||||
}).toList();
|
}).toList();
|
||||||
|
|
||||||
|
if (event.sortOrder == "Asc") {
|
||||||
|
currentSortOrder = "Asc";
|
||||||
|
filteredUsers.sort((a, b) => a.firstName
|
||||||
|
.toString()
|
||||||
|
.toLowerCase()
|
||||||
|
.compareTo(b.firstName.toString().toLowerCase()));
|
||||||
|
} else if (event.sortOrder == "Desc") {
|
||||||
|
currentSortOrder = "Desc";
|
||||||
|
filteredUsers.sort((a, b) => b.firstName!.compareTo(a.firstName!));
|
||||||
|
} else {
|
||||||
|
currentSortOrder = "";
|
||||||
|
}
|
||||||
emit(UsersLoadedState(users: filteredUsers));
|
emit(UsersLoadedState(users: filteredUsers));
|
||||||
}
|
}
|
||||||
|
|
||||||
void _filterUserStatus(
|
void _filterUserStatus(
|
||||||
FilterUsersByDeActevateEvent event, Emitter<UserTableState> emit) {
|
FilterUsersByDeActevateEvent event, Emitter<UserTableState> emit) {
|
||||||
selectedStatuses = event.selectedActivate.toSet();
|
selectedStatuses = event.selectedActivate!.toSet();
|
||||||
|
|
||||||
final filteredUsers = initialUsers.where((user) {
|
final filteredUsers = initialUsers.where((user) {
|
||||||
if (selectedStatuses.isEmpty) return true;
|
if (selectedStatuses.isEmpty) return true;
|
||||||
return selectedStatuses.contains(user.status);
|
return selectedStatuses.contains(user.status);
|
||||||
}).toList();
|
}).toList();
|
||||||
|
if (event.sortOrder == "Asc") {
|
||||||
|
currentSortOrder = "Asc";
|
||||||
|
filteredUsers.sort((a, b) => a.firstName
|
||||||
|
.toString()
|
||||||
|
.toLowerCase()
|
||||||
|
.compareTo(b.firstName.toString().toLowerCase()));
|
||||||
|
} else if (event.sortOrder == "Desc") {
|
||||||
|
currentSortOrder = "Desc";
|
||||||
|
filteredUsers.sort((a, b) => b.firstName!.compareTo(a.firstName!));
|
||||||
|
} else {
|
||||||
|
currentSortOrder = "";
|
||||||
|
}
|
||||||
emit(UsersLoadedState(users: filteredUsers));
|
emit(UsersLoadedState(users: filteredUsers));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -89,35 +89,36 @@ class DeleteUserEvent extends UserTableEvent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class FilterUsersByRoleEvent extends UserTableEvent {
|
class FilterUsersByRoleEvent extends UserTableEvent {
|
||||||
final List<String> selectedRoles;
|
final List<String>? selectedRoles;
|
||||||
|
final String? sortOrder;
|
||||||
|
|
||||||
FilterUsersByRoleEvent(this.selectedRoles);
|
const FilterUsersByRoleEvent({this.selectedRoles, this.sortOrder});
|
||||||
@override
|
List<Object?> get props => [selectedRoles, sortOrder];
|
||||||
List<Object?> get props => [selectedRoles];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class FilterUsersByJobEvent extends UserTableEvent {
|
class FilterUsersByJobEvent extends UserTableEvent {
|
||||||
final List<String> selectedJob;
|
final List<String>? selectedJob;
|
||||||
|
final String? sortOrder;
|
||||||
|
|
||||||
FilterUsersByJobEvent(this.selectedJob);
|
const FilterUsersByJobEvent({this.selectedJob, this.sortOrder});
|
||||||
@override
|
List<Object?> get props => [selectedJob, sortOrder];
|
||||||
List<Object?> get props => [selectedJob];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class FilterUsersByCreatedEvent extends UserTableEvent {
|
class FilterUsersByCreatedEvent extends UserTableEvent {
|
||||||
final List<String> selectedCreatedBy;
|
final List<String>? selectedCreatedBy;
|
||||||
|
|
||||||
FilterUsersByCreatedEvent(this.selectedCreatedBy);
|
final String? sortOrder;
|
||||||
@override
|
|
||||||
List<Object?> get props => [selectedCreatedBy];
|
const FilterUsersByCreatedEvent({this.selectedCreatedBy, this.sortOrder});
|
||||||
|
List<Object?> get props => [selectedCreatedBy, sortOrder];
|
||||||
}
|
}
|
||||||
|
|
||||||
class FilterUsersByDeActevateEvent extends UserTableEvent {
|
class FilterUsersByDeActevateEvent extends UserTableEvent {
|
||||||
final List<String> selectedActivate;
|
final List<String>? selectedActivate;
|
||||||
|
final String? sortOrder;
|
||||||
|
|
||||||
FilterUsersByDeActevateEvent(this.selectedActivate);
|
const FilterUsersByDeActevateEvent({this.selectedActivate, this.sortOrder});
|
||||||
@override
|
List<Object?> get props => [selectedActivate, sortOrder];
|
||||||
List<Object?> get props => [selectedActivate];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class FilterOptionsEvent extends UserTableEvent {
|
class FilterOptionsEvent extends UserTableEvent {
|
||||||
|
|||||||
@@ -19,19 +19,13 @@ class DynamicTableScreen extends StatefulWidget {
|
|||||||
class _DynamicTableScreenState extends State<DynamicTableScreen>
|
class _DynamicTableScreenState extends State<DynamicTableScreen>
|
||||||
with WidgetsBindingObserver {
|
with WidgetsBindingObserver {
|
||||||
late List<double> columnWidths;
|
late List<double> columnWidths;
|
||||||
|
late double totalWidth;
|
||||||
|
|
||||||
// @override
|
|
||||||
// void initState() {
|
|
||||||
// super.initState();
|
|
||||||
// // Initialize column widths with default sizes proportional to the screen width
|
|
||||||
// // Assigning placeholder values here. The actual sizes will be updated in `build`.
|
|
||||||
// }
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
setState(() {
|
columnWidths = List<double>.filled(widget.titles.length, 150.0);
|
||||||
columnWidths = List<double>.filled(widget.titles.length, 150.0);
|
totalWidth = columnWidths.reduce((a, b) => a + b);
|
||||||
});
|
|
||||||
WidgetsBinding.instance.addObserver(this);
|
WidgetsBinding.instance.addObserver(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -44,7 +38,6 @@ class _DynamicTableScreenState extends State<DynamicTableScreen>
|
|||||||
@override
|
@override
|
||||||
void didChangeMetrics() {
|
void didChangeMetrics() {
|
||||||
super.didChangeMetrics();
|
super.didChangeMetrics();
|
||||||
// Screen size might have changed
|
|
||||||
final newScreenWidth = MediaQuery.of(context).size.width;
|
final newScreenWidth = MediaQuery.of(context).size.width;
|
||||||
setState(() {
|
setState(() {
|
||||||
columnWidths = List<double>.generate(widget.titles.length, (index) {
|
columnWidths = List<double>.generate(widget.titles.length, (index) {
|
||||||
@@ -53,7 +46,7 @@ class _DynamicTableScreenState extends State<DynamicTableScreen>
|
|||||||
0.12; // 20% of screen width for the second column
|
0.12; // 20% of screen width for the second column
|
||||||
} else if (index == 9) {
|
} else if (index == 9) {
|
||||||
return newScreenWidth *
|
return newScreenWidth *
|
||||||
0.2; // 25% of screen width for the tenth column
|
0.1; // 25% of screen width for the tenth column
|
||||||
}
|
}
|
||||||
return newScreenWidth *
|
return newScreenWidth *
|
||||||
0.09; // Default to 10% of screen width for other columns
|
0.09; // Default to 10% of screen width for other columns
|
||||||
@@ -64,293 +57,204 @@ class _DynamicTableScreenState extends State<DynamicTableScreen>
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final screenWidth = MediaQuery.of(context).size.width;
|
final screenWidth = MediaQuery.of(context).size.width;
|
||||||
|
if (columnWidths.every((width) => width == screenWidth * 7)) {
|
||||||
// Initialize column widths if they are still set to placeholder values
|
|
||||||
if (columnWidths.every((width) => width == 120.0)) {
|
|
||||||
columnWidths = List<double>.generate(widget.titles.length, (index) {
|
columnWidths = List<double>.generate(widget.titles.length, (index) {
|
||||||
if (index == 1) {
|
if (index == 1) {
|
||||||
return screenWidth * 0.11;
|
return screenWidth * 0.11;
|
||||||
} else if (index == 9) {
|
} else if (index == 9) {
|
||||||
return screenWidth * 0.2;
|
return screenWidth * 0.1;
|
||||||
}
|
}
|
||||||
return screenWidth * 0.11;
|
return screenWidth * 0.09;
|
||||||
});
|
});
|
||||||
setState(() {});
|
setState(() {});
|
||||||
}
|
}
|
||||||
return Container(
|
return SingleChildScrollView(
|
||||||
child: SingleChildScrollView(
|
clipBehavior: Clip.none,
|
||||||
clipBehavior: Clip.none,
|
scrollDirection: Axis.horizontal,
|
||||||
scrollDirection: Axis.horizontal,
|
child: Container(
|
||||||
child: Container(
|
decoration: containerDecoration.copyWith(
|
||||||
decoration: containerDecoration.copyWith(
|
color: ColorsManager.whiteColors,
|
||||||
color: ColorsManager.whiteColors,
|
borderRadius: const BorderRadius.all(Radius.circular(20))),
|
||||||
borderRadius: const BorderRadius.all(Radius.circular(20))),
|
child: FittedBox(
|
||||||
child: FittedBox(
|
child: Column(
|
||||||
child: Column(
|
children: [
|
||||||
children: [
|
Container(
|
||||||
// Header Row with Resizable Columns
|
width: totalWidth,
|
||||||
Container(
|
decoration: containerDecoration.copyWith(
|
||||||
width: MediaQuery.of(context).size.width,
|
color: ColorsManager.circleRolesBackground,
|
||||||
decoration: containerDecoration.copyWith(
|
borderRadius: const BorderRadius.only(
|
||||||
color: ColorsManager.circleRolesBackground,
|
topLeft: Radius.circular(15),
|
||||||
borderRadius: const BorderRadius.only(
|
topRight: Radius.circular(15))),
|
||||||
topLeft: Radius.circular(15),
|
child: Row(
|
||||||
topRight: Radius.circular(15))),
|
children: List.generate(widget.titles.length, (index) {
|
||||||
child: Row(
|
return Row(
|
||||||
children: List.generate(widget.titles.length, (index) {
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
return Row(
|
children: [
|
||||||
mainAxisAlignment: MainAxisAlignment.start,
|
FittedBox(
|
||||||
children: [
|
child: Container(
|
||||||
FittedBox(
|
padding: const EdgeInsets.only(left: 5, right: 5),
|
||||||
child: Container(
|
width: columnWidths[index],
|
||||||
padding: const EdgeInsets.only(left: 5, right: 5),
|
child: Row(
|
||||||
width: columnWidths[index],
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
child: Row(
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
mainAxisAlignment:
|
children: [
|
||||||
MainAxisAlignment.spaceBetween,
|
SizedBox(
|
||||||
crossAxisAlignment: CrossAxisAlignment.center,
|
child: Text(
|
||||||
children: [
|
widget.titles[index],
|
||||||
SizedBox(
|
maxLines: 2,
|
||||||
child: Text(
|
style: const TextStyle(
|
||||||
widget.titles[index],
|
overflow: TextOverflow.ellipsis,
|
||||||
maxLines: 2,
|
fontWeight: FontWeight.w400,
|
||||||
style: const TextStyle(
|
fontSize: 13,
|
||||||
overflow: TextOverflow.ellipsis,
|
color: ColorsManager.grayColor,
|
||||||
fontWeight: FontWeight.w400,
|
|
||||||
fontSize: 13,
|
|
||||||
color: ColorsManager.grayColor,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
if (index != 1 &&
|
),
|
||||||
index != 9 &&
|
if (index != 1 &&
|
||||||
index != 8 &&
|
index != 9 &&
|
||||||
index != 5)
|
index != 8 &&
|
||||||
FittedBox(
|
index != 5)
|
||||||
child: IconButton(
|
FittedBox(
|
||||||
icon: SvgPicture.asset(
|
child: IconButton(
|
||||||
Assets.filterTableIcon,
|
icon: SvgPicture.asset(
|
||||||
fit: BoxFit.none,
|
Assets.filterTableIcon,
|
||||||
),
|
fit: BoxFit.none,
|
||||||
onPressed: () {
|
|
||||||
if (widget.onFilter != null) {
|
|
||||||
widget.onFilter!(index);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
)
|
onPressed: () {
|
||||||
],
|
if (widget.onFilter != null) {
|
||||||
),
|
widget.onFilter!(index);
|
||||||
),
|
}
|
||||||
),
|
},
|
||||||
GestureDetector(
|
|
||||||
onHorizontalDragUpdate: (details) {
|
|
||||||
setState(() {
|
|
||||||
columnWidths[index] = (columnWidths[index] +
|
|
||||||
details.delta.dx)
|
|
||||||
.clamp(
|
|
||||||
150.0, 300.0); // Minimum & Maximum size
|
|
||||||
});
|
|
||||||
},
|
|
||||||
child: MouseRegion(
|
|
||||||
cursor: SystemMouseCursors
|
|
||||||
.resizeColumn, // Set the cursor to resize
|
|
||||||
child: Container(
|
|
||||||
color: Colors.green,
|
|
||||||
child: Container(
|
|
||||||
color: ColorsManager.boxDivider,
|
|
||||||
width: 1,
|
|
||||||
height: 50, // Height of the header cell
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
}),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
// Data Rows with Dividers
|
|
||||||
widget.rows.isEmpty
|
|
||||||
? Container(
|
|
||||||
child: SizedBox(
|
|
||||||
height: MediaQuery.of(context).size.height / 2,
|
|
||||||
child: Container(
|
|
||||||
child: Column(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.center,
|
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
|
||||||
children: [
|
|
||||||
Column(
|
|
||||||
children: [
|
|
||||||
SvgPicture.asset(Assets.emptyTable),
|
|
||||||
const SizedBox(
|
|
||||||
height: 15,
|
|
||||||
),
|
),
|
||||||
const Text(
|
)
|
||||||
'No Users',
|
|
||||||
style: TextStyle(
|
|
||||||
color: ColorsManager.lightGrayColor,
|
|
||||||
fontSize: 16,
|
|
||||||
fontWeight: FontWeight.w700),
|
|
||||||
)
|
|
||||||
],
|
|
||||||
),
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
)
|
GestureDetector(
|
||||||
: Center(
|
onHorizontalDragUpdate: (details) {
|
||||||
child: Container(
|
setState(() {
|
||||||
// height: MediaQuery.of(context).size.height * 0.59,
|
columnWidths[index] =
|
||||||
width: MediaQuery.of(context).size.width,
|
(columnWidths[index] + details.delta.dx)
|
||||||
decoration: containerDecoration.copyWith(
|
.clamp(150.0, 300.0);
|
||||||
color: ColorsManager.whiteColors,
|
totalWidth = columnWidths.reduce((a, b) => a + b);
|
||||||
borderRadius: const BorderRadius.only(
|
});
|
||||||
bottomLeft: Radius.circular(15),
|
},
|
||||||
bottomRight: Radius.circular(15))),
|
child: MouseRegion(
|
||||||
child: ListView.builder(
|
cursor: SystemMouseCursors.resizeColumn,
|
||||||
physics: const NeverScrollableScrollPhysics(),
|
child: Container(
|
||||||
shrinkWrap: true,
|
color: Colors.green,
|
||||||
itemCount: widget.rows.length,
|
child: Container(
|
||||||
itemBuilder: (context, rowIndex) {
|
color: ColorsManager.boxDivider,
|
||||||
if (columnWidths
|
width: 1,
|
||||||
.every((width) => width == 120.0)) {
|
height: 50,
|
||||||
columnWidths = List<double>.generate(
|
),
|
||||||
widget.titles.length, (index) {
|
),
|
||||||
if (index == 1) {
|
),
|
||||||
return screenWidth * 0.11;
|
),
|
||||||
} else if (index == 9) {
|
],
|
||||||
return screenWidth * 0.2;
|
);
|
||||||
}
|
}),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
widget.rows.isEmpty
|
||||||
|
? SizedBox(
|
||||||
|
height: MediaQuery.of(context).size.height / 2,
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
Column(
|
||||||
|
children: [
|
||||||
|
SvgPicture.asset(Assets.emptyTable),
|
||||||
|
const SizedBox(
|
||||||
|
height: 15,
|
||||||
|
),
|
||||||
|
const Text(
|
||||||
|
'No Users',
|
||||||
|
style: TextStyle(
|
||||||
|
color: ColorsManager.lightGrayColor,
|
||||||
|
fontSize: 16,
|
||||||
|
fontWeight: FontWeight.w700),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
)
|
||||||
|
: Center(
|
||||||
|
child: Container(
|
||||||
|
width: totalWidth,
|
||||||
|
decoration: containerDecoration.copyWith(
|
||||||
|
color: ColorsManager.whiteColors,
|
||||||
|
borderRadius: const BorderRadius.only(
|
||||||
|
bottomLeft: Radius.circular(15),
|
||||||
|
bottomRight: Radius.circular(15))),
|
||||||
|
child: ListView.builder(
|
||||||
|
physics: const NeverScrollableScrollPhysics(),
|
||||||
|
shrinkWrap: true,
|
||||||
|
itemCount: widget.rows.length,
|
||||||
|
itemBuilder: (context, rowIndex) {
|
||||||
|
if (columnWidths.every((width) => width == 120.0)) {
|
||||||
|
columnWidths = List<double>.generate(
|
||||||
|
widget.titles.length, (index) {
|
||||||
|
if (index == 1) {
|
||||||
return screenWidth * 0.11;
|
return screenWidth * 0.11;
|
||||||
});
|
} else if (index == 9) {
|
||||||
setState(() {});
|
return screenWidth * 0.2;
|
||||||
}
|
}
|
||||||
final row = widget.rows[rowIndex];
|
return screenWidth * 0.11;
|
||||||
return Column(
|
});
|
||||||
children: [
|
setState(() {});
|
||||||
Container(
|
}
|
||||||
child: Padding(
|
final row = widget.rows[rowIndex];
|
||||||
padding: const EdgeInsets.only(
|
return Column(
|
||||||
left: 5,
|
children: [
|
||||||
top: 10,
|
Container(
|
||||||
right: 5,
|
child: Padding(
|
||||||
bottom: 10),
|
padding: const EdgeInsets.only(
|
||||||
child: Row(
|
left: 5, top: 10, right: 5, bottom: 10),
|
||||||
children:
|
child: Row(
|
||||||
List.generate(row.length, (index) {
|
children:
|
||||||
return SizedBox(
|
List.generate(row.length, (index) {
|
||||||
width: columnWidths[index],
|
|
||||||
child: SizedBox(
|
|
||||||
child: Padding(
|
|
||||||
padding: const EdgeInsets.only(
|
|
||||||
left: 15, right: 10),
|
|
||||||
child: row[index],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
if (rowIndex < widget.rows.length - 1)
|
|
||||||
Row(
|
|
||||||
children: List.generate(
|
|
||||||
widget.titles.length, (index) {
|
|
||||||
return SizedBox(
|
return SizedBox(
|
||||||
width: columnWidths[index],
|
width: columnWidths[index],
|
||||||
child: const Divider(
|
child: SizedBox(
|
||||||
color: ColorsManager.boxDivider,
|
child: Padding(
|
||||||
thickness: 1,
|
padding: const EdgeInsets.only(
|
||||||
height: 1,
|
left: 15, right: 10),
|
||||||
|
child: row[index],
|
||||||
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
],
|
),
|
||||||
);
|
),
|
||||||
},
|
if (rowIndex < widget.rows.length - 1)
|
||||||
),
|
Row(
|
||||||
|
children: List.generate(
|
||||||
|
widget.titles.length, (index) {
|
||||||
|
return SizedBox(
|
||||||
|
width: columnWidths[index],
|
||||||
|
child: const Divider(
|
||||||
|
color: ColorsManager.boxDivider,
|
||||||
|
thickness: 1,
|
||||||
|
height: 1,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
},
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
),
|
||||||
),
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Widget build(BuildContext context) {
|
|
||||||
// return Scaffold(
|
|
||||||
// body: SingleChildScrollView(
|
|
||||||
// scrollDirection: Axis.horizontal,
|
|
||||||
// child: SingleChildScrollView(
|
|
||||||
// scrollDirection: Axis.vertical,
|
|
||||||
// child: Column(
|
|
||||||
// children: [
|
|
||||||
// // Header Row with Resizable Columns
|
|
||||||
// Container(
|
|
||||||
// color: Colors.green,
|
|
||||||
// child: Row(
|
|
||||||
// children: List.generate(widget.titles.length, (index) {
|
|
||||||
// return Row(
|
|
||||||
// children: [
|
|
||||||
// Container(
|
|
||||||
// width: columnWidths[index],
|
|
||||||
// decoration: const BoxDecoration(
|
|
||||||
// color: Colors.green,
|
|
||||||
// ),
|
|
||||||
// child: Text(
|
|
||||||
// widget.titles[index],
|
|
||||||
// style: TextStyle(fontWeight: FontWeight.bold),
|
|
||||||
// textAlign: TextAlign.center,
|
|
||||||
// ),
|
|
||||||
// ),
|
|
||||||
// GestureDetector(
|
|
||||||
// onHorizontalDragUpdate: (details) {
|
|
||||||
// setState(() {
|
|
||||||
// columnWidths[index] = (columnWidths[index] +
|
|
||||||
// details.delta.dx)
|
|
||||||
// .clamp(50.0, 300.0); // Minimum & Maximum size
|
|
||||||
// });
|
|
||||||
// },
|
|
||||||
// child: MouseRegion(
|
|
||||||
// cursor: SystemMouseCursors
|
|
||||||
// .resizeColumn, // Set the cursor to resize
|
|
||||||
// child: Container(
|
|
||||||
// color: Colors.green,
|
|
||||||
// child: Container(
|
|
||||||
// color: Colors.black,
|
|
||||||
// width: 1,
|
|
||||||
// height: 50, // Height of the header cell
|
|
||||||
// ),
|
|
||||||
// ),
|
|
||||||
// ),
|
|
||||||
// ),
|
|
||||||
// ],
|
|
||||||
// );
|
|
||||||
// }),
|
|
||||||
// ),
|
|
||||||
// ),
|
|
||||||
// // Data Rows
|
|
||||||
// ...widget.rows.map((row) {
|
|
||||||
// return Row(
|
|
||||||
// children: List.generate(row.length, (index) {
|
|
||||||
// return Container(
|
|
||||||
// width: columnWidths[index],
|
|
||||||
// child: row[index],
|
|
||||||
// );
|
|
||||||
// }),
|
|
||||||
// );
|
|
||||||
// }).toList(),
|
|
||||||
// ],
|
|
||||||
// ),
|
|
||||||
// ),
|
|
||||||
// ),
|
|
||||||
// );
|
|
||||||
// }
|
|
||||||
@@ -107,7 +107,6 @@ class UsersPage extends StatelessWidget {
|
|||||||
builder: (context, state) {
|
builder: (context, state) {
|
||||||
final screenSize = MediaQuery.of(context).size;
|
final screenSize = MediaQuery.of(context).size;
|
||||||
final _blocRole = BlocProvider.of<UserTableBloc>(context);
|
final _blocRole = BlocProvider.of<UserTableBloc>(context);
|
||||||
|
|
||||||
if (state is UsersLoadingState) {
|
if (state is UsersLoadingState) {
|
||||||
_blocRole.add(ChangePage(_blocRole.currentPage));
|
_blocRole.add(ChangePage(_blocRole.currentPage));
|
||||||
return const Center(child: CircularProgressIndicator());
|
return const Center(child: CircularProgressIndicator());
|
||||||
@@ -189,8 +188,6 @@ class UsersPage extends StatelessWidget {
|
|||||||
const SizedBox(height: 25),
|
const SizedBox(height: 25),
|
||||||
DynamicTableScreen(
|
DynamicTableScreen(
|
||||||
onFilter: (columnIndex) {
|
onFilter: (columnIndex) {
|
||||||
_blocRole.add(FilterClearEvent());
|
|
||||||
|
|
||||||
if (columnIndex == 0) {
|
if (columnIndex == 0) {
|
||||||
showNameMenu(
|
showNameMenu(
|
||||||
context: context,
|
context: context,
|
||||||
@@ -210,11 +207,12 @@ class UsersPage extends StatelessWidget {
|
|||||||
if (columnIndex == 2) {
|
if (columnIndex == 2) {
|
||||||
final Map<String, bool> checkboxStates = {
|
final Map<String, bool> checkboxStates = {
|
||||||
for (var item in _blocRole.jobTitle)
|
for (var item in _blocRole.jobTitle)
|
||||||
item: false, // Initialize with false
|
item: _blocRole.selectedJobTitles.contains(item),
|
||||||
};
|
};
|
||||||
final RenderBox overlay = Overlay.of(context)
|
final RenderBox overlay = Overlay.of(context)
|
||||||
.context
|
.context
|
||||||
.findRenderObject() as RenderBox;
|
.findRenderObject() as RenderBox;
|
||||||
|
|
||||||
showPopUpFilterMenu(
|
showPopUpFilterMenu(
|
||||||
position: RelativeRect.fromLTRB(
|
position: RelativeRect.fromLTRB(
|
||||||
overlay.size.width / 4,
|
overlay.size.width / 4,
|
||||||
@@ -225,26 +223,28 @@ class UsersPage extends StatelessWidget {
|
|||||||
list: _blocRole.jobTitle,
|
list: _blocRole.jobTitle,
|
||||||
context: context,
|
context: context,
|
||||||
checkboxStates: checkboxStates,
|
checkboxStates: checkboxStates,
|
||||||
|
isSelected: _blocRole.currentSortOrder,
|
||||||
onOkPressed: () {
|
onOkPressed: () {
|
||||||
|
_blocRole.add(FilterClearEvent());
|
||||||
final selectedItems = checkboxStates.entries
|
final selectedItems = checkboxStates.entries
|
||||||
.where((entry) => entry.value)
|
.where((entry) => entry.value)
|
||||||
.map((entry) => entry.key)
|
.map((entry) => entry.key)
|
||||||
.toList();
|
.toList();
|
||||||
Navigator.of(context).pop();
|
Navigator.of(context).pop();
|
||||||
_blocRole.add(FilterUsersByJobEvent(selectedItems));
|
_blocRole.add(FilterUsersByJobEvent(
|
||||||
|
selectedJob: selectedItems,
|
||||||
|
sortOrder: _blocRole.currentSortOrder,
|
||||||
|
));
|
||||||
},
|
},
|
||||||
onSortAtoZ: () {
|
onSortAtoZ: (v) {
|
||||||
context
|
_blocRole.currentSortOrder = v;
|
||||||
.read<UserTableBloc>()
|
|
||||||
.add(const SortUsersByNameAsc());
|
|
||||||
},
|
},
|
||||||
onSortZtoA: () {
|
onSortZtoA: (v) {
|
||||||
context
|
_blocRole.currentSortOrder = v;
|
||||||
.read<UserTableBloc>()
|
|
||||||
.add(const SortUsersByNameDesc());
|
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (columnIndex == 3) {
|
if (columnIndex == 3) {
|
||||||
final Map<String, bool> checkboxStates = {
|
final Map<String, bool> checkboxStates = {
|
||||||
for (var item in _blocRole.roleTypes)
|
for (var item in _blocRole.roleTypes)
|
||||||
@@ -263,32 +263,31 @@ class UsersPage extends StatelessWidget {
|
|||||||
list: _blocRole.roleTypes,
|
list: _blocRole.roleTypes,
|
||||||
context: context,
|
context: context,
|
||||||
checkboxStates: checkboxStates,
|
checkboxStates: checkboxStates,
|
||||||
|
isSelected: _blocRole.currentSortOrder,
|
||||||
onOkPressed: () {
|
onOkPressed: () {
|
||||||
|
_blocRole.add(FilterClearEvent());
|
||||||
final selectedItems = checkboxStates.entries
|
final selectedItems = checkboxStates.entries
|
||||||
.where((entry) => entry.value)
|
.where((entry) => entry.value)
|
||||||
.map((entry) => entry.key)
|
.map((entry) => entry.key)
|
||||||
.toList();
|
.toList();
|
||||||
Navigator.of(context).pop();
|
Navigator.of(context).pop();
|
||||||
context
|
context.read<UserTableBloc>().add(
|
||||||
.read<UserTableBloc>()
|
FilterUsersByRoleEvent(
|
||||||
.add(FilterUsersByRoleEvent(selectedItems));
|
selectedRoles: selectedItems,
|
||||||
|
sortOrder: _blocRole.currentSortOrder));
|
||||||
},
|
},
|
||||||
onSortAtoZ: () {
|
onSortAtoZ: (v) {
|
||||||
context
|
_blocRole.currentSortOrder = v;
|
||||||
.read<UserTableBloc>()
|
|
||||||
.add(const SortUsersByNameAsc());
|
|
||||||
},
|
},
|
||||||
onSortZtoA: () {
|
onSortZtoA: (v) {
|
||||||
context
|
_blocRole.currentSortOrder = v;
|
||||||
.read<UserTableBloc>()
|
|
||||||
.add(const SortUsersByNameDesc());
|
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if (columnIndex == 4) {
|
if (columnIndex == 4) {
|
||||||
showDateFilterMenu(
|
showDateFilterMenu(
|
||||||
context: context,
|
context: context,
|
||||||
isSelected: _blocRole.currentSortOrderDate,
|
isSelected: _blocRole.currentSortOrder,
|
||||||
aToZTap: () {
|
aToZTap: () {
|
||||||
context
|
context
|
||||||
.read<UserTableBloc>()
|
.read<UserTableBloc>()
|
||||||
@@ -319,32 +318,30 @@ class UsersPage extends StatelessWidget {
|
|||||||
list: _blocRole.createdBy,
|
list: _blocRole.createdBy,
|
||||||
context: context,
|
context: context,
|
||||||
checkboxStates: checkboxStates,
|
checkboxStates: checkboxStates,
|
||||||
|
isSelected: _blocRole.currentSortOrder,
|
||||||
onOkPressed: () {
|
onOkPressed: () {
|
||||||
|
_blocRole.add(FilterClearEvent());
|
||||||
final selectedItems = checkboxStates.entries
|
final selectedItems = checkboxStates.entries
|
||||||
.where((entry) => entry.value)
|
.where((entry) => entry.value)
|
||||||
.map((entry) => entry.key)
|
.map((entry) => entry.key)
|
||||||
.toList();
|
.toList();
|
||||||
Navigator.of(context).pop();
|
Navigator.of(context).pop();
|
||||||
_blocRole
|
_blocRole.add(FilterUsersByCreatedEvent(
|
||||||
.add(FilterUsersByCreatedEvent(selectedItems));
|
selectedCreatedBy: selectedItems,
|
||||||
|
sortOrder: _blocRole.currentSortOrder));
|
||||||
},
|
},
|
||||||
onSortAtoZ: () {
|
onSortAtoZ: (v) {
|
||||||
context
|
_blocRole.currentSortOrder = v;
|
||||||
.read<UserTableBloc>()
|
|
||||||
.add(const SortUsersByNameAsc());
|
|
||||||
},
|
},
|
||||||
onSortZtoA: () {
|
onSortZtoA: (v) {
|
||||||
context
|
_blocRole.currentSortOrder = v;
|
||||||
.read<UserTableBloc>()
|
|
||||||
.add(const SortUsersByNameDesc());
|
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (columnIndex == 7) {
|
if (columnIndex == 7) {
|
||||||
final Map<String, bool> checkboxStates = {
|
final Map<String, bool> checkboxStates = {
|
||||||
for (var item in _blocRole.status)
|
for (var item in _blocRole.status)
|
||||||
item: _blocRole.selectedCreatedBy.contains(item),
|
item: _blocRole.selectedStatuses.contains(item),
|
||||||
};
|
};
|
||||||
final RenderBox overlay = Overlay.of(context)
|
final RenderBox overlay = Overlay.of(context)
|
||||||
.context
|
.context
|
||||||
@@ -359,24 +356,24 @@ class UsersPage extends StatelessWidget {
|
|||||||
list: _blocRole.status,
|
list: _blocRole.status,
|
||||||
context: context,
|
context: context,
|
||||||
checkboxStates: checkboxStates,
|
checkboxStates: checkboxStates,
|
||||||
|
isSelected: _blocRole.currentSortOrder,
|
||||||
onOkPressed: () {
|
onOkPressed: () {
|
||||||
|
_blocRole.add(FilterClearEvent());
|
||||||
|
|
||||||
final selectedItems = checkboxStates.entries
|
final selectedItems = checkboxStates.entries
|
||||||
.where((entry) => entry.value)
|
.where((entry) => entry.value)
|
||||||
.map((entry) => entry.key)
|
.map((entry) => entry.key)
|
||||||
.toList();
|
.toList();
|
||||||
Navigator.of(context).pop();
|
Navigator.of(context).pop();
|
||||||
_blocRole
|
_blocRole.add(FilterUsersByDeActevateEvent(
|
||||||
.add(FilterUsersByCreatedEvent(selectedItems));
|
selectedActivate: selectedItems,
|
||||||
|
sortOrder: _blocRole.currentSortOrder));
|
||||||
},
|
},
|
||||||
onSortAtoZ: () {
|
onSortAtoZ: (v) {
|
||||||
context
|
_blocRole.currentSortOrder = v;
|
||||||
.read<UserTableBloc>()
|
|
||||||
.add(const SortUsersByNameAsc());
|
|
||||||
},
|
},
|
||||||
onSortZtoA: () {
|
onSortZtoA: (v) {
|
||||||
context
|
_blocRole.currentSortOrder = v;
|
||||||
.read<UserTableBloc>()
|
|
||||||
.add(const SortUsersByNameDesc());
|
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -412,8 +409,8 @@ class UsersPage extends StatelessWidget {
|
|||||||
rows: state.users.map((user) {
|
rows: state.users.map((user) {
|
||||||
return [
|
return [
|
||||||
Text('${user.firstName} ${user.lastName}'),
|
Text('${user.firstName} ${user.lastName}'),
|
||||||
Text(user.email ),
|
Text(user.email),
|
||||||
Text(user.jobTitle ?? ''),
|
Text(user.jobTitle ?? '-'),
|
||||||
Text(user.roleType ?? ''),
|
Text(user.roleType ?? ''),
|
||||||
Text(user.createdDate ?? ''),
|
Text(user.createdDate ?? ''),
|
||||||
Text(user.createdTime ?? ''),
|
Text(user.createdTime ?? ''),
|
||||||
@@ -476,11 +473,17 @@ class UsersPage extends StatelessWidget {
|
|||||||
barrierDismissible: false,
|
barrierDismissible: false,
|
||||||
builder: (BuildContext context) {
|
builder: (BuildContext context) {
|
||||||
return DeleteUserDialog(
|
return DeleteUserDialog(
|
||||||
onTapDelete: () {
|
onTapDelete: () async {
|
||||||
|
try {
|
||||||
_blocRole.add(DeleteUserEvent(
|
_blocRole.add(DeleteUserEvent(
|
||||||
user.uuid, context));
|
user.uuid, context));
|
||||||
},
|
await Future.delayed(
|
||||||
);
|
const Duration(seconds: 2));
|
||||||
|
return true;
|
||||||
|
} catch (e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
});
|
||||||
},
|
},
|
||||||
).then((v) {
|
).then((v) {
|
||||||
if (v != null) {
|
if (v != null) {
|
||||||
@@ -504,6 +507,7 @@ class UsersPage extends StatelessWidget {
|
|||||||
SizedBox(
|
SizedBox(
|
||||||
width: 500,
|
width: 500,
|
||||||
child: NumberPagination(
|
child: NumberPagination(
|
||||||
|
visiblePagesCount: 4,
|
||||||
buttonRadius: 10,
|
buttonRadius: 10,
|
||||||
selectedButtonColor: ColorsManager.secondaryColor,
|
selectedButtonColor: ColorsManager.secondaryColor,
|
||||||
buttonUnSelectedBorderColor:
|
buttonUnSelectedBorderColor:
|
||||||
|
|||||||
@@ -4,20 +4,17 @@ import 'package:syncrow_web/pages/spaces_management/all_spaces/model/product_mod
|
|||||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/space_model.dart';
|
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/space_model.dart';
|
||||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/bloc/space_management_event.dart';
|
import 'package:syncrow_web/pages/spaces_management/all_spaces/bloc/space_management_event.dart';
|
||||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/bloc/space_management_state.dart';
|
import 'package:syncrow_web/pages/spaces_management/all_spaces/bloc/space_management_state.dart';
|
||||||
import 'package:syncrow_web/pages/spaces_management/space_model/models/space_template_model.dart';
|
|
||||||
import 'package:syncrow_web/services/product_api.dart';
|
import 'package:syncrow_web/services/product_api.dart';
|
||||||
import 'package:syncrow_web/services/space_mana_api.dart';
|
import 'package:syncrow_web/services/space_mana_api.dart';
|
||||||
import 'package:syncrow_web/services/space_model_mang_api.dart';
|
|
||||||
|
|
||||||
class SpaceManagementBloc
|
class SpaceManagementBloc
|
||||||
extends Bloc<SpaceManagementEvent, SpaceManagementState> {
|
extends Bloc<SpaceManagementEvent, SpaceManagementState> {
|
||||||
final CommunitySpaceManagementApi _api;
|
final CommunitySpaceManagementApi _api;
|
||||||
final ProductApi _productApi;
|
final ProductApi _productApi;
|
||||||
final SpaceModelManagementApi _spaceModelApi;
|
|
||||||
|
|
||||||
List<ProductModel>? _cachedProducts;
|
List<ProductModel>? _cachedProducts;
|
||||||
|
|
||||||
SpaceManagementBloc(this._api, this._productApi, this._spaceModelApi)
|
SpaceManagementBloc(this._api, this._productApi)
|
||||||
: super(SpaceManagementInitial()) {
|
: super(SpaceManagementInitial()) {
|
||||||
on<LoadCommunityAndSpacesEvent>(_onLoadCommunityAndSpaces);
|
on<LoadCommunityAndSpacesEvent>(_onLoadCommunityAndSpaces);
|
||||||
on<UpdateSpacePositionEvent>(_onUpdateSpacePosition);
|
on<UpdateSpacePositionEvent>(_onUpdateSpacePosition);
|
||||||
@@ -29,8 +26,6 @@ class SpaceManagementBloc
|
|||||||
on<FetchProductsEvent>(_onFetchProducts);
|
on<FetchProductsEvent>(_onFetchProducts);
|
||||||
on<SelectSpaceEvent>(_onSelectSpace);
|
on<SelectSpaceEvent>(_onSelectSpace);
|
||||||
on<NewCommunityEvent>(_onNewCommunity);
|
on<NewCommunityEvent>(_onNewCommunity);
|
||||||
on<BlankStateEvent>(_onBlankState);
|
|
||||||
on<SpaceModelLoadEvent>(_onLoadSpaceModel);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void _onUpdateCommunity(
|
void _onUpdateCommunity(
|
||||||
@@ -108,45 +103,6 @@ class SpaceManagementBloc
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _onBlankState(
|
|
||||||
BlankStateEvent event, Emitter<SpaceManagementState> emit) async {
|
|
||||||
try {
|
|
||||||
final previousState = state;
|
|
||||||
|
|
||||||
if (previousState is SpaceManagementLoaded ||
|
|
||||||
previousState is BlankState) {
|
|
||||||
final prevCommunities = (previousState as dynamic).communities ?? [];
|
|
||||||
emit(BlankState(
|
|
||||||
communities: List<CommunityModel>.from(prevCommunities),
|
|
||||||
products: _cachedProducts ?? [],
|
|
||||||
));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
final communities = await _api.fetchCommunities();
|
|
||||||
final updatedCommunities =
|
|
||||||
await Future.wait(communities.map((community) async {
|
|
||||||
final spaces = await _fetchSpacesForCommunity(community.uuid);
|
|
||||||
return CommunityModel(
|
|
||||||
uuid: community.uuid,
|
|
||||||
createdAt: community.createdAt,
|
|
||||||
updatedAt: community.updatedAt,
|
|
||||||
name: community.name,
|
|
||||||
description: community.description,
|
|
||||||
spaces: spaces,
|
|
||||||
region: community.region,
|
|
||||||
);
|
|
||||||
}));
|
|
||||||
|
|
||||||
emit(BlankState(
|
|
||||||
communities: updatedCommunities,
|
|
||||||
products: _cachedProducts ?? [],
|
|
||||||
));
|
|
||||||
} catch (error) {
|
|
||||||
emit(SpaceManagementError('Error loading communities: $error'));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void _onLoadCommunityAndSpaces(
|
void _onLoadCommunityAndSpaces(
|
||||||
LoadCommunityAndSpacesEvent event,
|
LoadCommunityAndSpacesEvent event,
|
||||||
Emitter<SpaceManagementState> emit,
|
Emitter<SpaceManagementState> emit,
|
||||||
@@ -239,15 +195,11 @@ class SpaceManagementBloc
|
|||||||
SelectCommunityEvent event,
|
SelectCommunityEvent event,
|
||||||
Emitter<SpaceManagementState> emit,
|
Emitter<SpaceManagementState> emit,
|
||||||
) async {
|
) async {
|
||||||
try {
|
_handleCommunitySpaceStateUpdate(
|
||||||
_handleCommunitySpaceStateUpdate(
|
emit: emit,
|
||||||
emit: emit,
|
selectedCommunity: event.selectedCommunity,
|
||||||
selectedCommunity: event.selectedCommunity,
|
selectedSpace: null,
|
||||||
selectedSpace: null,
|
);
|
||||||
);
|
|
||||||
} catch (e) {
|
|
||||||
emit(SpaceManagementError('Error updating state: $e'));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void _onSelectSpace(
|
void _onSelectSpace(
|
||||||
@@ -271,8 +223,7 @@ class SpaceManagementBloc
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
if (previousState is SpaceManagementLoaded ||
|
if (previousState is SpaceManagementLoaded ||
|
||||||
previousState is BlankState ||
|
previousState is BlankState) {
|
||||||
previousState is SpaceModelLoaded) {
|
|
||||||
final communities = List<CommunityModel>.from(
|
final communities = List<CommunityModel>.from(
|
||||||
(previousState as dynamic).communities,
|
(previousState as dynamic).communities,
|
||||||
);
|
);
|
||||||
@@ -366,26 +317,26 @@ class SpaceManagementBloc
|
|||||||
try {
|
try {
|
||||||
if (space.uuid != null && space.uuid!.isNotEmpty) {
|
if (space.uuid != null && space.uuid!.isNotEmpty) {
|
||||||
final response = await _api.updateSpace(
|
final response = await _api.updateSpace(
|
||||||
communityId: communityUuid,
|
communityId: communityUuid,
|
||||||
spaceId: space.uuid!,
|
spaceId: space.uuid!,
|
||||||
name: space.name,
|
name: space.name,
|
||||||
parentId: space.parent?.uuid,
|
parentId: space.parent?.uuid,
|
||||||
isPrivate: space.isPrivate,
|
isPrivate: space.isPrivate,
|
||||||
position: space.position,
|
position: space.position,
|
||||||
icon: space.icon,
|
icon: space.icon,
|
||||||
direction: space.incomingConnection?.direction,
|
direction: space.incomingConnection?.direction,
|
||||||
);
|
products: space.selectedProducts);
|
||||||
} else {
|
} else {
|
||||||
// Call create if the space does not have a UUID
|
// Call create if the space does not have a UUID
|
||||||
final response = await _api.createSpace(
|
final response = await _api.createSpace(
|
||||||
communityId: communityUuid,
|
communityId: communityUuid,
|
||||||
name: space.name,
|
name: space.name,
|
||||||
parentId: space.parent?.uuid,
|
parentId: space.parent?.uuid,
|
||||||
isPrivate: space.isPrivate,
|
isPrivate: space.isPrivate,
|
||||||
position: space.position,
|
position: space.position,
|
||||||
icon: space.icon,
|
icon: space.icon,
|
||||||
direction: space.incomingConnection?.direction,
|
direction: space.incomingConnection?.direction,
|
||||||
);
|
products: space.selectedProducts);
|
||||||
space.uuid = response?.uuid;
|
space.uuid = response?.uuid;
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
@@ -419,37 +370,4 @@ class SpaceManagementBloc
|
|||||||
}
|
}
|
||||||
return result.toList(); // Convert back to a list
|
return result.toList(); // Convert back to a list
|
||||||
}
|
}
|
||||||
|
|
||||||
void _onLoadSpaceModel(
|
|
||||||
SpaceModelLoadEvent event, Emitter<SpaceManagementState> emit) async {
|
|
||||||
emit(SpaceManagementLoading());
|
|
||||||
try {
|
|
||||||
List<CommunityModel> communities = await _api.fetchCommunities();
|
|
||||||
|
|
||||||
List<CommunityModel> updatedCommunities = await Future.wait(
|
|
||||||
communities.map((community) async {
|
|
||||||
List<SpaceModel> spaces =
|
|
||||||
await _fetchSpacesForCommunity(community.uuid);
|
|
||||||
return CommunityModel(
|
|
||||||
uuid: community.uuid,
|
|
||||||
createdAt: community.createdAt,
|
|
||||||
updatedAt: community.updatedAt,
|
|
||||||
name: community.name,
|
|
||||||
description: community.description,
|
|
||||||
spaces: spaces, // New spaces list
|
|
||||||
region: community.region,
|
|
||||||
);
|
|
||||||
}).toList(),
|
|
||||||
);
|
|
||||||
|
|
||||||
List<SpaceTemplateModel> spaceModels =
|
|
||||||
await _spaceModelApi.listSpaceModels(page: 1);
|
|
||||||
emit(SpaceModelLoaded(
|
|
||||||
communities: updatedCommunities,
|
|
||||||
products: _cachedProducts ?? [],
|
|
||||||
spaceModels: spaceModels));
|
|
||||||
} catch (e) {
|
|
||||||
emit(SpaceManagementError('Error loading communities and spaces: $e'));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -140,8 +140,3 @@ class LoadSpaceHierarchyEvent extends SpaceManagementEvent {
|
|||||||
@override
|
@override
|
||||||
List<Object> get props => [communityId];
|
List<Object> get props => [communityId];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class BlankStateEvent extends SpaceManagementEvent {}
|
|
||||||
|
|
||||||
class SpaceModelLoadEvent extends SpaceManagementEvent {}
|
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ import 'package:equatable/equatable.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/product_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/space_model.dart';
|
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/space_model.dart';
|
||||||
import 'package:syncrow_web/pages/spaces_management/space_model/models/space_template_model.dart';
|
|
||||||
|
|
||||||
abstract class SpaceManagementState extends Equatable {
|
abstract class SpaceManagementState extends Equatable {
|
||||||
const SpaceManagementState();
|
const SpaceManagementState();
|
||||||
@@ -28,10 +27,6 @@ class SpaceManagementLoaded extends SpaceManagementState {
|
|||||||
this.selectedSpace});
|
this.selectedSpace});
|
||||||
}
|
}
|
||||||
|
|
||||||
class SpaceModelManagenetLoaded extends SpaceManagementState {
|
|
||||||
SpaceModelManagenetLoaded();
|
|
||||||
}
|
|
||||||
|
|
||||||
class BlankState extends SpaceManagementState {
|
class BlankState extends SpaceManagementState {
|
||||||
final List<CommunityModel> communities;
|
final List<CommunityModel> communities;
|
||||||
final List<ProductModel> products;
|
final List<ProductModel> products;
|
||||||
@@ -59,18 +54,3 @@ class SpaceManagementError extends SpaceManagementState {
|
|||||||
@override
|
@override
|
||||||
List<Object> get props => [errorMessage];
|
List<Object> get props => [errorMessage];
|
||||||
}
|
}
|
||||||
|
|
||||||
class SpaceModelLoaded extends SpaceManagementState {
|
|
||||||
final List<SpaceTemplateModel> spaceModels;
|
|
||||||
final List<ProductModel> products;
|
|
||||||
final List<CommunityModel> communities;
|
|
||||||
|
|
||||||
SpaceModelLoaded({
|
|
||||||
required this.communities,
|
|
||||||
required this.products,
|
|
||||||
required this.spaceModels,
|
|
||||||
});
|
|
||||||
|
|
||||||
@override
|
|
||||||
List<Object> get props => [communities, products, spaceModels];
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ class ProductModel {
|
|||||||
|
|
||||||
// Factory method to create a Product from JSON
|
// Factory method to create a Product from JSON
|
||||||
factory ProductModel.fromMap(Map<String, dynamic> json) {
|
factory ProductModel.fromMap(Map<String, dynamic> json) {
|
||||||
|
String icon = _mapIconToProduct(json['prodType']);
|
||||||
return ProductModel(
|
return ProductModel(
|
||||||
uuid: json['uuid'],
|
uuid: json['uuid'],
|
||||||
catName: json['catName'],
|
catName: json['catName'],
|
||||||
|
|||||||
@@ -1,18 +1,13 @@
|
|||||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/product_model.dart';
|
|
||||||
|
|
||||||
class SelectedProduct {
|
class SelectedProduct {
|
||||||
final String productId;
|
final String productId;
|
||||||
int count;
|
int count;
|
||||||
final String productName;
|
|
||||||
final ProductModel? product;
|
|
||||||
|
|
||||||
SelectedProduct({required this.productId, required this.count, required this.productName, this.product});
|
SelectedProduct({required this.productId, required this.count});
|
||||||
|
|
||||||
Map<String, dynamic> toJson() {
|
Map<String, dynamic> toJson() {
|
||||||
return {
|
return {
|
||||||
'productId': productId,
|
'productId': productId,
|
||||||
'count': count,
|
'count': count,
|
||||||
'productName': productName,
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ class SpaceModel {
|
|||||||
Offset position;
|
Offset position;
|
||||||
bool isHovered;
|
bool isHovered;
|
||||||
SpaceStatus status;
|
SpaceStatus status;
|
||||||
|
List<SelectedProduct> selectedProducts;
|
||||||
String internalId;
|
String internalId;
|
||||||
|
|
||||||
List<Connection> outgoingConnections = []; // Connections from this space
|
List<Connection> outgoingConnections = []; // Connections from this space
|
||||||
@@ -40,6 +41,7 @@ class SpaceModel {
|
|||||||
this.isHovered = false,
|
this.isHovered = false,
|
||||||
this.incomingConnection,
|
this.incomingConnection,
|
||||||
this.status = SpaceStatus.unchanged,
|
this.status = SpaceStatus.unchanged,
|
||||||
|
this.selectedProducts = const [],
|
||||||
}) : internalId = internalId ?? const Uuid().v4();
|
}) : internalId = internalId ?? const Uuid().v4();
|
||||||
|
|
||||||
factory SpaceModel.fromJson(Map<String, dynamic> json,
|
factory SpaceModel.fromJson(Map<String, dynamic> json,
|
||||||
@@ -83,6 +85,14 @@ class SpaceModel {
|
|||||||
icon: json['icon'] ?? Assets.location,
|
icon: json['icon'] ?? Assets.location,
|
||||||
position: Offset(json['x'] ?? 0, json['y'] ?? 0),
|
position: Offset(json['x'] ?? 0, json['y'] ?? 0),
|
||||||
isHovered: false,
|
isHovered: false,
|
||||||
|
selectedProducts: json['spaceProducts'] != null
|
||||||
|
? (json['spaceProducts'] as List).map((product) {
|
||||||
|
return SelectedProduct(
|
||||||
|
productId: product['product']['uuid'],
|
||||||
|
count: product['productCount'],
|
||||||
|
);
|
||||||
|
}).toList()
|
||||||
|
: [],
|
||||||
);
|
);
|
||||||
|
|
||||||
if (json['incomingConnections'] != null &&
|
if (json['incomingConnections'] != null &&
|
||||||
|
|||||||
@@ -1,15 +1,15 @@
|
|||||||
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/pages/device_managment/shared/navigate_home_grid_view.dart';
|
import 'package:syncrow_web/pages/device_managment/shared/navigate_home_grid_view.dart';
|
||||||
import 'package:syncrow_web/pages/spaces_management/structure_selector/bloc/center_body_bloc.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_bloc.dart';
|
||||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/bloc/space_management_event.dart';
|
import 'package:syncrow_web/pages/spaces_management/all_spaces/bloc/space_management_event.dart';
|
||||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/bloc/space_management_state.dart';
|
import 'package:syncrow_web/pages/spaces_management/all_spaces/bloc/space_management_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/product_model.dart';
|
||||||
|
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/space_model.dart';
|
||||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/widgets/loaded_space_widget.dart';
|
import 'package:syncrow_web/pages/spaces_management/all_spaces/widgets/loaded_space_widget.dart';
|
||||||
import 'package:syncrow_web/pages/spaces_management/structure_selector/view/center_body_widget.dart';
|
|
||||||
import 'package:syncrow_web/services/product_api.dart';
|
import 'package:syncrow_web/services/product_api.dart';
|
||||||
import 'package:syncrow_web/services/space_mana_api.dart';
|
import 'package:syncrow_web/services/space_mana_api.dart';
|
||||||
import 'package:syncrow_web/services/space_model_mang_api.dart';
|
|
||||||
import 'package:syncrow_web/web_layout/web_scaffold.dart';
|
import 'package:syncrow_web/web_layout/web_scaffold.dart';
|
||||||
|
|
||||||
class SpaceManagementPage extends StatefulWidget {
|
class SpaceManagementPage extends StatefulWidget {
|
||||||
@@ -22,54 +22,48 @@ class SpaceManagementPage extends StatefulWidget {
|
|||||||
class SpaceManagementPageState extends State<SpaceManagementPage> {
|
class SpaceManagementPageState extends State<SpaceManagementPage> {
|
||||||
final CommunitySpaceManagementApi _api = CommunitySpaceManagementApi();
|
final CommunitySpaceManagementApi _api = CommunitySpaceManagementApi();
|
||||||
final ProductApi _productApi = ProductApi();
|
final ProductApi _productApi = ProductApi();
|
||||||
final SpaceModelManagementApi _spaceModelApi = SpaceModelManagementApi();
|
Map<String, List<SpaceModel>> communitySpaces = {};
|
||||||
|
List<ProductModel> products = [];
|
||||||
|
bool isProductDataLoaded = false;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return MultiBlocProvider(
|
return BlocProvider(
|
||||||
providers: [
|
create: (context) => SpaceManagementBloc(_api, _productApi)
|
||||||
BlocProvider(
|
..add(LoadCommunityAndSpacesEvent()),
|
||||||
create: (_) => SpaceManagementBloc(_api, _productApi, _spaceModelApi)
|
|
||||||
..add(LoadCommunityAndSpacesEvent()),
|
|
||||||
),
|
|
||||||
BlocProvider(
|
|
||||||
create: (_) => CenterBodyBloc(),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
child: WebScaffold(
|
child: WebScaffold(
|
||||||
appBarTitle: Text('Space Management',
|
appBarTitle: Text('Space Management',
|
||||||
style: Theme.of(context).textTheme.headlineLarge),
|
style: Theme.of(context).textTheme.headlineLarge),
|
||||||
enableMenuSidebar: false,
|
enableMenuSidebar: false,
|
||||||
centerBody: CenterBodyWidget(),
|
|
||||||
rightBody: const NavigateHomeGridView(),
|
rightBody: const NavigateHomeGridView(),
|
||||||
scaffoldBody: BlocBuilder<SpaceManagementBloc, SpaceManagementState>(
|
scaffoldBody: BlocBuilder<SpaceManagementBloc, SpaceManagementState>(
|
||||||
builder: (context, state) {
|
builder: (context, state) {
|
||||||
if (state is SpaceManagementLoading) {
|
if (state is SpaceManagementLoading) {
|
||||||
return const Center(child: CircularProgressIndicator());
|
return const Center(child: CircularProgressIndicator());
|
||||||
} else if (state is BlankState) {
|
} else if (state is BlankState) {
|
||||||
return LoadedSpaceView(
|
return LoadedSpaceView(
|
||||||
communities: state.communities,
|
communities: state.communities,
|
||||||
selectedCommunity: null,
|
selectedCommunity: null,
|
||||||
selectedSpace: null,
|
selectedSpace: null,
|
||||||
products: state.products,
|
products: state.products,
|
||||||
);
|
);
|
||||||
} else if (state is SpaceManagementLoaded) {
|
} else if (state is SpaceManagementLoaded) {
|
||||||
return LoadedSpaceView(
|
return LoadedSpaceView(
|
||||||
communities: state.communities,
|
communities: state.communities,
|
||||||
selectedCommunity: state.selectedCommunity,
|
selectedCommunity: state.selectedCommunity,
|
||||||
selectedSpace: state.selectedSpace,
|
selectedSpace: state.selectedSpace,
|
||||||
products: state.products,
|
products: state.products,
|
||||||
);
|
);
|
||||||
} else if (state is SpaceModelLoaded) {
|
} else if (state is SpaceManagementError) {
|
||||||
return LoadedSpaceView(
|
return Center(child: Text('Error: ${state.errorMessage}'));
|
||||||
communities: state.communities,
|
}
|
||||||
products: state.products,
|
return Container();
|
||||||
spaceModels: state.spaceModels);
|
}),
|
||||||
} else if (state is SpaceManagementError) {
|
|
||||||
return Center(child: Text('Error: ${state.errorMessage}'));
|
|
||||||
}
|
|
||||||
return Container();
|
|
||||||
},
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -99,7 +99,7 @@ class _AddDeviceWidgetState extends State<AddDeviceWidget> {
|
|||||||
_buildActionButton('Cancel', ColorsManager.boxColor, ColorsManager.blackColor, () {
|
_buildActionButton('Cancel', ColorsManager.boxColor, ColorsManager.blackColor, () {
|
||||||
Navigator.of(context).pop();
|
Navigator.of(context).pop();
|
||||||
}),
|
}),
|
||||||
_buildActionButton('Continue', ColorsManager.secondaryColor, ColorsManager.whiteColors, () {
|
_buildActionButton('Continue', ColorsManager.secondaryColor, Colors.white, () {
|
||||||
Navigator.of(context).pop();
|
Navigator.of(context).pop();
|
||||||
if (widget.onProductsSelected != null) {
|
if (widget.onProductsSelected != null) {
|
||||||
widget.onProductsSelected!(productCounts);
|
widget.onProductsSelected!(productCounts);
|
||||||
@@ -114,7 +114,7 @@ class _AddDeviceWidgetState extends State<AddDeviceWidget> {
|
|||||||
Widget _buildDeviceTypeTile(ProductModel product, Size size) {
|
Widget _buildDeviceTypeTile(ProductModel product, Size size) {
|
||||||
final selectedProduct = productCounts.firstWhere(
|
final selectedProduct = productCounts.firstWhere(
|
||||||
(p) => p.productId == product.uuid,
|
(p) => p.productId == product.uuid,
|
||||||
orElse: () => SelectedProduct(productId: product.uuid, count: 0, productName: product.catName, product: product),
|
orElse: () => SelectedProduct(productId: product.uuid, count: 0),
|
||||||
);
|
);
|
||||||
|
|
||||||
return SizedBox(
|
return SizedBox(
|
||||||
@@ -143,7 +143,7 @@ class _AddDeviceWidgetState extends State<AddDeviceWidget> {
|
|||||||
if (newCount > 0) {
|
if (newCount > 0) {
|
||||||
if (!productCounts.contains(selectedProduct)) {
|
if (!productCounts.contains(selectedProduct)) {
|
||||||
productCounts
|
productCounts
|
||||||
.add(SelectedProduct(productId: product.uuid, count: newCount, productName: product.catName, product: product));
|
.add(SelectedProduct(productId: product.uuid, count: newCount));
|
||||||
} else {
|
} else {
|
||||||
selectedProduct.count = newCount;
|
selectedProduct.count = newCount;
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -178,7 +178,7 @@ class _CommunityStructureHeaderState extends State<CommunityStructureHeader> {
|
|||||||
padding: 2.0,
|
padding: 2.0,
|
||||||
height: buttonHeight,
|
height: buttonHeight,
|
||||||
elevation: 0,
|
elevation: 0,
|
||||||
borderColor: ColorsManager.lightGrayColor,
|
borderColor: Colors.grey.shade300,
|
||||||
child: Row(
|
child: Row(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
children: [
|
||||||
|
|||||||
@@ -299,7 +299,7 @@ class _CommunityStructureAreaState extends State<CommunityStructureArea> {
|
|||||||
isPrivate: false,
|
isPrivate: false,
|
||||||
children: [],
|
children: [],
|
||||||
status: SpaceStatus.newSpace,
|
status: SpaceStatus.newSpace,
|
||||||
);
|
selectedProducts: selectedProducts);
|
||||||
|
|
||||||
if (parentIndex != null && direction != null) {
|
if (parentIndex != null && direction != null) {
|
||||||
SpaceModel parentSpace = spaces[parentIndex];
|
SpaceModel parentSpace = spaces[parentIndex];
|
||||||
@@ -335,12 +335,14 @@ class _CommunityStructureAreaState extends State<CommunityStructureArea> {
|
|||||||
icon: space.icon,
|
icon: space.icon,
|
||||||
editSpace: space,
|
editSpace: space,
|
||||||
isEdit: true,
|
isEdit: true,
|
||||||
|
selectedProducts: space.selectedProducts,
|
||||||
onCreateSpace: (String name, String icon,
|
onCreateSpace: (String name, String icon,
|
||||||
List<SelectedProduct> selectedProducts) {
|
List<SelectedProduct> selectedProducts) {
|
||||||
setState(() {
|
setState(() {
|
||||||
// Update the space's properties
|
// Update the space's properties
|
||||||
space.name = name;
|
space.name = name;
|
||||||
space.icon = icon;
|
space.icon = icon;
|
||||||
|
space.selectedProducts = selectedProducts;
|
||||||
|
|
||||||
if (space.status != SpaceStatus.newSpace) {
|
if (space.status != SpaceStatus.newSpace) {
|
||||||
space.status = SpaceStatus.modified; // Mark as modified
|
space.status = SpaceStatus.modified; // Mark as modified
|
||||||
|
|||||||
@@ -104,7 +104,7 @@ class CreateSpaceDialogState extends State<CreateSpaceDialog> {
|
|||||||
width: screenWidth * 0.020,
|
width: screenWidth * 0.020,
|
||||||
height: screenWidth * 0.020,
|
height: screenWidth * 0.020,
|
||||||
decoration: const BoxDecoration(
|
decoration: const BoxDecoration(
|
||||||
color: ColorsManager.whiteColors,
|
color: Colors.white,
|
||||||
shape: BoxShape.circle,
|
shape: BoxShape.circle,
|
||||||
),
|
),
|
||||||
child: SvgPicture.asset(
|
child: SvgPicture.asset(
|
||||||
@@ -143,7 +143,7 @@ class CreateSpaceDialogState extends State<CreateSpaceDialog> {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
style: const TextStyle(color: ColorsManager.blackColor),
|
style: const TextStyle(color: Colors.black),
|
||||||
decoration: InputDecoration(
|
decoration: InputDecoration(
|
||||||
hintText: 'Please enter the name',
|
hintText: 'Please enter the name',
|
||||||
hintStyle: const TextStyle(
|
hintStyle: const TextStyle(
|
||||||
@@ -212,7 +212,7 @@ class CreateSpaceDialogState extends State<CreateSpaceDialog> {
|
|||||||
);
|
);
|
||||||
},
|
},
|
||||||
backgroundColor: ColorsManager.textFieldGreyColor,
|
backgroundColor: ColorsManager.textFieldGreyColor,
|
||||||
foregroundColor: ColorsManager.blackColor,
|
foregroundColor: Colors.black,
|
||||||
borderColor: ColorsManager.neutralGray,
|
borderColor: ColorsManager.neutralGray,
|
||||||
borderRadius: 16.0,
|
borderRadius: 16.0,
|
||||||
padding: 10.0, // Reduced padding for smaller size
|
padding: 10.0, // Reduced padding for smaller size
|
||||||
|
|||||||
@@ -40,8 +40,8 @@ void showDeleteConfirmationDialog(BuildContext context, VoidCallback onConfirm,
|
|||||||
Navigator.of(context).pop(); // Close the first dialog
|
Navigator.of(context).pop(); // Close the first dialog
|
||||||
showProcessingPopup(context, isSpace, onConfirm);
|
showProcessingPopup(context, isSpace, onConfirm);
|
||||||
},
|
},
|
||||||
style: _dialogButtonStyle(ColorsManager.spaceColor),
|
style: _dialogButtonStyle(Colors.blue),
|
||||||
child: const Text('Continue', style: TextStyle(color: ColorsManager.whiteColors)),
|
child: const Text('Continue', style: TextStyle(color: Colors.white)),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
@@ -83,7 +83,7 @@ void showProcessingPopup(BuildContext context, bool isSpace, VoidCallback onDele
|
|||||||
ElevatedButton(
|
ElevatedButton(
|
||||||
onPressed: onDelete,
|
onPressed: onDelete,
|
||||||
style: _dialogButtonStyle(ColorsManager.warningRed),
|
style: _dialogButtonStyle(ColorsManager.warningRed),
|
||||||
child: const Text('Delete', style: TextStyle(color: ColorsManager.whiteColors)),
|
child: const Text('Delete', style: TextStyle(color: Colors.white)),
|
||||||
),
|
),
|
||||||
CancelButton(
|
CancelButton(
|
||||||
label: 'Cancel',
|
label: 'Cancel',
|
||||||
@@ -108,7 +108,7 @@ Widget _buildWarningIcon() {
|
|||||||
color: ColorsManager.warningRed,
|
color: ColorsManager.warningRed,
|
||||||
shape: BoxShape.circle,
|
shape: BoxShape.circle,
|
||||||
),
|
),
|
||||||
child: const Icon(Icons.close, color: ColorsManager.whiteColors, size: 40),
|
child: const Icon(Icons.close, color: Colors.white, size: 40),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ class IconSelectionDialog extends StatelessWidget {
|
|||||||
borderRadius: BorderRadius.circular(12),
|
borderRadius: BorderRadius.circular(12),
|
||||||
boxShadow: [
|
boxShadow: [
|
||||||
BoxShadow(
|
BoxShadow(
|
||||||
color: ColorsManager.blackColor.withOpacity(0.2), // Shadow color
|
color: Colors.black.withOpacity(0.2), // Shadow color
|
||||||
blurRadius: 20, // Spread of the blur
|
blurRadius: 20, // Spread of the blur
|
||||||
offset: const Offset(0, 8), // Offset of the shadow
|
offset: const Offset(0, 8), // Offset of the shadow
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -1,22 +1,16 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_bloc/flutter_bloc.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/product_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/space_model.dart';
|
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/space_model.dart';
|
||||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/widgets/community_structure_widget.dart';
|
import 'package:syncrow_web/pages/spaces_management/all_spaces/widgets/community_structure_widget.dart';
|
||||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/widgets/gradient_canvas_border_widget.dart';
|
import 'package:syncrow_web/pages/spaces_management/all_spaces/widgets/gradient_canvas_border_widget.dart';
|
||||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/widgets/sidebar_widget.dart';
|
import 'package:syncrow_web/pages/spaces_management/all_spaces/widgets/sidebar_widget.dart';
|
||||||
import 'package:syncrow_web/pages/spaces_management/space_model/bloc/space_model_bloc.dart';
|
|
||||||
import 'package:syncrow_web/pages/spaces_management/space_model/models/space_template_model.dart';
|
|
||||||
import 'package:syncrow_web/pages/spaces_management/space_model/view/space_model_page.dart';
|
|
||||||
import 'package:syncrow_web/services/space_model_mang_api.dart';
|
|
||||||
|
|
||||||
class LoadedSpaceView extends StatefulWidget {
|
class LoadedSpaceView extends StatefulWidget {
|
||||||
final List<CommunityModel> communities;
|
final List<CommunityModel> communities;
|
||||||
final CommunityModel? selectedCommunity;
|
final CommunityModel? selectedCommunity;
|
||||||
final SpaceModel? selectedSpace;
|
final SpaceModel? selectedSpace;
|
||||||
final List<ProductModel>? products;
|
final List<ProductModel>? products;
|
||||||
final List<SpaceTemplateModel>? spaceModels;
|
|
||||||
|
|
||||||
const LoadedSpaceView({
|
const LoadedSpaceView({
|
||||||
super.key,
|
super.key,
|
||||||
@@ -24,7 +18,6 @@ class LoadedSpaceView extends StatefulWidget {
|
|||||||
this.selectedCommunity,
|
this.selectedCommunity,
|
||||||
this.selectedSpace,
|
this.selectedSpace,
|
||||||
this.products,
|
this.products,
|
||||||
this.spaceModels,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -34,9 +27,6 @@ class LoadedSpaceView extends StatefulWidget {
|
|||||||
class _LoadedStateViewState extends State<LoadedSpaceView> {
|
class _LoadedStateViewState extends State<LoadedSpaceView> {
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final bool hasSpaceModels =
|
|
||||||
widget.spaceModels != null && widget.spaceModels!.isNotEmpty;
|
|
||||||
|
|
||||||
return Stack(
|
return Stack(
|
||||||
clipBehavior: Clip.none,
|
clipBehavior: Clip.none,
|
||||||
children: [
|
children: [
|
||||||
@@ -48,25 +38,13 @@ class _LoadedStateViewState extends State<LoadedSpaceView> {
|
|||||||
widget.selectedCommunity?.uuid ??
|
widget.selectedCommunity?.uuid ??
|
||||||
'',
|
'',
|
||||||
),
|
),
|
||||||
hasSpaceModels
|
CommunityStructureArea(
|
||||||
? Expanded(
|
selectedCommunity: widget.selectedCommunity,
|
||||||
child: BlocProvider(
|
selectedSpace: widget.selectedSpace,
|
||||||
create: (context) => SpaceModelBloc(
|
spaces: widget.selectedCommunity?.spaces ?? [],
|
||||||
api: SpaceModelManagementApi(),
|
products: widget.products,
|
||||||
initialSpaceModels: widget.spaceModels ?? [],
|
communities: widget.communities,
|
||||||
),
|
),
|
||||||
child: SpaceModelPage(
|
|
||||||
products: widget.products,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
: CommunityStructureArea(
|
|
||||||
selectedCommunity: widget.selectedCommunity,
|
|
||||||
selectedSpace: widget.selectedSpace,
|
|
||||||
spaces: widget.selectedCommunity?.spaces ?? [],
|
|
||||||
products: widget.products,
|
|
||||||
communities: widget.communities,
|
|
||||||
),
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
const GradientCanvasBorderWidget(),
|
const GradientCanvasBorderWidget(),
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ class PlusButtonWidget extends StatelessWidget {
|
|||||||
color: ColorsManager.spaceColor,
|
color: ColorsManager.spaceColor,
|
||||||
shape: BoxShape.circle,
|
shape: BoxShape.circle,
|
||||||
),
|
),
|
||||||
child: const Icon(Icons.add, color: ColorsManager.whiteColors, size: 20),
|
child: const Icon(Icons.add, color: Colors.white, size: 20),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -8,8 +8,6 @@ import 'package:syncrow_web/pages/spaces_management/all_spaces/model/community_m
|
|||||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/space_model.dart';
|
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/space_model.dart';
|
||||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/widgets/community_tile.dart';
|
import 'package:syncrow_web/pages/spaces_management/all_spaces/widgets/community_tile.dart';
|
||||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/widgets/space_tile_widget.dart';
|
import 'package:syncrow_web/pages/spaces_management/all_spaces/widgets/space_tile_widget.dart';
|
||||||
import 'package:syncrow_web/pages/spaces_management/structure_selector/bloc/center_body_bloc.dart';
|
|
||||||
import 'package:syncrow_web/pages/spaces_management/structure_selector/bloc/center_body_event.dart';
|
|
||||||
import 'package:syncrow_web/utils/color_manager.dart';
|
import 'package:syncrow_web/utils/color_manager.dart';
|
||||||
import 'package:syncrow_web/utils/constants/assets.dart';
|
import 'package:syncrow_web/utils/constants/assets.dart';
|
||||||
import 'package:syncrow_web/utils/style.dart';
|
import 'package:syncrow_web/utils/style.dart';
|
||||||
@@ -120,7 +118,7 @@ class _SidebarWidgetState extends State<SidebarWidget> {
|
|||||||
children: [
|
children: [
|
||||||
Text('Communities',
|
Text('Communities',
|
||||||
style: Theme.of(context).textTheme.titleMedium?.copyWith(
|
style: Theme.of(context).textTheme.titleMedium?.copyWith(
|
||||||
color: ColorsManager.blackColor,
|
color: Colors.black,
|
||||||
)),
|
)),
|
||||||
GestureDetector(
|
GestureDetector(
|
||||||
onTap: () => _navigateToBlank(context),
|
onTap: () => _navigateToBlank(context),
|
||||||
@@ -188,8 +186,6 @@ class _SidebarWidgetState extends State<SidebarWidget> {
|
|||||||
_selectedSpaceUuid = null; // Update the selected community
|
_selectedSpaceUuid = null; // Update the selected community
|
||||||
});
|
});
|
||||||
|
|
||||||
context.read<CenterBodyBloc>().add(CommunitySelectedEvent());
|
|
||||||
|
|
||||||
context.read<SpaceManagementBloc>().add(
|
context.read<SpaceManagementBloc>().add(
|
||||||
SelectCommunityEvent(selectedCommunity: community),
|
SelectCommunityEvent(selectedCommunity: community),
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -78,7 +78,7 @@ class SpaceContainerWidget extends StatelessWidget {
|
|||||||
borderRadius: BorderRadius.circular(15),
|
borderRadius: BorderRadius.circular(15),
|
||||||
boxShadow: [
|
boxShadow: [
|
||||||
BoxShadow(
|
BoxShadow(
|
||||||
color: ColorsManager.lightGrayColor.withOpacity(0.5),
|
color: Colors.grey.withOpacity(0.5),
|
||||||
spreadRadius: 2,
|
spreadRadius: 2,
|
||||||
blurRadius: 5,
|
blurRadius: 5,
|
||||||
offset: const Offset(0, 3), // Shadow position
|
offset: const Offset(0, 3), // Shadow position
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:syncrow_web/utils/color_manager.dart';
|
|
||||||
|
|
||||||
class SpaceWidget extends StatelessWidget {
|
class SpaceWidget extends StatelessWidget {
|
||||||
final String name;
|
final String name;
|
||||||
@@ -24,11 +23,11 @@ class SpaceWidget extends StatelessWidget {
|
|||||||
Container(
|
Container(
|
||||||
padding: const EdgeInsets.all(8.0),
|
padding: const EdgeInsets.all(8.0),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: ColorsManager.whiteColors,
|
color: Colors.white,
|
||||||
borderRadius: BorderRadius.circular(8),
|
borderRadius: BorderRadius.circular(8),
|
||||||
boxShadow: [
|
boxShadow: [
|
||||||
BoxShadow(
|
BoxShadow(
|
||||||
color: ColorsManager.lightGrayColor.withOpacity(0.5),
|
color: Colors.grey.withOpacity(0.5),
|
||||||
spreadRadius: 5,
|
spreadRadius: 5,
|
||||||
blurRadius: 7,
|
blurRadius: 7,
|
||||||
offset: const Offset(0, 3),
|
offset: const Offset(0, 3),
|
||||||
@@ -37,7 +36,7 @@ class SpaceWidget extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
child: Row(
|
child: Row(
|
||||||
children: [
|
children: [
|
||||||
const Icon(Icons.location_on, color: ColorsManager.spaceColor),
|
const Icon(Icons.location_on, color: Colors.blue),
|
||||||
const SizedBox(width: 8),
|
const SizedBox(width: 8),
|
||||||
Text(name, style: const TextStyle(fontSize: 16)),
|
Text(name, style: const TextStyle(fontSize: 16)),
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -1,153 +0,0 @@
|
|||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
|
||||||
import 'package:syncrow_web/pages/spaces_management/assign_tag_models/bloc/assign_tag_model_event.dart';
|
|
||||||
import 'package:syncrow_web/pages/spaces_management/assign_tag_models/bloc/assign_tag_model_state.dart';
|
|
||||||
import 'package:syncrow_web/pages/spaces_management/space_model/models/tag_model.dart';
|
|
||||||
|
|
||||||
class AssignTagModelBloc
|
|
||||||
extends Bloc<AssignTagModelEvent, AssignTagModelState> {
|
|
||||||
AssignTagModelBloc() : super(AssignTagModelInitial()) {
|
|
||||||
on<InitializeTagModels>((event, emit) {
|
|
||||||
final initialTags = event.initialTags ?? [];
|
|
||||||
|
|
||||||
final existingTagCounts = <String, int>{};
|
|
||||||
for (var tag in initialTags) {
|
|
||||||
if (tag.product != null) {
|
|
||||||
existingTagCounts[tag.product!.uuid] =
|
|
||||||
(existingTagCounts[tag.product!.uuid] ?? 0) + 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
final allTags = <TagModel>[];
|
|
||||||
|
|
||||||
for (var selectedProduct in event.addedProducts) {
|
|
||||||
final existingCount = existingTagCounts[selectedProduct.productId] ?? 0;
|
|
||||||
|
|
||||||
if (selectedProduct.count == 0 ||
|
|
||||||
selectedProduct.count <= existingCount) {
|
|
||||||
allTags.addAll(initialTags
|
|
||||||
.where((tag) => tag.product?.uuid == selectedProduct.productId));
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
final missingCount = selectedProduct.count - existingCount;
|
|
||||||
|
|
||||||
allTags.addAll(initialTags
|
|
||||||
.where((tag) => tag.product?.uuid == selectedProduct.productId));
|
|
||||||
|
|
||||||
if (missingCount > 0) {
|
|
||||||
allTags.addAll(List.generate(
|
|
||||||
missingCount,
|
|
||||||
(index) => TagModel(
|
|
||||||
tag: '',
|
|
||||||
product: selectedProduct.product,
|
|
||||||
location: 'None',
|
|
||||||
),
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
emit(AssignTagModelLoaded(
|
|
||||||
tags: allTags,
|
|
||||||
isSaveEnabled: _validateTags(allTags),
|
|
||||||
));
|
|
||||||
});
|
|
||||||
|
|
||||||
on<UpdateTag>((event, emit) {
|
|
||||||
final currentState = state;
|
|
||||||
|
|
||||||
if (currentState is AssignTagModelLoaded &&
|
|
||||||
currentState.tags.isNotEmpty) {
|
|
||||||
final tags = List<TagModel>.from(currentState.tags);
|
|
||||||
tags[event.index].tag = event.tag;
|
|
||||||
emit(AssignTagModelLoaded(
|
|
||||||
tags: tags,
|
|
||||||
isSaveEnabled: _validateTags(tags),
|
|
||||||
errorMessage: _getValidationError(tags),
|
|
||||||
));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
on<UpdateLocation>((event, emit) {
|
|
||||||
final currentState = state;
|
|
||||||
|
|
||||||
if (currentState is AssignTagModelLoaded &&
|
|
||||||
currentState.tags.isNotEmpty) {
|
|
||||||
final tags = List<TagModel>.from(currentState.tags);
|
|
||||||
|
|
||||||
// Use copyWith for immutability
|
|
||||||
tags[event.index] =
|
|
||||||
tags[event.index].copyWith(location: event.location);
|
|
||||||
|
|
||||||
emit(AssignTagModelLoaded(
|
|
||||||
tags: tags,
|
|
||||||
isSaveEnabled: _validateTags(tags),
|
|
||||||
));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
on<ValidateTagModels>((event, emit) {
|
|
||||||
final currentState = state;
|
|
||||||
|
|
||||||
if (currentState is AssignTagModelLoaded &&
|
|
||||||
currentState.tags.isNotEmpty) {
|
|
||||||
final tags = List<TagModel>.from(currentState.tags);
|
|
||||||
|
|
||||||
emit(AssignTagModelLoaded(
|
|
||||||
tags: tags,
|
|
||||||
isSaveEnabled: _validateTags(tags),
|
|
||||||
errorMessage: _getValidationError(tags),
|
|
||||||
));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
on<DeleteTagModel>((event, emit) {
|
|
||||||
final currentState = state;
|
|
||||||
|
|
||||||
if (currentState is AssignTagModelLoaded &&
|
|
||||||
currentState.tags.isNotEmpty) {
|
|
||||||
final updatedTags = List<TagModel>.from(currentState.tags)
|
|
||||||
..remove(event.tagToDelete);
|
|
||||||
|
|
||||||
emit(AssignTagModelLoaded(
|
|
||||||
tags: updatedTags,
|
|
||||||
isSaveEnabled: _validateTags(updatedTags),
|
|
||||||
));
|
|
||||||
} else {
|
|
||||||
emit(const AssignTagModelLoaded(
|
|
||||||
tags: [],
|
|
||||||
isSaveEnabled: false,
|
|
||||||
));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
bool _validateTags(List<TagModel> tags) {
|
|
||||||
if (tags.isEmpty) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
final uniqueTags = tags.map((tag) => tag.tag?.trim() ?? '').toSet();
|
|
||||||
final hasEmptyTag = tags.any((tag) => (tag.tag?.trim() ?? '').isEmpty);
|
|
||||||
return uniqueTags.length == tags.length && !hasEmptyTag;
|
|
||||||
}
|
|
||||||
|
|
||||||
String? _getValidationError(List<TagModel> tags) {
|
|
||||||
final hasEmptyTag = tags.any((tag) => (tag.tag?.trim() ?? '').isEmpty);
|
|
||||||
if (hasEmptyTag) return 'Tags cannot be empty.';
|
|
||||||
final duplicateTags = tags
|
|
||||||
.map((tag) => tag.tag?.trim() ?? '')
|
|
||||||
.fold<Map<String, int>>({}, (map, tag) {
|
|
||||||
map[tag] = (map[tag] ?? 0) + 1;
|
|
||||||
return map;
|
|
||||||
})
|
|
||||||
.entries
|
|
||||||
.where((entry) => entry.value > 1)
|
|
||||||
.map((entry) => entry.key)
|
|
||||||
.toList();
|
|
||||||
|
|
||||||
if (duplicateTags.isNotEmpty) {
|
|
||||||
return 'Duplicate tags found: ${duplicateTags.join(', ')}';
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,55 +0,0 @@
|
|||||||
import 'package:equatable/equatable.dart';
|
|
||||||
import 'package:syncrow_web/pages/spaces_management/space_model/models/tag_model.dart';
|
|
||||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/selected_product_model.dart';
|
|
||||||
|
|
||||||
abstract class AssignTagModelEvent extends Equatable {
|
|
||||||
const AssignTagModelEvent();
|
|
||||||
|
|
||||||
@override
|
|
||||||
List<Object> get props => [];
|
|
||||||
}
|
|
||||||
|
|
||||||
class InitializeTagModels extends AssignTagModelEvent {
|
|
||||||
final List<TagModel>? initialTags;
|
|
||||||
final List<SelectedProduct> addedProducts;
|
|
||||||
|
|
||||||
const InitializeTagModels({
|
|
||||||
required this.initialTags,
|
|
||||||
required this.addedProducts,
|
|
||||||
});
|
|
||||||
|
|
||||||
@override
|
|
||||||
List<Object> get props => [initialTags ?? [], addedProducts];
|
|
||||||
}
|
|
||||||
|
|
||||||
class UpdateTag extends AssignTagModelEvent {
|
|
||||||
final int index;
|
|
||||||
final String tag;
|
|
||||||
|
|
||||||
const UpdateTag({required this.index, required this.tag});
|
|
||||||
|
|
||||||
@override
|
|
||||||
List<Object> get props => [index, tag];
|
|
||||||
}
|
|
||||||
|
|
||||||
class UpdateLocation extends AssignTagModelEvent {
|
|
||||||
final int index;
|
|
||||||
final String location;
|
|
||||||
|
|
||||||
const UpdateLocation({required this.index, required this.location});
|
|
||||||
|
|
||||||
@override
|
|
||||||
List<Object> get props => [index, location];
|
|
||||||
}
|
|
||||||
|
|
||||||
class ValidateTagModels extends AssignTagModelEvent {}
|
|
||||||
|
|
||||||
class DeleteTagModel extends AssignTagModelEvent {
|
|
||||||
final TagModel tagToDelete;
|
|
||||||
final List<TagModel> tags;
|
|
||||||
|
|
||||||
const DeleteTagModel({required this.tagToDelete, required this.tags});
|
|
||||||
|
|
||||||
@override
|
|
||||||
List<Object> get props => [tagToDelete, tags];
|
|
||||||
}
|
|
||||||
@@ -1,37 +0,0 @@
|
|||||||
import 'package:equatable/equatable.dart';
|
|
||||||
import 'package:syncrow_web/pages/spaces_management/space_model/models/tag_model.dart';
|
|
||||||
|
|
||||||
abstract class AssignTagModelState extends Equatable {
|
|
||||||
const AssignTagModelState();
|
|
||||||
|
|
||||||
@override
|
|
||||||
List<Object> get props => [];
|
|
||||||
}
|
|
||||||
|
|
||||||
class AssignTagModelInitial extends AssignTagModelState {}
|
|
||||||
|
|
||||||
class AssignTagModelLoading extends AssignTagModelState {}
|
|
||||||
|
|
||||||
class AssignTagModelLoaded extends AssignTagModelState {
|
|
||||||
final List<TagModel> tags;
|
|
||||||
final bool isSaveEnabled;
|
|
||||||
final String? errorMessage;
|
|
||||||
|
|
||||||
const AssignTagModelLoaded({
|
|
||||||
required this.tags,
|
|
||||||
required this.isSaveEnabled,
|
|
||||||
this.errorMessage,
|
|
||||||
});
|
|
||||||
|
|
||||||
@override
|
|
||||||
List<Object> get props => [tags, isSaveEnabled];
|
|
||||||
}
|
|
||||||
|
|
||||||
class AssignTagModelError extends AssignTagModelState {
|
|
||||||
final String errorMessage;
|
|
||||||
|
|
||||||
const AssignTagModelError(this.errorMessage);
|
|
||||||
|
|
||||||
@override
|
|
||||||
List<Object> get props => [errorMessage];
|
|
||||||
}
|
|
||||||
@@ -1,364 +0,0 @@
|
|||||||
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/all_spaces/model/product_model.dart';
|
|
||||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/selected_product_model.dart';
|
|
||||||
import 'package:syncrow_web/pages/spaces_management/assign_tag_models/bloc/assign_tag_model_bloc.dart';
|
|
||||||
import 'package:syncrow_web/pages/spaces_management/assign_tag_models/bloc/assign_tag_model_event.dart';
|
|
||||||
import 'package:syncrow_web/pages/spaces_management/assign_tag_models/bloc/assign_tag_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/models/subspace_template_model.dart';
|
|
||||||
import 'package:syncrow_web/pages/spaces_management/space_model/models/tag_model.dart';
|
|
||||||
import 'package:syncrow_web/pages/spaces_management/space_model/widgets/dialog/create_space_model_dialog.dart';
|
|
||||||
import 'package:syncrow_web/utils/color_manager.dart';
|
|
||||||
|
|
||||||
class AssignTagModelsDialog extends StatelessWidget {
|
|
||||||
final List<ProductModel>? products;
|
|
||||||
final List<SubspaceTemplateModel>? subspaces;
|
|
||||||
final List<TagModel>? initialTags;
|
|
||||||
final ValueChanged<List<TagModel>>? onTagsAssigned;
|
|
||||||
final List<SelectedProduct> addedProducts;
|
|
||||||
final List<String>? allTags;
|
|
||||||
final String spaceName;
|
|
||||||
final String title;
|
|
||||||
|
|
||||||
const AssignTagModelsDialog({
|
|
||||||
Key? key,
|
|
||||||
required this.products,
|
|
||||||
required this.subspaces,
|
|
||||||
required this.addedProducts,
|
|
||||||
this.initialTags,
|
|
||||||
this.onTagsAssigned,
|
|
||||||
this.allTags,
|
|
||||||
required this.spaceName,
|
|
||||||
required this.title
|
|
||||||
}) : super(key: key);
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
final List<String> locations =
|
|
||||||
(subspaces ?? []).map((subspace) => subspace.subspaceName).toList();
|
|
||||||
return BlocProvider(
|
|
||||||
create: (_) => AssignTagModelBloc()
|
|
||||||
..add(InitializeTagModels(
|
|
||||||
initialTags: initialTags,
|
|
||||||
addedProducts: addedProducts,
|
|
||||||
)),
|
|
||||||
child: BlocBuilder<AssignTagModelBloc, AssignTagModelState>(
|
|
||||||
builder: (context, state) {
|
|
||||||
if (state is AssignTagModelLoaded) {
|
|
||||||
final controllers = List.generate(
|
|
||||||
state.tags.length,
|
|
||||||
(index) => TextEditingController(text: state.tags[index].tag),
|
|
||||||
);
|
|
||||||
|
|
||||||
return AlertDialog(
|
|
||||||
title: Text(title),
|
|
||||||
backgroundColor: ColorsManager.whiteColors,
|
|
||||||
content: SingleChildScrollView(
|
|
||||||
child: Column(
|
|
||||||
children: [
|
|
||||||
ClipRRect(
|
|
||||||
borderRadius: BorderRadius.circular(20),
|
|
||||||
child: DataTable(
|
|
||||||
headingRowColor: WidgetStateProperty.all(
|
|
||||||
ColorsManager.dataHeaderGrey),
|
|
||||||
border: TableBorder.all(
|
|
||||||
color: ColorsManager.dataHeaderGrey,
|
|
||||||
width: 1,
|
|
||||||
borderRadius: BorderRadius.circular(20),
|
|
||||||
),
|
|
||||||
columns: [
|
|
||||||
DataColumn(
|
|
||||||
label: Text('#',
|
|
||||||
style:
|
|
||||||
Theme.of(context).textTheme.bodyMedium)),
|
|
||||||
DataColumn(
|
|
||||||
label: Text('Device',
|
|
||||||
style:
|
|
||||||
Theme.of(context).textTheme.bodyMedium)),
|
|
||||||
DataColumn(
|
|
||||||
label: Text('Tag',
|
|
||||||
style:
|
|
||||||
Theme.of(context).textTheme.bodyMedium)),
|
|
||||||
DataColumn(
|
|
||||||
label: Text('Location',
|
|
||||||
style:
|
|
||||||
Theme.of(context).textTheme.bodyMedium)),
|
|
||||||
],
|
|
||||||
rows: state.tags.isEmpty
|
|
||||||
? [
|
|
||||||
const DataRow(cells: [
|
|
||||||
DataCell(
|
|
||||||
Center(
|
|
||||||
child: Text(
|
|
||||||
'No Data Available',
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 14,
|
|
||||||
color: ColorsManager.lightGrayColor,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
DataCell(SizedBox()),
|
|
||||||
DataCell(SizedBox()),
|
|
||||||
DataCell(SizedBox()),
|
|
||||||
])
|
|
||||||
]
|
|
||||||
: List.generate(state.tags.length, (index) {
|
|
||||||
final tag = state.tags[index];
|
|
||||||
final controller = controllers[index];
|
|
||||||
|
|
||||||
return DataRow(
|
|
||||||
cells: [
|
|
||||||
DataCell(Text(index.toString())),
|
|
||||||
DataCell(
|
|
||||||
Row(
|
|
||||||
mainAxisAlignment:
|
|
||||||
MainAxisAlignment.spaceBetween,
|
|
||||||
children: [
|
|
||||||
Expanded(
|
|
||||||
child: Text(
|
|
||||||
tag.product?.name ?? 'Unknown',
|
|
||||||
overflow: TextOverflow.ellipsis,
|
|
||||||
)),
|
|
||||||
IconButton(
|
|
||||||
icon: const Icon(Icons.close,
|
|
||||||
color: ColorsManager.warningRed,
|
|
||||||
size: 16),
|
|
||||||
onPressed: () {
|
|
||||||
context
|
|
||||||
.read<AssignTagModelBloc>()
|
|
||||||
.add(DeleteTagModel(
|
|
||||||
tagToDelete: tag,
|
|
||||||
tags: state.tags));
|
|
||||||
},
|
|
||||||
tooltip: 'Delete Tag',
|
|
||||||
)
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
DataCell(
|
|
||||||
Row(
|
|
||||||
children: [
|
|
||||||
Expanded(
|
|
||||||
child: TextFormField(
|
|
||||||
controller: controller,
|
|
||||||
onChanged: (value) {
|
|
||||||
context
|
|
||||||
.read<AssignTagModelBloc>()
|
|
||||||
.add(UpdateTag(
|
|
||||||
index: index,
|
|
||||||
tag: value.trim(),
|
|
||||||
));
|
|
||||||
},
|
|
||||||
decoration: const InputDecoration(
|
|
||||||
hintText: 'Enter Tag',
|
|
||||||
border: InputBorder.none,
|
|
||||||
),
|
|
||||||
style: const TextStyle(
|
|
||||||
fontSize: 14,
|
|
||||||
color: ColorsManager.blackColor,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
SizedBox(
|
|
||||||
width: MediaQuery.of(context)
|
|
||||||
.size
|
|
||||||
.width *
|
|
||||||
0.15,
|
|
||||||
child: PopupMenuButton<String>(
|
|
||||||
color: ColorsManager.whiteColors,
|
|
||||||
icon: const Icon(
|
|
||||||
Icons.arrow_drop_down,
|
|
||||||
color: ColorsManager.blackColor),
|
|
||||||
onSelected: (value) {
|
|
||||||
controller.text = value;
|
|
||||||
context
|
|
||||||
.read<AssignTagModelBloc>()
|
|
||||||
.add(UpdateTag(
|
|
||||||
index: index,
|
|
||||||
tag: value,
|
|
||||||
));
|
|
||||||
},
|
|
||||||
itemBuilder: (context) {
|
|
||||||
return (allTags ?? [])
|
|
||||||
.where((tagValue) => !state
|
|
||||||
.tags
|
|
||||||
.map((e) => e.tag)
|
|
||||||
.contains(tagValue))
|
|
||||||
.map((tagValue) {
|
|
||||||
return PopupMenuItem<String>(
|
|
||||||
textStyle: const TextStyle(
|
|
||||||
color: ColorsManager
|
|
||||||
.textPrimaryColor),
|
|
||||||
value: tagValue,
|
|
||||||
child: ConstrainedBox(
|
|
||||||
constraints:
|
|
||||||
BoxConstraints(
|
|
||||||
minWidth: MediaQuery.of(
|
|
||||||
context)
|
|
||||||
.size
|
|
||||||
.width *
|
|
||||||
0.15,
|
|
||||||
maxWidth: MediaQuery.of(
|
|
||||||
context)
|
|
||||||
.size
|
|
||||||
.width *
|
|
||||||
0.15,
|
|
||||||
),
|
|
||||||
child: Text(
|
|
||||||
tagValue,
|
|
||||||
overflow: TextOverflow
|
|
||||||
.ellipsis,
|
|
||||||
),
|
|
||||||
));
|
|
||||||
}).toList();
|
|
||||||
},
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
DataCell(
|
|
||||||
DropdownButtonHideUnderline(
|
|
||||||
child: DropdownButton<String>(
|
|
||||||
value: tag.location ?? 'None',
|
|
||||||
dropdownColor: ColorsManager
|
|
||||||
.whiteColors, // Dropdown background
|
|
||||||
style: const TextStyle(
|
|
||||||
color: Colors
|
|
||||||
.black), // Style for selected text
|
|
||||||
items: [
|
|
||||||
const DropdownMenuItem<String>(
|
|
||||||
value: 'None',
|
|
||||||
child: Text(
|
|
||||||
'None',
|
|
||||||
style: TextStyle(
|
|
||||||
color: ColorsManager
|
|
||||||
.textPrimaryColor),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
...locations.map((location) {
|
|
||||||
return DropdownMenuItem<String>(
|
|
||||||
value: location,
|
|
||||||
child: Text(
|
|
||||||
location,
|
|
||||||
style: const TextStyle(
|
|
||||||
color: ColorsManager
|
|
||||||
.textPrimaryColor),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}).toList(),
|
|
||||||
],
|
|
||||||
onChanged: (value) {
|
|
||||||
if (value != null) {
|
|
||||||
context
|
|
||||||
.read<AssignTagModelBloc>()
|
|
||||||
.add(UpdateLocation(
|
|
||||||
index: index,
|
|
||||||
location: value,
|
|
||||||
));
|
|
||||||
}
|
|
||||||
},
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
}),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
if (state.errorMessage != null)
|
|
||||||
Text(
|
|
||||||
state.errorMessage!,
|
|
||||||
style: const TextStyle(color: ColorsManager.warningRed),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
actions: [
|
|
||||||
Row(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceAround,
|
|
||||||
children: [
|
|
||||||
const SizedBox(width: 10),
|
|
||||||
Expanded(
|
|
||||||
child: CancelButton(
|
|
||||||
label: 'Cancel',
|
|
||||||
onPressed: () async {
|
|
||||||
Navigator.of(context).pop();
|
|
||||||
await showDialog(
|
|
||||||
barrierDismissible: false,
|
|
||||||
context: context,
|
|
||||||
builder: (context) => CreateSpaceModelDialog(
|
|
||||||
products: products,
|
|
||||||
allTags: allTags,
|
|
||||||
spaceModel: SpaceTemplateModel(
|
|
||||||
modelName: spaceName,
|
|
||||||
subspaceModels: subspaces,
|
|
||||||
tags: initialTags),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(width: 10),
|
|
||||||
Expanded(
|
|
||||||
child: DefaultButton(
|
|
||||||
borderRadius: 10,
|
|
||||||
backgroundColor: state.isSaveEnabled
|
|
||||||
? ColorsManager.secondaryColor
|
|
||||||
: ColorsManager.grayColor,
|
|
||||||
foregroundColor: ColorsManager.whiteColors,
|
|
||||||
onPressed: state.isSaveEnabled
|
|
||||||
? () async {
|
|
||||||
Navigator.of(context).pop();
|
|
||||||
final assignedTags = <TagModel>{};
|
|
||||||
for (var tag in state.tags) {
|
|
||||||
if (tag.location == null ||
|
|
||||||
subspaces == null) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
for (var subspace in subspaces!) {
|
|
||||||
if (tag.location == subspace.subspaceName) {
|
|
||||||
subspace.tags ??= [];
|
|
||||||
subspace.tags!.add(tag);
|
|
||||||
assignedTags.add(tag);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
state.tags.removeWhere(assignedTags.contains);
|
|
||||||
await showDialog(
|
|
||||||
barrierDismissible: false,
|
|
||||||
context: context,
|
|
||||||
builder: (context) => CreateSpaceModelDialog(
|
|
||||||
products: products,
|
|
||||||
allTags: allTags,
|
|
||||||
spaceModel: SpaceTemplateModel(
|
|
||||||
modelName: spaceName,
|
|
||||||
subspaceModels: subspaces,
|
|
||||||
tags: state.tags),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
: null,
|
|
||||||
child: const Text('Save'),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(width: 10),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
} else if (state is AssignTagModelLoading) {
|
|
||||||
return const Center(child: CircularProgressIndicator());
|
|
||||||
} else {
|
|
||||||
return const Center(child: Text('Something went wrong.'));
|
|
||||||
}
|
|
||||||
},
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,85 +0,0 @@
|
|||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
|
||||||
import 'package:syncrow_web/pages/spaces_management/create_subspace_model/bloc/subspace_model_event.dart';
|
|
||||||
import 'package:syncrow_web/pages/spaces_management/create_subspace_model/bloc/subspace_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/models/subspace_template_model.dart';
|
|
||||||
import 'package:syncrow_web/utils/constants/action_enum.dart';
|
|
||||||
|
|
||||||
class SubSpaceModelBloc extends Bloc<SubSpaceModelEvent, SubSpaceModelState> {
|
|
||||||
SubSpaceModelBloc() : super(SubSpaceModelState([], [], '')) {
|
|
||||||
// Handle AddSubSpaceModel Event
|
|
||||||
on<AddSubSpaceModel>((event, emit) {
|
|
||||||
// Check for duplicate names (case-insensitive)
|
|
||||||
final existingNames =
|
|
||||||
state.subSpaces.map((e) => e.subspaceName.toLowerCase()).toSet();
|
|
||||||
|
|
||||||
if (existingNames.contains(event.subSpace.subspaceName.toLowerCase())) {
|
|
||||||
// Emit state with an error message if duplicate name exists
|
|
||||||
emit(SubSpaceModelState(
|
|
||||||
state.subSpaces,
|
|
||||||
state.updatedSubSpaceModels,
|
|
||||||
'Subspace name already exists.',
|
|
||||||
));
|
|
||||||
} else {
|
|
||||||
// Add subspace if no duplicate exists
|
|
||||||
final updatedSubSpaces =
|
|
||||||
List<SubspaceTemplateModel>.from(state.subSpaces)
|
|
||||||
..add(event.subSpace);
|
|
||||||
|
|
||||||
emit(SubSpaceModelState(
|
|
||||||
updatedSubSpaces,
|
|
||||||
state.updatedSubSpaceModels,
|
|
||||||
'', // Clear error message
|
|
||||||
));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Handle RemoveSubSpaceModel Event
|
|
||||||
on<RemoveSubSpaceModel>((event, emit) {
|
|
||||||
final updatedSubSpaces = List<SubspaceTemplateModel>.from(state.subSpaces)
|
|
||||||
..remove(event.subSpace);
|
|
||||||
|
|
||||||
final updatedSubspaceModels = List<UpdateSubspaceTemplateModel>.from(
|
|
||||||
state.updatedSubSpaceModels,
|
|
||||||
);
|
|
||||||
|
|
||||||
if (event.subSpace.uuid?.isNotEmpty ?? false) {
|
|
||||||
updatedSubspaceModels.add(UpdateSubspaceTemplateModel(
|
|
||||||
action: Action.delete,
|
|
||||||
uuid: event.subSpace.uuid!,
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
emit(SubSpaceModelState(
|
|
||||||
updatedSubSpaces,
|
|
||||||
updatedSubspaceModels,
|
|
||||||
'', // Clear error message
|
|
||||||
));
|
|
||||||
});
|
|
||||||
|
|
||||||
// Handle UpdateSubSpaceModel Event
|
|
||||||
on<UpdateSubSpaceModel>((event, emit) {
|
|
||||||
final updatedSubSpaces = state.subSpaces.map((subSpace) {
|
|
||||||
if (subSpace.uuid == event.updatedSubSpace.uuid) {
|
|
||||||
return event.updatedSubSpace;
|
|
||||||
}
|
|
||||||
return subSpace;
|
|
||||||
}).toList();
|
|
||||||
|
|
||||||
final updatedSubspaceModels = List<UpdateSubspaceTemplateModel>.from(
|
|
||||||
state.updatedSubSpaceModels,
|
|
||||||
);
|
|
||||||
|
|
||||||
updatedSubspaceModels.add(UpdateSubspaceTemplateModel(
|
|
||||||
action: Action.update,
|
|
||||||
uuid: event.updatedSubSpace.uuid!,
|
|
||||||
));
|
|
||||||
|
|
||||||
emit(SubSpaceModelState(
|
|
||||||
updatedSubSpaces,
|
|
||||||
updatedSubspaceModels,
|
|
||||||
'', // Clear error message
|
|
||||||
));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
import 'package:syncrow_web/pages/spaces_management/space_model/models/subspace_template_model.dart';
|
|
||||||
|
|
||||||
abstract class SubSpaceModelEvent {}
|
|
||||||
|
|
||||||
class AddSubSpaceModel extends SubSpaceModelEvent {
|
|
||||||
final SubspaceTemplateModel subSpace;
|
|
||||||
AddSubSpaceModel(this.subSpace);
|
|
||||||
}
|
|
||||||
|
|
||||||
class RemoveSubSpaceModel extends SubSpaceModelEvent {
|
|
||||||
final SubspaceTemplateModel subSpace;
|
|
||||||
RemoveSubSpaceModel(this.subSpace);
|
|
||||||
}
|
|
||||||
|
|
||||||
class UpdateSubSpaceModel extends SubSpaceModelEvent {
|
|
||||||
final SubspaceTemplateModel updatedSubSpace;
|
|
||||||
UpdateSubSpaceModel(this.updatedSubSpace);
|
|
||||||
}
|
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
import 'package:syncrow_web/pages/spaces_management/space_model/models/space_template_model.dart';
|
|
||||||
import 'package:syncrow_web/pages/spaces_management/space_model/models/subspace_template_model.dart';
|
|
||||||
|
|
||||||
class SubSpaceModelState {
|
|
||||||
final List<SubspaceTemplateModel> subSpaces;
|
|
||||||
final List<UpdateSubspaceTemplateModel> updatedSubSpaceModels;
|
|
||||||
final String errorMessage;
|
|
||||||
|
|
||||||
SubSpaceModelState(
|
|
||||||
this.subSpaces,
|
|
||||||
this.updatedSubSpaceModels,
|
|
||||||
this.errorMessage,
|
|
||||||
);
|
|
||||||
|
|
||||||
SubSpaceModelState copyWith({
|
|
||||||
List<SubspaceTemplateModel>? subSpaces,
|
|
||||||
List<UpdateSubspaceTemplateModel>? updatedSubSpaceModels,
|
|
||||||
String? errorMessage,
|
|
||||||
}) {
|
|
||||||
return SubSpaceModelState(
|
|
||||||
subSpaces ?? this.subSpaces,
|
|
||||||
updatedSubSpaceModels ?? this.updatedSubSpaceModels,
|
|
||||||
errorMessage ?? this.errorMessage,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
-231
@@ -1,231 +0,0 @@
|
|||||||
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/all_spaces/model/product_model.dart';
|
|
||||||
import 'package:syncrow_web/pages/spaces_management/create_subspace_model/bloc/subspace_model_bloc.dart';
|
|
||||||
import 'package:syncrow_web/pages/spaces_management/create_subspace_model/bloc/subspace_model_event.dart';
|
|
||||||
import 'package:syncrow_web/pages/spaces_management/create_subspace_model/bloc/subspace_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/models/subspace_template_model.dart';
|
|
||||||
import 'package:syncrow_web/pages/spaces_management/space_model/models/tag_model.dart';
|
|
||||||
import 'package:syncrow_web/pages/spaces_management/space_model/widgets/dialog/create_space_model_dialog.dart';
|
|
||||||
import 'package:syncrow_web/utils/color_manager.dart';
|
|
||||||
|
|
||||||
class CreateSubSpaceModelDialog extends StatelessWidget {
|
|
||||||
final bool isEdit;
|
|
||||||
final String dialogTitle;
|
|
||||||
final List<SubspaceTemplateModel>? existingSubSpaces;
|
|
||||||
final String? spaceName;
|
|
||||||
final List<TagModel>? spaceTagModels;
|
|
||||||
final List<String>? allTags;
|
|
||||||
final List<ProductModel>? products;
|
|
||||||
final SpaceTemplateModel? spaceModel;
|
|
||||||
|
|
||||||
const CreateSubSpaceModelDialog({
|
|
||||||
Key? key,
|
|
||||||
required this.isEdit,
|
|
||||||
required this.dialogTitle,
|
|
||||||
this.existingSubSpaces,
|
|
||||||
required this.allTags,
|
|
||||||
required this.spaceName,
|
|
||||||
required this.spaceTagModels,
|
|
||||||
required this.products,
|
|
||||||
required this.spaceModel,
|
|
||||||
}) : super(key: key);
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
final screenWidth = MediaQuery.of(context).size.width;
|
|
||||||
final textController = TextEditingController();
|
|
||||||
|
|
||||||
return Dialog(
|
|
||||||
shape: RoundedRectangleBorder(
|
|
||||||
borderRadius: BorderRadius.circular(20),
|
|
||||||
),
|
|
||||||
child: BlocProvider(
|
|
||||||
create: (_) {
|
|
||||||
final bloc = SubSpaceModelBloc();
|
|
||||||
if (existingSubSpaces != null) {
|
|
||||||
for (var subSpace in existingSubSpaces!) {
|
|
||||||
bloc.add(AddSubSpaceModel(subSpace));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return bloc;
|
|
||||||
},
|
|
||||||
child: BlocBuilder<SubSpaceModelBloc, SubSpaceModelState>(
|
|
||||||
builder: (context, state) {
|
|
||||||
return Container(
|
|
||||||
color: ColorsManager.whiteColors,
|
|
||||||
child: SizedBox(
|
|
||||||
width: screenWidth * 0.35,
|
|
||||||
child: Padding(
|
|
||||||
padding: const EdgeInsets.all(16.0),
|
|
||||||
child: Column(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
mainAxisSize: MainAxisSize.min,
|
|
||||||
children: [
|
|
||||||
Text(
|
|
||||||
dialogTitle,
|
|
||||||
style: Theme.of(context)
|
|
||||||
.textTheme
|
|
||||||
.headlineLarge
|
|
||||||
?.copyWith(color: ColorsManager.blackColor),
|
|
||||||
),
|
|
||||||
const SizedBox(height: 16),
|
|
||||||
Container(
|
|
||||||
width: screenWidth * 0.35,
|
|
||||||
padding: const EdgeInsets.symmetric(
|
|
||||||
vertical: 10.0, horizontal: 16.0),
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: ColorsManager.boxColor,
|
|
||||||
borderRadius: BorderRadius.circular(10),
|
|
||||||
),
|
|
||||||
child: Wrap(
|
|
||||||
spacing: 8.0,
|
|
||||||
runSpacing: 8.0,
|
|
||||||
children: [
|
|
||||||
...state.subSpaces.map(
|
|
||||||
(subSpace) => Chip(
|
|
||||||
label: Text(
|
|
||||||
subSpace.subspaceName,
|
|
||||||
style: const TextStyle(
|
|
||||||
color: ColorsManager.spaceColor),
|
|
||||||
),
|
|
||||||
backgroundColor: ColorsManager.whiteColors,
|
|
||||||
shape: RoundedRectangleBorder(
|
|
||||||
borderRadius: BorderRadius.circular(8),
|
|
||||||
side: const BorderSide(
|
|
||||||
color: ColorsManager.transparentColor,
|
|
||||||
width: 0,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
deleteIcon: Container(
|
|
||||||
width: 24,
|
|
||||||
height: 24,
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
shape: BoxShape.circle,
|
|
||||||
border: Border.all(
|
|
||||||
color: ColorsManager.lightGrayColor,
|
|
||||||
width: 1.5,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
child: const Icon(
|
|
||||||
Icons.close,
|
|
||||||
size: 16,
|
|
||||||
color: ColorsManager.lightGrayColor,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
onDeleted: () => context
|
|
||||||
.read<SubSpaceModelBloc>()
|
|
||||||
.add(RemoveSubSpaceModel(subSpace)),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
SizedBox(
|
|
||||||
width: 200,
|
|
||||||
child: TextField(
|
|
||||||
controller: textController,
|
|
||||||
decoration: InputDecoration(
|
|
||||||
border: InputBorder.none,
|
|
||||||
hintText: state.subSpaces.isEmpty
|
|
||||||
? 'Please enter the name'
|
|
||||||
: null,
|
|
||||||
hintStyle: const TextStyle(
|
|
||||||
color: ColorsManager.lightGrayColor),
|
|
||||||
),
|
|
||||||
onSubmitted: (value) {
|
|
||||||
if (value.trim().isNotEmpty) {
|
|
||||||
context.read<SubSpaceModelBloc>().add(
|
|
||||||
AddSubSpaceModel(
|
|
||||||
SubspaceTemplateModel(
|
|
||||||
subspaceName: value.trim(),
|
|
||||||
disabled: false)));
|
|
||||||
textController.clear();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
style: const TextStyle(
|
|
||||||
color: ColorsManager.blackColor),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
if (state.errorMessage.isNotEmpty)
|
|
||||||
Padding(
|
|
||||||
padding: const EdgeInsets.only(top: 8.0),
|
|
||||||
child: Text(
|
|
||||||
state.errorMessage,
|
|
||||||
style: const TextStyle(
|
|
||||||
color: ColorsManager.warningRed,
|
|
||||||
fontSize: 12,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(height: 16),
|
|
||||||
Row(
|
|
||||||
children: [
|
|
||||||
Expanded(
|
|
||||||
child: CancelButton(
|
|
||||||
label: 'Cancel',
|
|
||||||
onPressed: () async {
|
|
||||||
Navigator.of(context).pop();
|
|
||||||
await showDialog(
|
|
||||||
barrierDismissible: false,
|
|
||||||
context: context,
|
|
||||||
builder: (context) =>
|
|
||||||
CreateSpaceModelDialog(
|
|
||||||
products: products,
|
|
||||||
allTags: allTags,
|
|
||||||
spaceModel: SpaceTemplateModel(
|
|
||||||
modelName: spaceName ?? '',
|
|
||||||
subspaceModels: existingSubSpaces,
|
|
||||||
tags: spaceTagModels,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(width: 10),
|
|
||||||
Expanded(
|
|
||||||
child: DefaultButton(
|
|
||||||
onPressed: () async {
|
|
||||||
final subSpaces = context
|
|
||||||
.read<SubSpaceModelBloc>()
|
|
||||||
.state
|
|
||||||
.subSpaces;
|
|
||||||
Navigator.of(context).pop();
|
|
||||||
|
|
||||||
await showDialog(
|
|
||||||
barrierDismissible: false,
|
|
||||||
context: context,
|
|
||||||
builder: (context) =>
|
|
||||||
CreateSpaceModelDialog(
|
|
||||||
products: products,
|
|
||||||
allTags: allTags,
|
|
||||||
spaceModel: SpaceTemplateModel(
|
|
||||||
modelName: spaceName ?? '',
|
|
||||||
subspaceModels: subSpaces,
|
|
||||||
tags: spaceTagModels,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
backgroundColor: ColorsManager.secondaryColor,
|
|
||||||
borderRadius: 10,
|
|
||||||
foregroundColor: ColorsManager.whiteColors,
|
|
||||||
child: const Text('OK'),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
));
|
|
||||||
},
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,107 +0,0 @@
|
|||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
|
||||||
import 'package:syncrow_web/pages/spaces_management/space_model/bloc/create_space_model_event.dart';
|
|
||||||
import 'package:syncrow_web/pages/spaces_management/space_model/bloc/create_space_model_state.dart';
|
|
||||||
import 'package:syncrow_web/pages/spaces_management/space_model/models/create_space_template_body_model.dart';
|
|
||||||
import 'package:syncrow_web/pages/spaces_management/space_model/models/space_template_model.dart';
|
|
||||||
import 'package:syncrow_web/pages/spaces_management/space_model/models/tag_model.dart';
|
|
||||||
import 'package:syncrow_web/services/space_model_mang_api.dart';
|
|
||||||
|
|
||||||
class CreateSpaceModelBloc
|
|
||||||
extends Bloc<CreateSpaceModelEvent, CreateSpaceModelState> {
|
|
||||||
SpaceTemplateModel? _space;
|
|
||||||
|
|
||||||
final SpaceModelManagementApi _api;
|
|
||||||
|
|
||||||
CreateSpaceModelBloc(this._api) : super(CreateSpaceModelInitial()) {
|
|
||||||
on<CreateSpaceTemplate>((event, emit) async {
|
|
||||||
try {
|
|
||||||
late SpaceTemplateModel spaceTemplate = event.spaceTemplate;
|
|
||||||
|
|
||||||
final tagBodyModels =
|
|
||||||
spaceTemplate.tags?.map((tag) => tag.toTagBodyModel()).toList() ??
|
|
||||||
[];
|
|
||||||
|
|
||||||
final subspaceTemplateBodyModels =
|
|
||||||
spaceTemplate.subspaceModels?.map((subspaceModel) {
|
|
||||||
final tagsubspaceBodyModels = subspaceModel.tags
|
|
||||||
?.map((tag) => tag.toTagBodyModel())
|
|
||||||
.toList() ??
|
|
||||||
[];
|
|
||||||
return CreateSubspaceTemplateModel()
|
|
||||||
..subspaceName = subspaceModel.subspaceName
|
|
||||||
..tags = tagsubspaceBodyModels;
|
|
||||||
}).toList() ??
|
|
||||||
[];
|
|
||||||
final spaceModelBody = CreateSpaceTemplateBodyModel(
|
|
||||||
modelName: spaceTemplate.modelName,
|
|
||||||
tags: tagBodyModels,
|
|
||||||
subspaceModels: subspaceTemplateBodyModels);
|
|
||||||
|
|
||||||
final newSpaceTemplate = await _api.createSpaceModel(spaceModelBody);
|
|
||||||
spaceTemplate.uuid = newSpaceTemplate?.uuid ?? '';
|
|
||||||
|
|
||||||
if (newSpaceTemplate != null) {
|
|
||||||
emit(CreateSpaceModelLoaded(spaceTemplate));
|
|
||||||
|
|
||||||
if (event.onCreate != null) {
|
|
||||||
event.onCreate!(spaceTemplate);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
emit(CreateSpaceModelError('Error creating space model'));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
on<LoadSpaceTemplate>((event, emit) {
|
|
||||||
emit(CreateSpaceModelLoading());
|
|
||||||
Future.delayed(const Duration(seconds: 1), () {
|
|
||||||
if (_space != null) {
|
|
||||||
emit(CreateSpaceModelLoaded(_space!));
|
|
||||||
} else {
|
|
||||||
emit(CreateSpaceModelError("No space template found"));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
on<UpdateSpaceTemplate>((event, emit) {
|
|
||||||
_space = event.spaceTemplate;
|
|
||||||
emit(CreateSpaceModelLoaded(_space!));
|
|
||||||
});
|
|
||||||
|
|
||||||
on<AddSubspacesToSpaceTemplate>((event, emit) {
|
|
||||||
final currentState = state;
|
|
||||||
|
|
||||||
if (currentState is CreateSpaceModelLoaded) {
|
|
||||||
final updatedSpace = currentState.space.copyWith(
|
|
||||||
subspaceModels: [
|
|
||||||
...(_space!.subspaceModels ?? []),
|
|
||||||
...event.subspaces,
|
|
||||||
],
|
|
||||||
);
|
|
||||||
emit(CreateSpaceModelLoaded(updatedSpace));
|
|
||||||
} else {
|
|
||||||
emit(CreateSpaceModelError("Space template not initialized"));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
on<UpdateSpaceTemplateName>((event, emit) {
|
|
||||||
final currentState = state;
|
|
||||||
if (currentState is CreateSpaceModelLoaded) {
|
|
||||||
if (event.name.trim().isEmpty) {
|
|
||||||
|
|
||||||
emit(CreateSpaceModelLoaded(
|
|
||||||
currentState.space,
|
|
||||||
errorMessage: "Model name cannot be empty",
|
|
||||||
));
|
|
||||||
} else {
|
|
||||||
final updatedSpaceModel =
|
|
||||||
currentState.space.copyWith(modelName: event.name);
|
|
||||||
|
|
||||||
emit(CreateSpaceModelLoaded(updatedSpaceModel));
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
emit(CreateSpaceModelError("Space template not initialized"));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,52 +0,0 @@
|
|||||||
import 'package:equatable/equatable.dart';
|
|
||||||
import 'package:syncrow_web/pages/spaces_management/space_model/models/space_template_model.dart';
|
|
||||||
import 'package:syncrow_web/pages/spaces_management/space_model/models/subspace_template_model.dart';
|
|
||||||
|
|
||||||
abstract class CreateSpaceModelEvent extends Equatable {
|
|
||||||
const CreateSpaceModelEvent();
|
|
||||||
|
|
||||||
@override
|
|
||||||
List<Object> get props => [];
|
|
||||||
}
|
|
||||||
|
|
||||||
class LoadSpaceTemplate extends CreateSpaceModelEvent {}
|
|
||||||
|
|
||||||
class UpdateSpaceTemplate extends CreateSpaceModelEvent {
|
|
||||||
final SpaceTemplateModel spaceTemplate;
|
|
||||||
|
|
||||||
UpdateSpaceTemplate(this.spaceTemplate);
|
|
||||||
}
|
|
||||||
|
|
||||||
class CreateSpaceTemplate extends CreateSpaceModelEvent {
|
|
||||||
final SpaceTemplateModel spaceTemplate;
|
|
||||||
final Function(SpaceTemplateModel)? onCreate;
|
|
||||||
|
|
||||||
const CreateSpaceTemplate({
|
|
||||||
required this.spaceTemplate,
|
|
||||||
this.onCreate,
|
|
||||||
});
|
|
||||||
|
|
||||||
@override
|
|
||||||
List<Object> get props => [spaceTemplate];
|
|
||||||
}
|
|
||||||
|
|
||||||
class UpdateSpaceTemplateName extends CreateSpaceModelEvent {
|
|
||||||
final String name;
|
|
||||||
|
|
||||||
UpdateSpaceTemplateName({required this.name});
|
|
||||||
|
|
||||||
@override
|
|
||||||
List<Object> get props => [name];
|
|
||||||
}
|
|
||||||
|
|
||||||
class AddSubspacesToSpaceTemplate extends CreateSpaceModelEvent {
|
|
||||||
final List<SubspaceTemplateModel> subspaces;
|
|
||||||
|
|
||||||
AddSubspacesToSpaceTemplate(this.subspaces);
|
|
||||||
}
|
|
||||||
|
|
||||||
class ValidateSpaceTemplateName extends CreateSpaceModelEvent {
|
|
||||||
final String name;
|
|
||||||
|
|
||||||
ValidateSpaceTemplateName({required this.name});
|
|
||||||
}
|
|
||||||
@@ -1,29 +0,0 @@
|
|||||||
import 'package:equatable/equatable.dart';
|
|
||||||
import 'package:syncrow_web/pages/spaces_management/space_model/models/space_template_model.dart';
|
|
||||||
|
|
||||||
abstract class CreateSpaceModelState extends Equatable {
|
|
||||||
const CreateSpaceModelState();
|
|
||||||
|
|
||||||
@override
|
|
||||||
List<Object?> get props => [];
|
|
||||||
}
|
|
||||||
|
|
||||||
class CreateSpaceModelInitial extends CreateSpaceModelState {}
|
|
||||||
|
|
||||||
class CreateSpaceModelLoading extends CreateSpaceModelState {}
|
|
||||||
|
|
||||||
class CreateSpaceModelLoaded extends CreateSpaceModelState {
|
|
||||||
final SpaceTemplateModel space;
|
|
||||||
final String? errorMessage;
|
|
||||||
|
|
||||||
CreateSpaceModelLoaded(this.space, {this.errorMessage});
|
|
||||||
|
|
||||||
@override
|
|
||||||
List<Object?> get props => [space, errorMessage];
|
|
||||||
}
|
|
||||||
|
|
||||||
class CreateSpaceModelError extends CreateSpaceModelState {
|
|
||||||
final String message;
|
|
||||||
|
|
||||||
CreateSpaceModelError(this.message);
|
|
||||||
}
|
|
||||||
@@ -1,36 +0,0 @@
|
|||||||
import 'package:flutter_bloc/flutter_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/bloc/space_model_state.dart';
|
|
||||||
import 'package:syncrow_web/pages/spaces_management/space_model/models/space_template_model.dart';
|
|
||||||
import 'package:syncrow_web/services/space_model_mang_api.dart';
|
|
||||||
|
|
||||||
class SpaceModelBloc extends Bloc<SpaceModelEvent, SpaceModelState> {
|
|
||||||
final SpaceModelManagementApi api;
|
|
||||||
|
|
||||||
SpaceModelBloc({
|
|
||||||
required this.api,
|
|
||||||
required List<SpaceTemplateModel> initialSpaceModels,
|
|
||||||
}) : super(SpaceModelLoaded(spaceModels: initialSpaceModels)) {
|
|
||||||
on<CreateSpaceModel>(_onCreateSpaceModel);
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> _onCreateSpaceModel(
|
|
||||||
CreateSpaceModel event, Emitter<SpaceModelState> emit) async {
|
|
||||||
final currentState = state;
|
|
||||||
if (currentState is SpaceModelLoaded) {
|
|
||||||
try {
|
|
||||||
final newSpaceModel =
|
|
||||||
await api.getSpaceModel(event.newSpaceModel.uuid ?? '');
|
|
||||||
|
|
||||||
if (newSpaceModel != null) {
|
|
||||||
final updatedSpaceModels =
|
|
||||||
List<SpaceTemplateModel>.from(currentState.spaceModels)
|
|
||||||
..add(newSpaceModel);
|
|
||||||
emit(SpaceModelLoaded(spaceModels: updatedSpaceModels));
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
emit(SpaceModelError(message: e.toString()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
import 'package:equatable/equatable.dart';
|
|
||||||
import 'package:syncrow_web/pages/spaces_management/space_model/models/space_template_model.dart';
|
|
||||||
|
|
||||||
abstract class SpaceModelEvent extends Equatable {
|
|
||||||
@override
|
|
||||||
List<Object?> get props => [];
|
|
||||||
}
|
|
||||||
|
|
||||||
class LoadSpaceModels extends SpaceModelEvent {}
|
|
||||||
|
|
||||||
class CreateSpaceModel extends SpaceModelEvent {
|
|
||||||
final SpaceTemplateModel newSpaceModel;
|
|
||||||
|
|
||||||
CreateSpaceModel({required this.newSpaceModel});
|
|
||||||
|
|
||||||
@override
|
|
||||||
List<Object?> get props => [newSpaceModel];
|
|
||||||
}
|
|
||||||
@@ -1,29 +0,0 @@
|
|||||||
import 'package:equatable/equatable.dart';
|
|
||||||
import 'package:syncrow_web/pages/spaces_management/space_model/models/space_template_model.dart';
|
|
||||||
|
|
||||||
abstract class SpaceModelState extends Equatable {
|
|
||||||
@override
|
|
||||||
List<Object?> get props => [];
|
|
||||||
}
|
|
||||||
|
|
||||||
class SpaceModelInitial extends SpaceModelState {}
|
|
||||||
|
|
||||||
class SpaceModelLoading extends SpaceModelState {}
|
|
||||||
|
|
||||||
class SpaceModelLoaded extends SpaceModelState {
|
|
||||||
final List<SpaceTemplateModel> spaceModels;
|
|
||||||
|
|
||||||
SpaceModelLoaded({required this.spaceModels});
|
|
||||||
|
|
||||||
@override
|
|
||||||
List<Object?> get props => [spaceModels];
|
|
||||||
}
|
|
||||||
|
|
||||||
class SpaceModelError extends SpaceModelState {
|
|
||||||
final String message;
|
|
||||||
|
|
||||||
SpaceModelError({required this.message});
|
|
||||||
|
|
||||||
@override
|
|
||||||
List<Object?> get props => [message];
|
|
||||||
}
|
|
||||||
@@ -1,50 +0,0 @@
|
|||||||
class TagBodyModel {
|
|
||||||
late String uuid;
|
|
||||||
late String tag;
|
|
||||||
late final String? productUuid;
|
|
||||||
|
|
||||||
Map<String, dynamic> toJson() {
|
|
||||||
return {
|
|
||||||
'uuid': uuid,
|
|
||||||
'tag': tag,
|
|
||||||
'productUuid': productUuid,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
String toString() {
|
|
||||||
return toJson().toString();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class CreateSubspaceTemplateModel {
|
|
||||||
late String subspaceName;
|
|
||||||
late List<TagBodyModel>? tags;
|
|
||||||
|
|
||||||
Map<String, dynamic> toJson() {
|
|
||||||
return {
|
|
||||||
'subspaceName': subspaceName,
|
|
||||||
'tags': tags?.map((tag) => tag.toJson()).toList(),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class CreateSpaceTemplateBodyModel {
|
|
||||||
final String modelName;
|
|
||||||
final List<dynamic>? tags;
|
|
||||||
final List<dynamic>? subspaceModels;
|
|
||||||
|
|
||||||
CreateSpaceTemplateBodyModel({
|
|
||||||
required this.modelName,
|
|
||||||
this.tags,
|
|
||||||
this.subspaceModels,
|
|
||||||
});
|
|
||||||
|
|
||||||
Map<String, dynamic> toJson() {
|
|
||||||
return {
|
|
||||||
'modelName': modelName,
|
|
||||||
'tags': tags,
|
|
||||||
'subspaceModels': subspaceModels,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,167 +0,0 @@
|
|||||||
import 'package:equatable/equatable.dart';
|
|
||||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/product_model.dart';
|
|
||||||
import 'package:syncrow_web/pages/spaces_management/space_model/models/subspace_template_model.dart';
|
|
||||||
import 'package:syncrow_web/pages/spaces_management/space_model/models/tag_model.dart';
|
|
||||||
import 'package:syncrow_web/utils/constants/action_enum.dart';
|
|
||||||
import 'package:uuid/uuid.dart';
|
|
||||||
|
|
||||||
class SpaceTemplateModel extends Equatable {
|
|
||||||
String? uuid;
|
|
||||||
String modelName;
|
|
||||||
List<SubspaceTemplateModel>? subspaceModels;
|
|
||||||
final List<TagModel>? tags;
|
|
||||||
String internalId;
|
|
||||||
|
|
||||||
@override
|
|
||||||
List<Object?> get props => [modelName, subspaceModels];
|
|
||||||
|
|
||||||
SpaceTemplateModel({
|
|
||||||
this.uuid,
|
|
||||||
String? internalId,
|
|
||||||
required this.modelName,
|
|
||||||
this.subspaceModels,
|
|
||||||
this.tags,
|
|
||||||
}) : 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'] ?? '',
|
|
||||||
internalId: internalId,
|
|
||||||
modelName: json['modelName'] ?? '',
|
|
||||||
subspaceModels: (json['subspaceModels'] as List<dynamic>?)
|
|
||||||
?.where((e) => e is Map<String, dynamic>) // Validate type
|
|
||||||
.map((e) =>
|
|
||||||
SubspaceTemplateModel.fromJson(e as Map<String, dynamic>))
|
|
||||||
.toList() ??
|
|
||||||
[],
|
|
||||||
tags: (json['tags'] as List<dynamic>?)
|
|
||||||
?.where((item) => item is Map<String, dynamic>) // Validate type
|
|
||||||
.map((item) => TagModel.fromJson(item as Map<String, dynamic>))
|
|
||||||
.toList() ??
|
|
||||||
[],
|
|
||||||
);
|
|
||||||
}
|
|
||||||
SpaceTemplateModel copyWith({
|
|
||||||
String? uuid,
|
|
||||||
String? modelName,
|
|
||||||
List<SubspaceTemplateModel>? subspaceModels,
|
|
||||||
List<TagModel>? tags,
|
|
||||||
String? internalId,
|
|
||||||
}) {
|
|
||||||
return SpaceTemplateModel(
|
|
||||||
uuid: uuid ?? this.uuid,
|
|
||||||
modelName: modelName ?? this.modelName,
|
|
||||||
subspaceModels: subspaceModels ?? this.subspaceModels,
|
|
||||||
tags: tags ?? this.tags,
|
|
||||||
internalId: internalId ?? this.internalId,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Map<String, dynamic> toJson() {
|
|
||||||
return {
|
|
||||||
'uuid': uuid,
|
|
||||||
'modelName': modelName,
|
|
||||||
'subspaceModels': subspaceModels?.map((e) => e.toJson()).toList(),
|
|
||||||
'tags': tags?.map((e) => e.toJson()).toList(),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class UpdateSubspaceTemplateModel {
|
|
||||||
final String uuid;
|
|
||||||
final Action action;
|
|
||||||
final String? subspaceName;
|
|
||||||
final List<UpdateTagModel>? tags;
|
|
||||||
|
|
||||||
UpdateSubspaceTemplateModel({
|
|
||||||
required this.action,
|
|
||||||
required this.uuid,
|
|
||||||
this.subspaceName,
|
|
||||||
this.tags,
|
|
||||||
});
|
|
||||||
|
|
||||||
factory UpdateSubspaceTemplateModel.fromJson(Map<String, dynamic> json) {
|
|
||||||
return UpdateSubspaceTemplateModel(
|
|
||||||
action: ActionExtension.fromValue(json['action']),
|
|
||||||
uuid: json['uuid'] ?? '',
|
|
||||||
subspaceName: json['subspaceName'] ?? '',
|
|
||||||
tags: (json['tags'] as List)
|
|
||||||
.map((item) => UpdateTagModel.fromJson(item))
|
|
||||||
.toList(),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Map<String, dynamic> toJson() {
|
|
||||||
return {
|
|
||||||
'action': action.value,
|
|
||||||
'uuid': uuid,
|
|
||||||
'subspaceName': subspaceName,
|
|
||||||
'tags': tags?.map((e) => e.toJson()).toList() ?? [],
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class UpdateTagModel {
|
|
||||||
final Action action;
|
|
||||||
final String? uuid;
|
|
||||||
final String tag;
|
|
||||||
final bool disabled;
|
|
||||||
final ProductModel? product;
|
|
||||||
|
|
||||||
UpdateTagModel({
|
|
||||||
required this.action,
|
|
||||||
this.uuid,
|
|
||||||
required this.tag,
|
|
||||||
required this.disabled,
|
|
||||||
this.product,
|
|
||||||
});
|
|
||||||
|
|
||||||
factory UpdateTagModel.fromJson(Map<String, dynamic> json) {
|
|
||||||
return UpdateTagModel(
|
|
||||||
action: ActionExtension.fromValue(json['action']),
|
|
||||||
uuid: json['uuid'] ?? '',
|
|
||||||
tag: json['tag'] ?? '',
|
|
||||||
disabled: json['disabled'] ?? false,
|
|
||||||
product: json['product'] != null
|
|
||||||
? ProductModel.fromMap(json['product'])
|
|
||||||
: null,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Map<String, dynamic> toJson() {
|
|
||||||
return {
|
|
||||||
'action': action.value,
|
|
||||||
'uuid': uuid,
|
|
||||||
'tag': tag,
|
|
||||||
'disabled': disabled,
|
|
||||||
'product': product?.toMap(),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extension SpaceTemplateExtensions on SpaceTemplateModel {
|
|
||||||
List<String> listAllTagValues() {
|
|
||||||
final List<String> tagValues = [];
|
|
||||||
|
|
||||||
if (tags != null) {
|
|
||||||
tagValues.addAll(
|
|
||||||
tags!.map((tag) => tag.tag ?? '').where((tag) => tag.isNotEmpty));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (subspaceModels != null) {
|
|
||||||
for (final subspace in subspaceModels!) {
|
|
||||||
if (subspace.tags != null) {
|
|
||||||
tagValues.addAll(
|
|
||||||
subspace.tags!
|
|
||||||
.map((tag) => tag.tag ?? '')
|
|
||||||
.where((tag) => tag.isNotEmpty),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return tagValues;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,36 +0,0 @@
|
|||||||
import 'package:syncrow_web/pages/spaces_management/space_model/models/tag_model.dart';
|
|
||||||
|
|
||||||
class SubspaceTemplateModel {
|
|
||||||
final String? uuid;
|
|
||||||
String subspaceName;
|
|
||||||
final bool disabled;
|
|
||||||
List<TagModel>? tags;
|
|
||||||
|
|
||||||
SubspaceTemplateModel({
|
|
||||||
this.uuid,
|
|
||||||
required this.subspaceName,
|
|
||||||
required this.disabled,
|
|
||||||
this.tags,
|
|
||||||
});
|
|
||||||
|
|
||||||
factory SubspaceTemplateModel.fromJson(Map<String, dynamic> json) {
|
|
||||||
return SubspaceTemplateModel(
|
|
||||||
uuid: json['uuid'] ?? '',
|
|
||||||
subspaceName: json['subspaceName'] ?? '',
|
|
||||||
disabled: json['disabled'] ?? false,
|
|
||||||
tags: (json['tags'] as List<dynamic>?)
|
|
||||||
?.map((item) => TagModel.fromJson(item))
|
|
||||||
.toList() ??
|
|
||||||
[],
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Map<String, dynamic> toJson() {
|
|
||||||
return {
|
|
||||||
'uuid': uuid,
|
|
||||||
'subspaceName': subspaceName,
|
|
||||||
'disabled': disabled,
|
|
||||||
'tags': tags?.map((e) => e.toJson()).toList() ?? [],
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,61 +0,0 @@
|
|||||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/product_model.dart';
|
|
||||||
import 'package:syncrow_web/pages/spaces_management/space_model/models/create_space_template_body_model.dart';
|
|
||||||
import 'package:uuid/uuid.dart';
|
|
||||||
|
|
||||||
class TagModel {
|
|
||||||
String? uuid;
|
|
||||||
String? tag;
|
|
||||||
final ProductModel? product;
|
|
||||||
String internalId;
|
|
||||||
String? location;
|
|
||||||
|
|
||||||
TagModel(
|
|
||||||
{this.uuid,
|
|
||||||
required this.tag,
|
|
||||||
this.product,
|
|
||||||
String? internalId,
|
|
||||||
this.location})
|
|
||||||
: internalId = internalId ?? const Uuid().v4();
|
|
||||||
|
|
||||||
factory TagModel.fromJson(Map<String, dynamic> json) {
|
|
||||||
final String internalId = json['internalId'] ?? const Uuid().v4();
|
|
||||||
|
|
||||||
return TagModel(
|
|
||||||
uuid: json['uuid'] ?? '',
|
|
||||||
internalId: internalId,
|
|
||||||
tag: json['tag'] ?? '',
|
|
||||||
product: json['product'] != null
|
|
||||||
? ProductModel.fromMap(json['product'])
|
|
||||||
: null,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
TagModel copyWith({
|
|
||||||
String? tag,
|
|
||||||
ProductModel? product,
|
|
||||||
String? location,
|
|
||||||
}) {
|
|
||||||
return TagModel(
|
|
||||||
tag: tag ?? this.tag,
|
|
||||||
product: product ?? this.product,
|
|
||||||
location: location ?? this.location,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Map<String, dynamic> toJson() {
|
|
||||||
return {
|
|
||||||
'uuid': uuid,
|
|
||||||
'tag': tag,
|
|
||||||
'product': product?.toMap(),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extension TagModelExtensions on TagModel {
|
|
||||||
TagBodyModel toTagBodyModel() {
|
|
||||||
return TagBodyModel()
|
|
||||||
..uuid = uuid ?? ''
|
|
||||||
..tag = tag ?? ''
|
|
||||||
..productUuid = product?.uuid;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,117 +0,0 @@
|
|||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
|
||||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/product_model.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/bloc/space_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/add_space_model_widget.dart';
|
|
||||||
import 'package:syncrow_web/pages/spaces_management/space_model/widgets/dialog/create_space_model_dialog.dart';
|
|
||||||
import 'package:syncrow_web/pages/spaces_management/space_model/widgets/space_model_card_widget.dart';
|
|
||||||
import 'package:syncrow_web/utils/color_manager.dart';
|
|
||||||
|
|
||||||
class SpaceModelPage extends StatelessWidget {
|
|
||||||
final List<ProductModel>? products;
|
|
||||||
|
|
||||||
const SpaceModelPage({Key? key, this.products}) : super(key: key);
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return BlocBuilder<SpaceModelBloc, SpaceModelState>(
|
|
||||||
builder: (context, state) {
|
|
||||||
if (state is SpaceModelLoading) {
|
|
||||||
return const Center(child: CircularProgressIndicator());
|
|
||||||
} else if (state is SpaceModelLoaded) {
|
|
||||||
final spaceModels = state.spaceModels;
|
|
||||||
final allTagValues = _getAllTagValues(spaceModels);
|
|
||||||
|
|
||||||
return Scaffold(
|
|
||||||
backgroundColor: ColorsManager.whiteColors,
|
|
||||||
body: Padding(
|
|
||||||
padding: const EdgeInsets.fromLTRB(20.0, 16.0, 16.0, 16.0),
|
|
||||||
child: Column(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
|
||||||
children: [
|
|
||||||
Expanded(
|
|
||||||
child: GridView.builder(
|
|
||||||
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
|
|
||||||
crossAxisCount: 3,
|
|
||||||
crossAxisSpacing: 10.0,
|
|
||||||
mainAxisSpacing: 10.0,
|
|
||||||
childAspectRatio: _calculateChildAspectRatio(context),
|
|
||||||
),
|
|
||||||
itemCount: spaceModels.length + 1,
|
|
||||||
itemBuilder: (context, index) {
|
|
||||||
if (index == spaceModels.length) {
|
|
||||||
// Add Button
|
|
||||||
return GestureDetector(
|
|
||||||
onTap: () {
|
|
||||||
showDialog(
|
|
||||||
context: context,
|
|
||||||
builder: (BuildContext dialogContext) {
|
|
||||||
return CreateSpaceModelDialog(
|
|
||||||
products: products,
|
|
||||||
allTags: allTagValues,
|
|
||||||
onLoad: (newModel) {
|
|
||||||
context.read<SpaceModelBloc>().add(
|
|
||||||
CreateSpaceModel(
|
|
||||||
newSpaceModel: newModel),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
},
|
|
||||||
child: const AddSpaceModelWidget(),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
// Render existing space model
|
|
||||||
final model = spaceModels[index];
|
|
||||||
return Container(
|
|
||||||
margin: const EdgeInsets.all(8.0),
|
|
||||||
child: SpaceModelCardWidget(model: model),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
} else if (state is SpaceModelError) {
|
|
||||||
return Center(
|
|
||||||
child: Text(
|
|
||||||
'Error: ${state.message}',
|
|
||||||
style: const TextStyle(color: ColorsManager.warningRed),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return const Center(child: Text('Initializing...'));
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
double _calculateChildAspectRatio(BuildContext context) {
|
|
||||||
double screenWidth = MediaQuery.of(context).size.width;
|
|
||||||
if (screenWidth > 1600) {
|
|
||||||
return 2; // Taller cards for larger screens
|
|
||||||
}
|
|
||||||
if (screenWidth > 1200) {
|
|
||||||
return 3; // Adjusted height for medium screens
|
|
||||||
} else if (screenWidth > 800) {
|
|
||||||
return 3.5; // Adjusted height for smaller screens
|
|
||||||
} else {
|
|
||||||
return 4.0; // Default ratio for smallest screens
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
List<String> _getAllTagValues(List<SpaceTemplateModel> spaceModels) {
|
|
||||||
final List<String> allTags = [];
|
|
||||||
for (final spaceModel in spaceModels) {
|
|
||||||
if (spaceModel.tags != null) {
|
|
||||||
allTags.addAll(spaceModel.listAllTagValues());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return allTags;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,50 +0,0 @@
|
|||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:syncrow_web/utils/color_manager.dart';
|
|
||||||
|
|
||||||
class AddSpaceModelWidget extends StatelessWidget {
|
|
||||||
const AddSpaceModelWidget({Key? key}) : super(key: key);
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return Container(
|
|
||||||
margin: const EdgeInsets.all(8.0),
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: ColorsManager.whiteColors,
|
|
||||||
borderRadius: BorderRadius.circular(20),
|
|
||||||
boxShadow: const [
|
|
||||||
BoxShadow(
|
|
||||||
color: ColorsManager.semiTransparentBlackColor,
|
|
||||||
blurRadius: 15,
|
|
||||||
offset: Offset(0, 4),
|
|
||||||
spreadRadius: 0,
|
|
||||||
),
|
|
||||||
BoxShadow(
|
|
||||||
color: ColorsManager.semiTransparentBlackColor,
|
|
||||||
blurRadius: 25,
|
|
||||||
offset: Offset(0, 15),
|
|
||||||
spreadRadius: -5,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
child: Center(
|
|
||||||
child: Container(
|
|
||||||
width: 60,
|
|
||||||
height: 60, // Set a proper height here
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
shape: BoxShape.circle,
|
|
||||||
color: ColorsManager.neutralGray,
|
|
||||||
border: Border.all(
|
|
||||||
color: ColorsManager.textFieldGreyColor,
|
|
||||||
width: 2.0,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
child: const Icon(
|
|
||||||
Icons.add,
|
|
||||||
size: 40,
|
|
||||||
color: ColorsManager.spaceColor,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,53 +0,0 @@
|
|||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:syncrow_web/utils/color_manager.dart';
|
|
||||||
|
|
||||||
class ButtonContentWidget extends StatelessWidget {
|
|
||||||
final IconData icon;
|
|
||||||
final String label;
|
|
||||||
|
|
||||||
const ButtonContentWidget({
|
|
||||||
Key? key,
|
|
||||||
required this.icon,
|
|
||||||
required this.label,
|
|
||||||
}) : super(key: key);
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
final screenWidth = MediaQuery.of(context).size.width;
|
|
||||||
|
|
||||||
return SizedBox(
|
|
||||||
width: screenWidth * 0.25,
|
|
||||||
child: Container(
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: ColorsManager.textFieldGreyColor,
|
|
||||||
border: Border.all(
|
|
||||||
color: ColorsManager.neutralGray,
|
|
||||||
width: 3.0,
|
|
||||||
),
|
|
||||||
borderRadius: BorderRadius.circular(20),
|
|
||||||
),
|
|
||||||
child: Padding(
|
|
||||||
padding: const EdgeInsets.symmetric(vertical: 10.0, horizontal: 16.0),
|
|
||||||
child: Row(
|
|
||||||
children: [
|
|
||||||
Icon(
|
|
||||||
icon,
|
|
||||||
color: ColorsManager.spaceColor,
|
|
||||||
),
|
|
||||||
const SizedBox(width: 10),
|
|
||||||
Expanded(
|
|
||||||
child: Text(
|
|
||||||
label,
|
|
||||||
style: const TextStyle(
|
|
||||||
color: ColorsManager.blackColor,
|
|
||||||
fontSize: 16,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
-188
@@ -1,188 +0,0 @@
|
|||||||
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/all_spaces/model/product_model.dart';
|
|
||||||
import 'package:syncrow_web/pages/spaces_management/space_model/bloc/create_space_model_bloc.dart';
|
|
||||||
import 'package:syncrow_web/pages/spaces_management/space_model/bloc/create_space_model_event.dart';
|
|
||||||
import 'package:syncrow_web/pages/spaces_management/space_model/bloc/create_space_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/tag_chips_display_widget.dart';
|
|
||||||
|
|
||||||
import 'package:syncrow_web/pages/spaces_management/space_model/widgets/subspace_model_create_widget.dart';
|
|
||||||
import 'package:syncrow_web/services/space_model_mang_api.dart';
|
|
||||||
import 'package:syncrow_web/utils/color_manager.dart';
|
|
||||||
|
|
||||||
class CreateSpaceModelDialog extends StatelessWidget {
|
|
||||||
final List<ProductModel>? products;
|
|
||||||
final List<String>? allTags;
|
|
||||||
final SpaceTemplateModel? spaceModel;
|
|
||||||
final void Function(SpaceTemplateModel newModel)? onLoad;
|
|
||||||
|
|
||||||
const CreateSpaceModelDialog({
|
|
||||||
Key? key,
|
|
||||||
this.products,
|
|
||||||
this.allTags,
|
|
||||||
this.spaceModel,
|
|
||||||
this.onLoad,
|
|
||||||
}) : super(key: key);
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
final SpaceModelManagementApi _spaceModelApi = SpaceModelManagementApi();
|
|
||||||
|
|
||||||
final screenWidth = MediaQuery.of(context).size.width;
|
|
||||||
final TextEditingController spaceNameController = TextEditingController(
|
|
||||||
text: spaceModel?.modelName ?? '',
|
|
||||||
);
|
|
||||||
|
|
||||||
return AlertDialog(
|
|
||||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(20)),
|
|
||||||
backgroundColor: ColorsManager.whiteColors,
|
|
||||||
content: SizedBox(
|
|
||||||
width: screenWidth * 0.3,
|
|
||||||
child: BlocProvider(
|
|
||||||
create: (_) {
|
|
||||||
final bloc = CreateSpaceModelBloc(_spaceModelApi);
|
|
||||||
if (spaceModel != null) {
|
|
||||||
bloc.add(UpdateSpaceTemplate(spaceModel!));
|
|
||||||
} else {
|
|
||||||
bloc.add(UpdateSpaceTemplate(SpaceTemplateModel(
|
|
||||||
modelName: '',
|
|
||||||
subspaceModels: const [],
|
|
||||||
)));
|
|
||||||
}
|
|
||||||
|
|
||||||
spaceNameController.addListener(() {
|
|
||||||
bloc.add(UpdateSpaceTemplateName(name: spaceNameController.text));
|
|
||||||
});
|
|
||||||
|
|
||||||
return bloc;
|
|
||||||
},
|
|
||||||
child: BlocBuilder<CreateSpaceModelBloc, CreateSpaceModelState>(
|
|
||||||
builder: (context, state) {
|
|
||||||
if (state is CreateSpaceModelLoading) {
|
|
||||||
return const Center(child: CircularProgressIndicator());
|
|
||||||
} else if (state is CreateSpaceModelLoaded) {
|
|
||||||
final updatedSpaceModel = state.space;
|
|
||||||
final subspaces = updatedSpaceModel.subspaceModels ?? [];
|
|
||||||
final isNameValid = spaceNameController.text.trim().isNotEmpty;
|
|
||||||
|
|
||||||
return Column(
|
|
||||||
mainAxisSize: MainAxisSize.min,
|
|
||||||
children: [
|
|
||||||
Text(
|
|
||||||
'Create New Space Model',
|
|
||||||
style: Theme.of(context)
|
|
||||||
.textTheme
|
|
||||||
.headlineLarge
|
|
||||||
?.copyWith(color: ColorsManager.blackColor),
|
|
||||||
),
|
|
||||||
const SizedBox(height: 16),
|
|
||||||
SizedBox(
|
|
||||||
width: screenWidth * 0.25,
|
|
||||||
child: TextField(
|
|
||||||
controller: spaceNameController,
|
|
||||||
onChanged: (value) {
|
|
||||||
context
|
|
||||||
.read<CreateSpaceModelBloc>()
|
|
||||||
.add(UpdateSpaceTemplateName(name: value));
|
|
||||||
},
|
|
||||||
style: const TextStyle(color: ColorsManager.blackColor),
|
|
||||||
decoration: InputDecoration(
|
|
||||||
filled: true,
|
|
||||||
fillColor: ColorsManager.textFieldGreyColor,
|
|
||||||
hintText: 'Please enter the name',
|
|
||||||
errorText: state.errorMessage,
|
|
||||||
hintStyle: const TextStyle(
|
|
||||||
color: ColorsManager.lightGrayColor),
|
|
||||||
border: OutlineInputBorder(
|
|
||||||
borderRadius: BorderRadius.circular(10),
|
|
||||||
borderSide: BorderSide.none,
|
|
||||||
),
|
|
||||||
contentPadding: const EdgeInsets.symmetric(
|
|
||||||
vertical: 12.0,
|
|
||||||
horizontal: 16.0,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(height: 16),
|
|
||||||
SubspaceModelCreate(context,
|
|
||||||
subspaces: state.space.subspaceModels ?? [],
|
|
||||||
allTags: allTags,
|
|
||||||
products: products,
|
|
||||||
spaceModel: spaceModel,
|
|
||||||
spaceTagModels: spaceModel?.tags ?? [],
|
|
||||||
spaceNameController: spaceNameController),
|
|
||||||
const SizedBox(height: 10),
|
|
||||||
TagChipDisplay(context,
|
|
||||||
screenWidth: screenWidth,
|
|
||||||
spaceModel: updatedSpaceModel,
|
|
||||||
products: products,
|
|
||||||
subspaces: subspaces,
|
|
||||||
allTags: allTags,
|
|
||||||
spaceNameController: spaceNameController),
|
|
||||||
const SizedBox(height: 20),
|
|
||||||
SizedBox(
|
|
||||||
width: screenWidth * 0.25,
|
|
||||||
child: Row(
|
|
||||||
children: [
|
|
||||||
Expanded(
|
|
||||||
child: CancelButton(
|
|
||||||
label: 'Cancel',
|
|
||||||
onPressed: () => Navigator.of(context).pop(),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(width: 10),
|
|
||||||
Expanded(
|
|
||||||
child: DefaultButton(
|
|
||||||
onPressed: state.errorMessage == null ||
|
|
||||||
isNameValid
|
|
||||||
? () {
|
|
||||||
final updatedSpaceTemplate =
|
|
||||||
updatedSpaceModel.copyWith(
|
|
||||||
modelName:
|
|
||||||
spaceNameController.text.trim(),
|
|
||||||
);
|
|
||||||
context.read<CreateSpaceModelBloc>().add(
|
|
||||||
CreateSpaceTemplate(
|
|
||||||
spaceTemplate:
|
|
||||||
updatedSpaceTemplate,
|
|
||||||
onCreate: (newModel) {
|
|
||||||
onLoad!(newModel);
|
|
||||||
Navigator.of(context)
|
|
||||||
.pop(); // Close the dialog
|
|
||||||
},
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
: null,
|
|
||||||
backgroundColor: ColorsManager.secondaryColor,
|
|
||||||
borderRadius: 10,
|
|
||||||
foregroundColor: isNameValid
|
|
||||||
? ColorsManager.whiteColors
|
|
||||||
: ColorsManager.whiteColorsWithOpacity,
|
|
||||||
child: const Text('OK'),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
} else if (state is CreateSpaceModelError) {
|
|
||||||
return Text(
|
|
||||||
'Error: ${state.message}',
|
|
||||||
style: const TextStyle(color: ColorsManager.warningRed),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return const Center(child: Text('Initializing...'));
|
|
||||||
},
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,142 +0,0 @@
|
|||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:flutter_svg/svg.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/subspace_chip_widget.dart';
|
|
||||||
import 'package:syncrow_web/utils/color_manager.dart';
|
|
||||||
|
|
||||||
class SpaceModelCardWidget extends StatelessWidget {
|
|
||||||
final SpaceTemplateModel model;
|
|
||||||
|
|
||||||
const SpaceModelCardWidget({Key? key, required this.model}) : super(key: key);
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
final Map<String, int> productTagCount = {};
|
|
||||||
|
|
||||||
if (model.tags != null) {
|
|
||||||
for (var tag in model.tags!) {
|
|
||||||
final prodIcon = tag.product?.icon ?? 'Unknown';
|
|
||||||
productTagCount[prodIcon] = (productTagCount[prodIcon] ?? 0) + 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (model.subspaceModels != null) {
|
|
||||||
for (var subspace in model.subspaceModels!) {
|
|
||||||
if (subspace.tags != null) {
|
|
||||||
for (var tag in subspace.tags!) {
|
|
||||||
final prodIcon = tag.product?.icon ?? 'Unknown';
|
|
||||||
productTagCount[prodIcon] = (productTagCount[prodIcon] ?? 0) + 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return Container(
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: ColorsManager.whiteColors,
|
|
||||||
borderRadius: BorderRadius.circular(10),
|
|
||||||
boxShadow: [
|
|
||||||
BoxShadow(
|
|
||||||
color: ColorsManager.lightGrayColor.withOpacity(0.5),
|
|
||||||
spreadRadius: 2,
|
|
||||||
blurRadius: 5,
|
|
||||||
offset: const Offset(0, 3),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
padding: const EdgeInsets.fromLTRB(16.0, 14.0, 8.0, 8.0),
|
|
||||||
child: Column(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: [
|
|
||||||
Flexible(
|
|
||||||
child: Text(
|
|
||||||
model.modelName,
|
|
||||||
style: Theme.of(context).textTheme.headlineMedium?.copyWith(
|
|
||||||
color: ColorsManager.blackColor,
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
),
|
|
||||||
maxLines: 1,
|
|
||||||
overflow: TextOverflow.ellipsis,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(height: 10),
|
|
||||||
Expanded(
|
|
||||||
child: Row(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: [
|
|
||||||
Expanded(
|
|
||||||
child: Wrap(
|
|
||||||
spacing: 3.0,
|
|
||||||
runSpacing: 3.0,
|
|
||||||
children: [
|
|
||||||
for (var subspace in model.subspaceModels!
|
|
||||||
.take(calculateTakeCount(context)))
|
|
||||||
SubspaceChipWidget(subspace: subspace.subspaceName),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
if (productTagCount.isNotEmpty)
|
|
||||||
Container(
|
|
||||||
width: 1,
|
|
||||||
height: double.infinity,
|
|
||||||
color: ColorsManager.softGray,
|
|
||||||
margin: const EdgeInsets.symmetric(horizontal: 4.0),
|
|
||||||
),
|
|
||||||
const SizedBox(width: 7),
|
|
||||||
Expanded(
|
|
||||||
child: Wrap(
|
|
||||||
spacing: 4.0,
|
|
||||||
runSpacing: 4.0,
|
|
||||||
children: productTagCount.entries.map((entry) {
|
|
||||||
final prodType = entry.key;
|
|
||||||
final count = entry.value;
|
|
||||||
|
|
||||||
return Chip(
|
|
||||||
label: Row(
|
|
||||||
mainAxisSize: MainAxisSize.min,
|
|
||||||
children: [
|
|
||||||
SvgPicture.asset(
|
|
||||||
prodType,
|
|
||||||
width: 15,
|
|
||||||
height: 16,
|
|
||||||
),
|
|
||||||
const SizedBox(width: 4),
|
|
||||||
Text(
|
|
||||||
'x$count', // Product count
|
|
||||||
style: const TextStyle(fontSize: 12),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
backgroundColor: ColorsManager.textFieldGreyColor,
|
|
||||||
shape: RoundedRectangleBorder(
|
|
||||||
borderRadius: BorderRadius.circular(8),
|
|
||||||
side: const BorderSide(
|
|
||||||
color: ColorsManager.transparentColor,
|
|
||||||
width: 0,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}).toList(),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(height: 5),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
int calculateTakeCount(BuildContext context) {
|
|
||||||
double screenWidth = MediaQuery.of(context).size.width;
|
|
||||||
// Adjust the count based on the screen width
|
|
||||||
if (screenWidth > 1500) {
|
|
||||||
return 3; // For large screens
|
|
||||||
} else if (screenWidth > 1200) {
|
|
||||||
return 2;
|
|
||||||
} else {
|
|
||||||
return 1; // For smaller screens
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,34 +0,0 @@
|
|||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:syncrow_web/utils/color_manager.dart';
|
|
||||||
|
|
||||||
class SubspaceChipWidget extends StatelessWidget {
|
|
||||||
final String subspace;
|
|
||||||
|
|
||||||
const SubspaceChipWidget({
|
|
||||||
Key? key,
|
|
||||||
required this.subspace,
|
|
||||||
}) : super(key: key);
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return Chip(
|
|
||||||
label: Text(
|
|
||||||
subspace,
|
|
||||||
overflow: TextOverflow.ellipsis,
|
|
||||||
maxLines: 1,
|
|
||||||
),
|
|
||||||
backgroundColor: ColorsManager.textFieldGreyColor,
|
|
||||||
labelStyle: Theme.of(context)
|
|
||||||
.textTheme
|
|
||||||
.bodySmall
|
|
||||||
?.copyWith(color: ColorsManager.spaceColor),
|
|
||||||
shape: RoundedRectangleBorder(
|
|
||||||
borderRadius: BorderRadius.circular(8),
|
|
||||||
side: const BorderSide(
|
|
||||||
color: ColorsManager.transparentColor,
|
|
||||||
width: 0,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,139 +0,0 @@
|
|||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/product_model.dart';
|
|
||||||
import 'package:syncrow_web/pages/spaces_management/space_model/models/space_template_model.dart';
|
|
||||||
import 'package:syncrow_web/pages/spaces_management/space_model/models/subspace_template_model.dart';
|
|
||||||
import 'package:syncrow_web/pages/spaces_management/space_model/models/tag_model.dart';
|
|
||||||
import 'package:syncrow_web/pages/spaces_management/space_model/widgets/button_content_widget.dart';
|
|
||||||
import 'package:syncrow_web/pages/spaces_management/create_subspace_model/views/create_subspace_model_dialog.dart';
|
|
||||||
import 'package:syncrow_web/utils/color_manager.dart';
|
|
||||||
|
|
||||||
class SubspaceModelCreate extends StatelessWidget {
|
|
||||||
final List<SubspaceTemplateModel> subspaces;
|
|
||||||
final TextEditingController spaceNameController;
|
|
||||||
final List<TagModel>? spaceTagModels;
|
|
||||||
final List<String>? allTags;
|
|
||||||
final List<ProductModel>? products;
|
|
||||||
final SpaceTemplateModel? spaceModel;
|
|
||||||
|
|
||||||
const SubspaceModelCreate(
|
|
||||||
BuildContext context, {
|
|
||||||
Key? key,
|
|
||||||
required this.subspaces,
|
|
||||||
this.spaceTagModels,
|
|
||||||
required this.allTags,
|
|
||||||
required this.products,
|
|
||||||
required this.spaceModel,
|
|
||||||
required this.spaceNameController,
|
|
||||||
}) : super(key: key);
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
final screenWidth = MediaQuery.of(context).size.width;
|
|
||||||
return Container(
|
|
||||||
child: subspaces.isEmpty
|
|
||||||
? TextButton(
|
|
||||||
style: TextButton.styleFrom(
|
|
||||||
overlayColor: ColorsManager.transparentColor,
|
|
||||||
),
|
|
||||||
onPressed: () async {
|
|
||||||
Navigator.of(context).pop();
|
|
||||||
|
|
||||||
await showDialog<List<SubspaceTemplateModel>>(
|
|
||||||
barrierDismissible: false,
|
|
||||||
context: context,
|
|
||||||
builder: (BuildContext context) {
|
|
||||||
return CreateSubSpaceModelDialog(
|
|
||||||
allTags: allTags,
|
|
||||||
spaceName: spaceNameController.text,
|
|
||||||
spaceModel: spaceModel,
|
|
||||||
spaceTagModels: spaceTagModels,
|
|
||||||
products: products,
|
|
||||||
isEdit: true,
|
|
||||||
dialogTitle: subspaces.isEmpty
|
|
||||||
? 'Create Sub-space'
|
|
||||||
: 'Edit Sub-space',
|
|
||||||
existingSubSpaces: subspaces,
|
|
||||||
);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
},
|
|
||||||
child: const ButtonContentWidget(
|
|
||||||
icon: Icons.add,
|
|
||||||
label: 'Create Sub Space',
|
|
||||||
),
|
|
||||||
)
|
|
||||||
: SizedBox(
|
|
||||||
width: screenWidth * 0.25,
|
|
||||||
child: Container(
|
|
||||||
padding: const EdgeInsets.all(8.0),
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: ColorsManager.textFieldGreyColor,
|
|
||||||
borderRadius: BorderRadius.circular(15),
|
|
||||||
border: Border.all(
|
|
||||||
color: ColorsManager.textFieldGreyColor,
|
|
||||||
width: 3.0, // Border width
|
|
||||||
),
|
|
||||||
),
|
|
||||||
child: Wrap(
|
|
||||||
spacing: 8.0,
|
|
||||||
runSpacing: 8.0,
|
|
||||||
children: [
|
|
||||||
...subspaces.map(
|
|
||||||
(subspace) => Chip(
|
|
||||||
label: Text(
|
|
||||||
subspace.subspaceName,
|
|
||||||
style: const TextStyle(
|
|
||||||
color: ColorsManager.spaceColor), // Text color
|
|
||||||
),
|
|
||||||
backgroundColor: ColorsManager.whiteColors, // Chip background color
|
|
||||||
shape: RoundedRectangleBorder(
|
|
||||||
borderRadius:
|
|
||||||
BorderRadius.circular(16), // Rounded chip
|
|
||||||
side: const BorderSide(
|
|
||||||
color: ColorsManager.spaceColor), // Border color
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
GestureDetector(
|
|
||||||
onTap: () async {
|
|
||||||
Navigator.of(context).pop();
|
|
||||||
await showDialog<List<SubspaceTemplateModel>>(
|
|
||||||
barrierDismissible: false,
|
|
||||||
context: context,
|
|
||||||
builder: (BuildContext context) {
|
|
||||||
return CreateSubSpaceModelDialog(
|
|
||||||
isEdit: true,
|
|
||||||
dialogTitle: 'Edit Sub-space',
|
|
||||||
existingSubSpaces: subspaces,
|
|
||||||
allTags: allTags,
|
|
||||||
spaceName: spaceNameController.text,
|
|
||||||
spaceTagModels: spaceTagModels,
|
|
||||||
products: products,
|
|
||||||
spaceModel: spaceModel,
|
|
||||||
);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
},
|
|
||||||
child: Chip(
|
|
||||||
label: const Text(
|
|
||||||
'Edit',
|
|
||||||
style: TextStyle(
|
|
||||||
color: ColorsManager.spaceColor),
|
|
||||||
),
|
|
||||||
backgroundColor:
|
|
||||||
ColorsManager.whiteColors,
|
|
||||||
shape: RoundedRectangleBorder(
|
|
||||||
borderRadius:
|
|
||||||
BorderRadius.circular(16),
|
|
||||||
side: const BorderSide(
|
|
||||||
color: ColorsManager.spaceColor),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,192 +0,0 @@
|
|||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:flutter_svg/svg.dart';
|
|
||||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/product_model.dart';
|
|
||||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/selected_product_model.dart';
|
|
||||||
import 'package:syncrow_web/pages/spaces_management/space_model/models/space_template_model.dart';
|
|
||||||
import 'package:syncrow_web/pages/spaces_management/space_model/models/subspace_template_model.dart';
|
|
||||||
import 'package:syncrow_web/pages/spaces_management/space_model/models/tag_model.dart';
|
|
||||||
import 'package:syncrow_web/pages/spaces_management/space_model/widgets/button_content_widget.dart';
|
|
||||||
import 'package:syncrow_web/pages/spaces_management/tag_model/views/add_device_type_model_widget.dart';
|
|
||||||
import 'package:syncrow_web/utils/color_manager.dart';
|
|
||||||
|
|
||||||
class TagChipDisplay extends StatelessWidget {
|
|
||||||
final double screenWidth;
|
|
||||||
final SpaceTemplateModel? spaceModel;
|
|
||||||
final List<ProductModel>? products;
|
|
||||||
final List<SubspaceTemplateModel>? subspaces;
|
|
||||||
final List<String>? allTags;
|
|
||||||
final TextEditingController spaceNameController;
|
|
||||||
|
|
||||||
const TagChipDisplay(
|
|
||||||
BuildContext context, {
|
|
||||||
Key? key,
|
|
||||||
required this.screenWidth,
|
|
||||||
required this.spaceModel,
|
|
||||||
required this.products,
|
|
||||||
required this.subspaces,
|
|
||||||
required this.allTags,
|
|
||||||
required this.spaceNameController,
|
|
||||||
}) : super(key: key);
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return (spaceModel?.tags?.isNotEmpty == true ||
|
|
||||||
spaceModel?.subspaceModels
|
|
||||||
?.any((subspace) => subspace.tags?.isNotEmpty == true) ==
|
|
||||||
true)
|
|
||||||
? SizedBox(
|
|
||||||
width: screenWidth * 0.25,
|
|
||||||
child: Container(
|
|
||||||
padding: const EdgeInsets.all(8.0),
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: ColorsManager.textFieldGreyColor,
|
|
||||||
borderRadius: BorderRadius.circular(15),
|
|
||||||
border: Border.all(
|
|
||||||
color: ColorsManager.textFieldGreyColor,
|
|
||||||
width: 3.0, // Border width
|
|
||||||
),
|
|
||||||
),
|
|
||||||
child: Wrap(
|
|
||||||
spacing: 8.0,
|
|
||||||
runSpacing: 8.0,
|
|
||||||
children: [
|
|
||||||
// Combine tags from spaceModel and subspaces
|
|
||||||
..._groupTags([
|
|
||||||
...?spaceModel?.tags,
|
|
||||||
...?spaceModel?.subspaceModels
|
|
||||||
?.expand((subspace) => subspace.tags ?? [])
|
|
||||||
]).entries.map(
|
|
||||||
(entry) => Chip(
|
|
||||||
avatar: SizedBox(
|
|
||||||
width: 24,
|
|
||||||
height: 24,
|
|
||||||
child: SvgPicture.asset(
|
|
||||||
entry.key.icon ?? 'assets/icons/gateway.svg',
|
|
||||||
fit: BoxFit.contain,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
label: Text(
|
|
||||||
'x${entry.value}', // Show count
|
|
||||||
style: const TextStyle(
|
|
||||||
color: ColorsManager.spaceColor,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
backgroundColor: ColorsManager.whiteColors,
|
|
||||||
shape: RoundedRectangleBorder(
|
|
||||||
borderRadius: BorderRadius.circular(16),
|
|
||||||
side: const BorderSide(
|
|
||||||
color: ColorsManager.spaceColor,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
GestureDetector(
|
|
||||||
onTap: () async {
|
|
||||||
Navigator.of(context).pop();
|
|
||||||
|
|
||||||
await showDialog<bool>(
|
|
||||||
barrierDismissible: false,
|
|
||||||
context: context,
|
|
||||||
builder: (context) => AddDeviceTypeModelWidget(
|
|
||||||
products: products,
|
|
||||||
subspaces: subspaces,
|
|
||||||
allTags: allTags,
|
|
||||||
spaceName: spaceNameController.text,
|
|
||||||
spaceTagModels: spaceModel?.tags,
|
|
||||||
initialSelectedProducts:
|
|
||||||
_createInitialSelectedProducts(
|
|
||||||
spaceModel?.tags, spaceModel?.subspaceModels),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
// Edit action
|
|
||||||
},
|
|
||||||
child: Chip(
|
|
||||||
label: const Text(
|
|
||||||
'Edit',
|
|
||||||
style: TextStyle(
|
|
||||||
color: ColorsManager.spaceColor),
|
|
||||||
),
|
|
||||||
backgroundColor:
|
|
||||||
ColorsManager.whiteColors,
|
|
||||||
shape: RoundedRectangleBorder(
|
|
||||||
borderRadius: BorderRadius.circular(16),
|
|
||||||
side: const BorderSide(
|
|
||||||
color: ColorsManager.spaceColor),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
: TextButton(
|
|
||||||
onPressed: () async {
|
|
||||||
Navigator.of(context).pop();
|
|
||||||
|
|
||||||
final result = await showDialog<bool>(
|
|
||||||
barrierDismissible: false,
|
|
||||||
context: context,
|
|
||||||
builder: (context) => AddDeviceTypeModelWidget(
|
|
||||||
products: products,
|
|
||||||
subspaces: subspaces,
|
|
||||||
allTags: allTags,
|
|
||||||
spaceName: spaceNameController.text,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
if (result == true) {}
|
|
||||||
},
|
|
||||||
style: TextButton.styleFrom(
|
|
||||||
padding: EdgeInsets.zero,
|
|
||||||
),
|
|
||||||
child: const ButtonContentWidget(
|
|
||||||
icon: Icons.add,
|
|
||||||
label: 'Add Devices',
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Map<ProductModel, int> _groupTags(List<TagModel> tags) {
|
|
||||||
final Map<ProductModel, int> groupedTags = {};
|
|
||||||
for (var tag in tags) {
|
|
||||||
if (tag.product != null) {
|
|
||||||
groupedTags[tag.product!] = (groupedTags[tag.product!] ?? 0) + 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return groupedTags;
|
|
||||||
}
|
|
||||||
|
|
||||||
List<SelectedProduct> _createInitialSelectedProducts(
|
|
||||||
List<TagModel>? tags, List<SubspaceTemplateModel>? subspaces) {
|
|
||||||
final Map<ProductModel, int> productCounts = {};
|
|
||||||
|
|
||||||
if (tags != null) {
|
|
||||||
for (var tag in tags) {
|
|
||||||
if (tag.product != null) {
|
|
||||||
productCounts[tag.product!] = (productCounts[tag.product!] ?? 0) + 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (subspaces != null) {
|
|
||||||
for (var subspace in subspaces) {
|
|
||||||
if (subspace.tags != null) {
|
|
||||||
for (var tag in subspace.tags!) {
|
|
||||||
if (tag.product != null) {
|
|
||||||
productCounts[tag.product!] =
|
|
||||||
(productCounts[tag.product!] ?? 0) + 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return productCounts.entries
|
|
||||||
.map((entry) => SelectedProduct(
|
|
||||||
productId: entry.key.uuid,
|
|
||||||
count: entry.value,
|
|
||||||
productName: entry.key.name ?? 'Unnamed',
|
|
||||||
product: entry.key,
|
|
||||||
))
|
|
||||||
.toList();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,20 +0,0 @@
|
|||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
|
||||||
import 'package:syncrow_web/pages/spaces_management/structure_selector/bloc/center_body_event.dart';
|
|
||||||
import 'package:syncrow_web/pages/spaces_management/structure_selector/bloc/center_body_state.dart';
|
|
||||||
|
|
||||||
// Bloc Implementation
|
|
||||||
class CenterBodyBloc extends Bloc<CenterBodyEvent, CenterBodyState> {
|
|
||||||
CenterBodyBloc() : super(InitialState()) {
|
|
||||||
on<CommunityStructureSelectedEvent>((event, emit) {
|
|
||||||
emit(CommunityStructureState());
|
|
||||||
});
|
|
||||||
|
|
||||||
on<SpaceModelSelectedEvent>((event, emit) {
|
|
||||||
emit(SpaceModelState());
|
|
||||||
});
|
|
||||||
|
|
||||||
on<CommunitySelectedEvent>((event, emit) {
|
|
||||||
emit(CommunitySelectedState());
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
// Define Events
|
|
||||||
abstract class CenterBodyEvent {}
|
|
||||||
|
|
||||||
class CommunityStructureSelectedEvent extends CenterBodyEvent {}
|
|
||||||
|
|
||||||
class SpaceModelSelectedEvent extends CenterBodyEvent {}
|
|
||||||
|
|
||||||
class CommunitySelectedEvent extends CenterBodyEvent {}
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
abstract class CenterBodyState {}
|
|
||||||
|
|
||||||
class InitialState extends CenterBodyState {}
|
|
||||||
|
|
||||||
class CommunityStructureState extends CenterBodyState {}
|
|
||||||
|
|
||||||
class SpaceModelState extends CenterBodyState {}
|
|
||||||
|
|
||||||
class CommunitySelectedState extends CenterBodyState {}
|
|
||||||
@@ -1,81 +0,0 @@
|
|||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:flutter_bloc/flutter_bloc.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/structure_selector/bloc/center_body_event.dart';
|
|
||||||
import 'package:syncrow_web/pages/spaces_management/structure_selector/bloc/center_body_state.dart';
|
|
||||||
import '../bloc/center_body_bloc.dart';
|
|
||||||
|
|
||||||
class CenterBodyWidget extends StatelessWidget {
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return BlocBuilder<CenterBodyBloc, CenterBodyState>(
|
|
||||||
builder: (context, state) {
|
|
||||||
if (state is InitialState) {
|
|
||||||
context.read<CenterBodyBloc>().add(CommunityStructureSelectedEvent());
|
|
||||||
}
|
|
||||||
if (state is CommunityStructureState) {
|
|
||||||
context.read<SpaceManagementBloc>().add(BlankStateEvent());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (state is SpaceModelState) {
|
|
||||||
context.read<SpaceManagementBloc>().add(SpaceModelLoadEvent());
|
|
||||||
}
|
|
||||||
|
|
||||||
return Container(
|
|
||||||
child: Column(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
|
||||||
children: [
|
|
||||||
Row(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
|
||||||
children: [
|
|
||||||
GestureDetector(
|
|
||||||
onTap: () {
|
|
||||||
context.read<CenterBodyBloc>().add(CommunityStructureSelectedEvent());
|
|
||||||
},
|
|
||||||
child: Text(
|
|
||||||
'Community Structure',
|
|
||||||
style: Theme.of(context).textTheme.bodyLarge!.copyWith(
|
|
||||||
fontWeight: state is CommunityStructureState || state is CommunitySelectedState
|
|
||||||
? FontWeight.bold
|
|
||||||
: FontWeight.normal,
|
|
||||||
color: state is CommunityStructureState || state is CommunitySelectedState
|
|
||||||
? Theme.of(context).textTheme.bodyLarge!.color
|
|
||||||
: Theme.of(context)
|
|
||||||
.textTheme
|
|
||||||
.bodyLarge!
|
|
||||||
.color!
|
|
||||||
.withOpacity(0.5),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(width: 20),
|
|
||||||
GestureDetector(
|
|
||||||
onTap: () {
|
|
||||||
context.read<CenterBodyBloc>().add(SpaceModelSelectedEvent());
|
|
||||||
},
|
|
||||||
child: Text(
|
|
||||||
'Space Model',
|
|
||||||
style: Theme.of(context).textTheme.bodyLarge!.copyWith(
|
|
||||||
fontWeight: state is SpaceModelState
|
|
||||||
? FontWeight.bold
|
|
||||||
: FontWeight.normal,
|
|
||||||
color: state is SpaceModelState
|
|
||||||
? Theme.of(context).textTheme.bodyLarge!.color
|
|
||||||
: Theme.of(context)
|
|
||||||
.textTheme
|
|
||||||
.bodyLarge!
|
|
||||||
.color!
|
|
||||||
.withOpacity(0.5),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,38 +0,0 @@
|
|||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
|
||||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/selected_product_model.dart';
|
|
||||||
import 'package:syncrow_web/pages/spaces_management/tag_model/bloc/add_device_type_model_event.dart';
|
|
||||||
|
|
||||||
class AddDeviceTypeModelBloc
|
|
||||||
extends Bloc<AddDeviceTypeModelEvent, List<SelectedProduct>> {
|
|
||||||
AddDeviceTypeModelBloc(List<SelectedProduct> initialProducts)
|
|
||||||
: super(initialProducts) {
|
|
||||||
on<UpdateProductCountEvent>(_onUpdateProductCount);
|
|
||||||
}
|
|
||||||
|
|
||||||
void _onUpdateProductCount(
|
|
||||||
UpdateProductCountEvent event, Emitter<List<SelectedProduct>> emit) {
|
|
||||||
final existingProduct = state.firstWhere(
|
|
||||||
(p) => p.productId == event.productId,
|
|
||||||
orElse: () => SelectedProduct(productId: event.productId, count: 0,productName: event.productName,product: event.product ),
|
|
||||||
);
|
|
||||||
|
|
||||||
if (event.count > 0) {
|
|
||||||
if (!state.contains(existingProduct)) {
|
|
||||||
emit([
|
|
||||||
...state,
|
|
||||||
SelectedProduct(productId: event.productId, count: event.count, productName: event.productName, product: event.product)
|
|
||||||
]);
|
|
||||||
} else {
|
|
||||||
final updatedList = state.map((p) {
|
|
||||||
if (p.productId == event.productId) {
|
|
||||||
return SelectedProduct(productId: p.productId, count: event.count, productName: p.productName,product: p.product);
|
|
||||||
}
|
|
||||||
return p;
|
|
||||||
}).toList();
|
|
||||||
emit(updatedList);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
emit(state.where((p) => p.productId != event.productId).toList());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
import 'package:equatable/equatable.dart';
|
|
||||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/product_model.dart';
|
|
||||||
|
|
||||||
abstract class AddDeviceTypeModelEvent extends Equatable {
|
|
||||||
@override
|
|
||||||
List<Object> get props => [];
|
|
||||||
}
|
|
||||||
|
|
||||||
class UpdateProductCountEvent extends AddDeviceTypeModelEvent {
|
|
||||||
final String productId;
|
|
||||||
final int count;
|
|
||||||
final String productName;
|
|
||||||
final ProductModel product;
|
|
||||||
|
|
||||||
UpdateProductCountEvent({required this.productId, required this.count, required this.productName, required this.product});
|
|
||||||
|
|
||||||
@override
|
|
||||||
List<Object> get props => [productId, count];
|
|
||||||
}
|
|
||||||
@@ -1,156 +0,0 @@
|
|||||||
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/spaces_management/assign_tag_models/views/assign_tag_models_dialog.dart';
|
|
||||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/product_model.dart';
|
|
||||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/selected_product_model.dart';
|
|
||||||
import 'package:syncrow_web/pages/spaces_management/space_model/models/space_template_model.dart';
|
|
||||||
import 'package:syncrow_web/pages/spaces_management/space_model/models/subspace_template_model.dart';
|
|
||||||
import 'package:syncrow_web/pages/spaces_management/space_model/models/tag_model.dart';
|
|
||||||
import 'package:syncrow_web/pages/spaces_management/space_model/widgets/dialog/create_space_model_dialog.dart';
|
|
||||||
import 'package:syncrow_web/pages/spaces_management/tag_model/bloc/add_device_model_bloc.dart';
|
|
||||||
import 'package:syncrow_web/pages/spaces_management/tag_model/widgets/action_button_widget.dart';
|
|
||||||
import 'package:syncrow_web/pages/spaces_management/tag_model/widgets/scrollable_grid_view_widget.dart';
|
|
||||||
import 'package:syncrow_web/utils/color_manager.dart';
|
|
||||||
|
|
||||||
class AddDeviceTypeModelWidget extends StatelessWidget {
|
|
||||||
final List<ProductModel>? products;
|
|
||||||
final ValueChanged<List<SelectedProduct>>? onProductsSelected;
|
|
||||||
final List<SelectedProduct>? initialSelectedProducts;
|
|
||||||
final List<SubspaceTemplateModel>? subspaces;
|
|
||||||
final List<TagModel>? spaceTagModels;
|
|
||||||
final List<String>? allTags;
|
|
||||||
final String spaceName;
|
|
||||||
|
|
||||||
const AddDeviceTypeModelWidget(
|
|
||||||
{super.key,
|
|
||||||
this.products,
|
|
||||||
this.initialSelectedProducts,
|
|
||||||
this.onProductsSelected,
|
|
||||||
this.subspaces,
|
|
||||||
this.allTags,
|
|
||||||
this.spaceTagModels,
|
|
||||||
required this.spaceName});
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
final size = MediaQuery.of(context).size;
|
|
||||||
final crossAxisCount = size.width > 1200
|
|
||||||
? 8
|
|
||||||
: size.width > 800
|
|
||||||
? 5
|
|
||||||
: 3;
|
|
||||||
|
|
||||||
return BlocProvider(
|
|
||||||
create: (_) => AddDeviceTypeModelBloc(initialSelectedProducts ?? []),
|
|
||||||
child: Builder(
|
|
||||||
builder: (context) => AlertDialog(
|
|
||||||
title: const Text('Add Devices'),
|
|
||||||
backgroundColor: ColorsManager.whiteColors,
|
|
||||||
content: SingleChildScrollView(
|
|
||||||
child: Container(
|
|
||||||
width: size.width * 0.9,
|
|
||||||
height: size.height * 0.65,
|
|
||||||
color: ColorsManager.textFieldGreyColor,
|
|
||||||
child: Column(
|
|
||||||
children: [
|
|
||||||
const SizedBox(height: 16),
|
|
||||||
Expanded(
|
|
||||||
child: Padding(
|
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 20.0),
|
|
||||||
child: ScrollableGridViewWidget(
|
|
||||||
products: products, crossAxisCount: crossAxisCount),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
actions: [
|
|
||||||
Row(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
||||||
children: [
|
|
||||||
CancelButton(
|
|
||||||
label: 'Cancel',
|
|
||||||
onPressed: () async {
|
|
||||||
Navigator.of(context).pop();
|
|
||||||
await showDialog(
|
|
||||||
barrierDismissible: false,
|
|
||||||
context: context,
|
|
||||||
builder: (context) => CreateSpaceModelDialog(
|
|
||||||
products: products,
|
|
||||||
allTags: allTags,
|
|
||||||
spaceModel: SpaceTemplateModel(
|
|
||||||
modelName: spaceName,
|
|
||||||
subspaceModels: subspaces,
|
|
||||||
tags: spaceTagModels,
|
|
||||||
)),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
ActionButton(
|
|
||||||
label: 'Continue',
|
|
||||||
backgroundColor: ColorsManager.secondaryColor,
|
|
||||||
foregroundColor: ColorsManager.whiteColors,
|
|
||||||
onPressed: () async {
|
|
||||||
final currentState =
|
|
||||||
context.read<AddDeviceTypeModelBloc>().state;
|
|
||||||
Navigator.of(context).pop();
|
|
||||||
|
|
||||||
if (currentState.isNotEmpty) {
|
|
||||||
final initialTags = generateInitialTags(
|
|
||||||
spaceTagModels: spaceTagModels,
|
|
||||||
subspaces: subspaces,
|
|
||||||
);
|
|
||||||
|
|
||||||
final dialogTitle = initialTags.isNotEmpty
|
|
||||||
? 'Edit Device'
|
|
||||||
: 'Assign Tags';
|
|
||||||
await showDialog<bool>(
|
|
||||||
barrierDismissible: false,
|
|
||||||
context: context,
|
|
||||||
builder: (context) => AssignTagModelsDialog(
|
|
||||||
products: products,
|
|
||||||
subspaces: subspaces,
|
|
||||||
addedProducts: currentState,
|
|
||||||
allTags: allTags,
|
|
||||||
spaceName: spaceName,
|
|
||||||
initialTags: initialTags,
|
|
||||||
title: dialogTitle,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
List<TagModel> generateInitialTags({
|
|
||||||
List<TagModel>? spaceTagModels,
|
|
||||||
List<SubspaceTemplateModel>? subspaces,
|
|
||||||
}) {
|
|
||||||
final List<TagModel> initialTags = [];
|
|
||||||
|
|
||||||
if (spaceTagModels != null) {
|
|
||||||
initialTags.addAll(spaceTagModels);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (subspaces != null) {
|
|
||||||
for (var subspace in subspaces) {
|
|
||||||
if (subspace.tags != null) {
|
|
||||||
initialTags.addAll(
|
|
||||||
subspace.tags!.map(
|
|
||||||
(tag) => tag.copyWith(location: subspace.subspaceName),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return initialTags;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,30 +0,0 @@
|
|||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:syncrow_web/pages/common/buttons/default_button.dart';
|
|
||||||
|
|
||||||
class ActionButton extends StatelessWidget {
|
|
||||||
final String label;
|
|
||||||
final Color backgroundColor;
|
|
||||||
final Color foregroundColor;
|
|
||||||
final VoidCallback onPressed;
|
|
||||||
|
|
||||||
const ActionButton({
|
|
||||||
super.key,
|
|
||||||
required this.label,
|
|
||||||
required this.backgroundColor,
|
|
||||||
required this.foregroundColor,
|
|
||||||
required this.onPressed,
|
|
||||||
});
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return SizedBox(
|
|
||||||
width: 120,
|
|
||||||
child: DefaultButton(
|
|
||||||
onPressed: onPressed,
|
|
||||||
backgroundColor: backgroundColor,
|
|
||||||
foregroundColor: foregroundColor,
|
|
||||||
child: Text(label),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,36 +0,0 @@
|
|||||||
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 DeviceIconWidget extends StatelessWidget {
|
|
||||||
final String? icon;
|
|
||||||
|
|
||||||
const DeviceIconWidget({
|
|
||||||
super.key,
|
|
||||||
required this.icon,
|
|
||||||
});
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return Container(
|
|
||||||
height: 50,
|
|
||||||
width: 50,
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
shape: BoxShape.circle,
|
|
||||||
color: ColorsManager.textFieldGreyColor,
|
|
||||||
border: Border.all(
|
|
||||||
color: ColorsManager.neutralGray,
|
|
||||||
width: 2,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
child: Center(
|
|
||||||
child: SvgPicture.asset(
|
|
||||||
icon ?? Assets.sensors,
|
|
||||||
width: 30,
|
|
||||||
height: 30,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,28 +0,0 @@
|
|||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:syncrow_web/utils/color_manager.dart';
|
|
||||||
|
|
||||||
class DeviceNameWidget extends StatelessWidget {
|
|
||||||
final String? name;
|
|
||||||
|
|
||||||
const DeviceNameWidget({
|
|
||||||
super.key,
|
|
||||||
required this.name,
|
|
||||||
});
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return SizedBox(
|
|
||||||
height: 35,
|
|
||||||
child: Text(
|
|
||||||
name ?? '',
|
|
||||||
style: Theme.of(context)
|
|
||||||
.textTheme
|
|
||||||
.bodySmall
|
|
||||||
?.copyWith(color: ColorsManager.blackColor),
|
|
||||||
textAlign: TextAlign.center,
|
|
||||||
maxLines: 2,
|
|
||||||
overflow: TextOverflow.ellipsis,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,67 +0,0 @@
|
|||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
|
||||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/product_model.dart';
|
|
||||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/selected_product_model.dart';
|
|
||||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/widgets/counter_widget.dart';
|
|
||||||
import 'package:syncrow_web/pages/spaces_management/tag_model/bloc/add_device_model_bloc.dart';
|
|
||||||
import 'package:syncrow_web/pages/spaces_management/tag_model/bloc/add_device_type_model_event.dart';
|
|
||||||
import 'package:syncrow_web/pages/spaces_management/tag_model/widgets/device_icon_widget.dart';
|
|
||||||
import 'package:syncrow_web/pages/spaces_management/tag_model/widgets/device_name_widget.dart';
|
|
||||||
import 'package:syncrow_web/utils/color_manager.dart';
|
|
||||||
import 'package:syncrow_web/utils/constants/assets.dart';
|
|
||||||
|
|
||||||
class DeviceTypeTileWidget extends StatelessWidget {
|
|
||||||
final ProductModel product;
|
|
||||||
final List<SelectedProduct> productCounts;
|
|
||||||
|
|
||||||
const DeviceTypeTileWidget({
|
|
||||||
super.key,
|
|
||||||
required this.product,
|
|
||||||
required this.productCounts,
|
|
||||||
});
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
final selectedProduct = productCounts.firstWhere(
|
|
||||||
(p) => p.productId == product.uuid,
|
|
||||||
orElse: () => SelectedProduct(
|
|
||||||
productId: product.uuid,
|
|
||||||
count: 0,
|
|
||||||
productName: product.catName,
|
|
||||||
product: product),
|
|
||||||
);
|
|
||||||
|
|
||||||
return Card(
|
|
||||||
elevation: 2,
|
|
||||||
color: ColorsManager.whiteColors,
|
|
||||||
shape: RoundedRectangleBorder(
|
|
||||||
borderRadius: BorderRadius.circular(8),
|
|
||||||
),
|
|
||||||
child: Padding(
|
|
||||||
padding: const EdgeInsets.all(4.0),
|
|
||||||
child: Column(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.start,
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.center,
|
|
||||||
children: [
|
|
||||||
DeviceIconWidget(icon: product.icon ?? Assets.doorSensor),
|
|
||||||
const SizedBox(height: 4),
|
|
||||||
DeviceNameWidget(name: product.name),
|
|
||||||
const SizedBox(height: 4),
|
|
||||||
CounterWidget(
|
|
||||||
initialCount: selectedProduct.count,
|
|
||||||
onCountChanged: (newCount) {
|
|
||||||
context.read<AddDeviceTypeModelBloc>().add(
|
|
||||||
UpdateProductCountEvent(
|
|
||||||
productId: product.uuid,
|
|
||||||
count: newCount,
|
|
||||||
productName: product.catName,
|
|
||||||
product: product),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,72 +0,0 @@
|
|||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
|
||||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/product_model.dart';
|
|
||||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/selected_product_model.dart';
|
|
||||||
import 'package:syncrow_web/pages/spaces_management/tag_model/bloc/add_device_model_bloc.dart';
|
|
||||||
import 'package:syncrow_web/pages/spaces_management/tag_model/widgets/device_type_tile_widget.dart';
|
|
||||||
|
|
||||||
class ScrollableGridViewWidget extends StatelessWidget {
|
|
||||||
final List<ProductModel>? products;
|
|
||||||
final int crossAxisCount;
|
|
||||||
final List<SelectedProduct>? initialProductCounts;
|
|
||||||
|
|
||||||
const ScrollableGridViewWidget({
|
|
||||||
super.key,
|
|
||||||
required this.products,
|
|
||||||
required this.crossAxisCount,
|
|
||||||
this.initialProductCounts,
|
|
||||||
});
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
final scrollController = ScrollController();
|
|
||||||
|
|
||||||
return Scrollbar(
|
|
||||||
controller: scrollController,
|
|
||||||
thumbVisibility: true,
|
|
||||||
child: BlocBuilder<AddDeviceTypeModelBloc, List<SelectedProduct>>(
|
|
||||||
builder: (context, productCounts) {
|
|
||||||
return GridView.builder(
|
|
||||||
controller: scrollController,
|
|
||||||
shrinkWrap: true,
|
|
||||||
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
|
|
||||||
crossAxisCount: crossAxisCount,
|
|
||||||
mainAxisSpacing: 6,
|
|
||||||
crossAxisSpacing: 4,
|
|
||||||
childAspectRatio: .8,
|
|
||||||
),
|
|
||||||
itemCount: products?.length ?? 0,
|
|
||||||
itemBuilder: (context, index) {
|
|
||||||
final product = products![index];
|
|
||||||
final initialProductCount = _findInitialProductCount(product);
|
|
||||||
|
|
||||||
return DeviceTypeTileWidget(
|
|
||||||
product: product,
|
|
||||||
productCounts: initialProductCount != null
|
|
||||||
? [...productCounts, initialProductCount]
|
|
||||||
: productCounts,
|
|
||||||
);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
SelectedProduct? _findInitialProductCount(ProductModel product) {
|
|
||||||
// Check if the product exists in initialProductCounts
|
|
||||||
if (initialProductCounts == null) return null;
|
|
||||||
final matchingProduct = initialProductCounts!.firstWhere(
|
|
||||||
(selectedProduct) => selectedProduct.productId == product.uuid,
|
|
||||||
orElse: () => SelectedProduct(
|
|
||||||
productId: '',
|
|
||||||
count: 0,
|
|
||||||
productName: '',
|
|
||||||
product: null,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
// Check if the product was actually found
|
|
||||||
return matchingProduct.productId.isNotEmpty ? matchingProduct : null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -12,4 +12,33 @@ class HomeApi {
|
|||||||
});
|
});
|
||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future fetchTerms() async {
|
||||||
|
final response = await HTTPService().get(
|
||||||
|
path: ApiEndpoints.terms,
|
||||||
|
showServerMessage: true,
|
||||||
|
expectedResponseModel: (json) {
|
||||||
|
return json['data'];
|
||||||
|
});
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
|
||||||
|
Future fetchPolicy() async {
|
||||||
|
final response = await HTTPService().get(
|
||||||
|
path: ApiEndpoints.policy,
|
||||||
|
showServerMessage: true,
|
||||||
|
expectedResponseModel: (json) {
|
||||||
|
return json['data'];
|
||||||
|
});
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
|
||||||
|
Future confirmUserAgreements(uuid) async {
|
||||||
|
final response = await HTTPService().patch(
|
||||||
|
path: ApiEndpoints.userAgreements.replaceAll('{userUuid}', uuid!),
|
||||||
|
expectedResponseModel: (json) {
|
||||||
|
return json['data'];
|
||||||
|
});
|
||||||
|
return response;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.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/selected_product_model.dart';
|
||||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/space_model.dart';
|
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/space_model.dart';
|
||||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/space_response_model.dart';
|
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/space_response_model.dart';
|
||||||
import 'package:syncrow_web/services/api/http_service.dart';
|
import 'package:syncrow_web/services/api/http_service.dart';
|
||||||
@@ -166,6 +167,7 @@ class CommunitySpaceManagementApi {
|
|||||||
bool isPrivate = false,
|
bool isPrivate = false,
|
||||||
required Offset position,
|
required Offset position,
|
||||||
String? icon,
|
String? icon,
|
||||||
|
required List<SelectedProduct> products,
|
||||||
}) async {
|
}) async {
|
||||||
try {
|
try {
|
||||||
final body = {
|
final body = {
|
||||||
@@ -175,6 +177,7 @@ class CommunitySpaceManagementApi {
|
|||||||
'y': position.dy,
|
'y': position.dy,
|
||||||
'direction': direction,
|
'direction': direction,
|
||||||
'icon': icon,
|
'icon': icon,
|
||||||
|
'products': products.map((product) => product.toJson()).toList(),
|
||||||
};
|
};
|
||||||
if (parentId != null) {
|
if (parentId != null) {
|
||||||
body['parentUuid'] = parentId;
|
body['parentUuid'] = parentId;
|
||||||
@@ -204,6 +207,7 @@ class CommunitySpaceManagementApi {
|
|||||||
String? direction,
|
String? direction,
|
||||||
bool isPrivate = false,
|
bool isPrivate = false,
|
||||||
required Offset position,
|
required Offset position,
|
||||||
|
required List<SelectedProduct> products,
|
||||||
}) async {
|
}) async {
|
||||||
try {
|
try {
|
||||||
final body = {
|
final body = {
|
||||||
@@ -213,6 +217,7 @@ class CommunitySpaceManagementApi {
|
|||||||
'y': position.dy,
|
'y': position.dy,
|
||||||
'direction': direction,
|
'direction': direction,
|
||||||
'icon': icon,
|
'icon': icon,
|
||||||
|
'products': products.map((product) => product.toJson()).toList(),
|
||||||
};
|
};
|
||||||
if (parentId != null) {
|
if (parentId != null) {
|
||||||
body['parentUuid'] = parentId;
|
body['parentUuid'] = parentId;
|
||||||
|
|||||||
@@ -1,77 +0,0 @@
|
|||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:syncrow_web/pages/spaces_management/space_model/models/create_space_template_body_model.dart';
|
|
||||||
import 'package:syncrow_web/pages/spaces_management/space_model/models/space_template_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 SpaceModelManagementApi {
|
|
||||||
Future<List<SpaceTemplateModel>> listSpaceModels({int page = 1}) async {
|
|
||||||
try {
|
|
||||||
List<SpaceTemplateModel> spaceModels = [];
|
|
||||||
bool hasNext = true;
|
|
||||||
while (hasNext) {
|
|
||||||
await HTTPService().get(
|
|
||||||
path: ApiEndpoints.listSpaceModels
|
|
||||||
.replaceAll('{projectId}', TempConst.projectId),
|
|
||||||
queryParameters: {'page': page},
|
|
||||||
expectedResponseModel: (json) {
|
|
||||||
List<dynamic> jsonData = json['data'];
|
|
||||||
hasNext = json['hasNext'] ?? false;
|
|
||||||
int currentPage = json['page'] ?? 1;
|
|
||||||
List<SpaceTemplateModel> spaceModelList = jsonData.map((jsonItem) {
|
|
||||||
return SpaceTemplateModel.fromJson(jsonItem);
|
|
||||||
}).toList();
|
|
||||||
|
|
||||||
spaceModels.addAll(spaceModelList);
|
|
||||||
page = currentPage + 1;
|
|
||||||
return spaceModelList;
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return spaceModels;
|
|
||||||
} catch (e) {
|
|
||||||
debugPrint('Error fetching space models: $e');
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<SpaceTemplateModel?> createSpaceModel(
|
|
||||||
CreateSpaceTemplateBodyModel spaceModel) async {
|
|
||||||
try {
|
|
||||||
final response = await HTTPService().post(
|
|
||||||
path: ApiEndpoints.createSpaceModel
|
|
||||||
.replaceAll('{projectId}', TempConst.projectId),
|
|
||||||
showServerMessage: true,
|
|
||||||
body: spaceModel.toJson(),
|
|
||||||
expectedResponseModel: (json) {
|
|
||||||
return SpaceTemplateModel.fromJson(json['data']);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
return response;
|
|
||||||
} catch (e) {
|
|
||||||
debugPrint('Error creating space model: $e');
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<SpaceTemplateModel?> getSpaceModel(String spaceModelUuid) async {
|
|
||||||
try {
|
|
||||||
final response = await HTTPService().get(
|
|
||||||
path: ApiEndpoints.getSpaceModel
|
|
||||||
.replaceAll('{projectId}', TempConst.projectId)
|
|
||||||
.replaceAll('{spaceModelUuid}', spaceModelUuid),
|
|
||||||
showServerMessage: true,
|
|
||||||
expectedResponseModel: (json) {
|
|
||||||
debugPrint('Response JSON: $json');
|
|
||||||
|
|
||||||
return SpaceTemplateModel.fromJson(json['data']);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
return response;
|
|
||||||
} catch (e) {
|
|
||||||
debugPrint('Error getting space model: $e');
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -71,8 +71,8 @@ class UserPermissionApi {
|
|||||||
"firstName": firstName,
|
"firstName": firstName,
|
||||||
"lastName": lastName,
|
"lastName": lastName,
|
||||||
"email": email,
|
"email": email,
|
||||||
"jobTitle": jobTitle != '' ? jobTitle : " ",
|
"jobTitle": jobTitle != '' ? jobTitle : null,
|
||||||
"phoneNumber": phoneNumber != '' ? phoneNumber : " ",
|
"phoneNumber": phoneNumber != '' ? phoneNumber : null,
|
||||||
"roleUuid": roleUuid,
|
"roleUuid": roleUuid,
|
||||||
"projectUuid": "0e62577c-06fa-41b9-8a92-99a21fbaf51c",
|
"projectUuid": "0e62577c-06fa-41b9-8a92-99a21fbaf51c",
|
||||||
"spaceUuids": spaceUuids,
|
"spaceUuids": spaceUuids,
|
||||||
@@ -119,13 +119,8 @@ class UserPermissionApi {
|
|||||||
);
|
);
|
||||||
return response ?? 'Unknown error occurred';
|
return response ?? 'Unknown error occurred';
|
||||||
} on DioException catch (e) {
|
} on DioException catch (e) {
|
||||||
if (e.response != null) {
|
final errorMessage = e.response?.data['error'];
|
||||||
final errorMessage = e.response?.data['error'];
|
return errorMessage;
|
||||||
return errorMessage is String
|
|
||||||
? errorMessage
|
|
||||||
: 'Error occurred while checking email';
|
|
||||||
}
|
|
||||||
return 'Error occurred while checking email';
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
return e.toString();
|
return e.toString();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,8 +8,6 @@ abstract class ColorsManager {
|
|||||||
static Color primaryColorWithOpacity =
|
static Color primaryColorWithOpacity =
|
||||||
const Color(0xFF023DFE).withOpacity(0.6);
|
const Color(0xFF023DFE).withOpacity(0.6);
|
||||||
static const Color whiteColors = Colors.white;
|
static const Color whiteColors = Colors.white;
|
||||||
static Color whiteColorsWithOpacity = Colors.white.withOpacity(0.6);
|
|
||||||
|
|
||||||
static const Color secondaryColor = Color(0xFF023DFE);
|
static const Color secondaryColor = Color(0xFF023DFE);
|
||||||
static const Color onSecondaryColor = Color(0xFF023DFE);
|
static const Color onSecondaryColor = Color(0xFF023DFE);
|
||||||
static Color shadowBlackColor = Colors.black.withOpacity(0.2);
|
static Color shadowBlackColor = Colors.black.withOpacity(0.2);
|
||||||
@@ -56,10 +54,6 @@ abstract class ColorsManager {
|
|||||||
static const Color neutralGray = Color(0xFFE5E5E5);
|
static const Color neutralGray = Color(0xFFE5E5E5);
|
||||||
static const Color warningRed = Color(0xFFFF6465);
|
static const Color warningRed = Color(0xFFFF6465);
|
||||||
static const Color borderColor = Color(0xFFE5E5E5);
|
static const Color borderColor = Color(0xFFE5E5E5);
|
||||||
static const Color CircleImageBackground = Color(0xFFF4F4F4);
|
|
||||||
static const Color softGray = Color(0xFFD5D5D5);
|
|
||||||
static const Color semiTransparentBlack = Color(0x19000000);
|
|
||||||
static const Color dataHeaderGrey = Color(0x33999999);
|
|
||||||
static const Color circleImageBackground = Color(0xFFF4F4F4);
|
static const Color circleImageBackground = Color(0xFFF4F4F4);
|
||||||
static const Color circleRolesBackground = Color(0xFFF8F8F8);
|
static const Color circleRolesBackground = Color(0xFFF8F8F8);
|
||||||
static const Color activeGreen = Color(0xFF99FF93);
|
static const Color activeGreen = Color(0xFF99FF93);
|
||||||
|
|||||||
@@ -1,31 +0,0 @@
|
|||||||
enum Action {
|
|
||||||
update,
|
|
||||||
add,
|
|
||||||
delete,
|
|
||||||
}
|
|
||||||
|
|
||||||
extension ActionExtension on Action {
|
|
||||||
String get value {
|
|
||||||
switch (this) {
|
|
||||||
case Action.update:
|
|
||||||
return 'update';
|
|
||||||
case Action.add:
|
|
||||||
return 'add';
|
|
||||||
case Action.delete:
|
|
||||||
return 'delete';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static Action fromValue(String value) {
|
|
||||||
switch (value) {
|
|
||||||
case 'update':
|
|
||||||
return Action.update;
|
|
||||||
case 'add':
|
|
||||||
return Action.add;
|
|
||||||
case 'delete':
|
|
||||||
return Action.delete;
|
|
||||||
default:
|
|
||||||
throw ArgumentError('Invalid action: $value');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -97,12 +97,6 @@ abstract class ApiEndpoints {
|
|||||||
static const String updateScene = '/scene/tap-to-run/{sceneId}';
|
static const String updateScene = '/scene/tap-to-run/{sceneId}';
|
||||||
|
|
||||||
static const String updateAutomation = '/automation/{automationId}';
|
static const String updateAutomation = '/automation/{automationId}';
|
||||||
|
|
||||||
//space model
|
|
||||||
static const String listSpaceModels = '/projects/{projectId}/space-models';
|
|
||||||
static const String createSpaceModel = '/projects/{projectId}/space-models';
|
|
||||||
static const String getSpaceModel = '/projects/{projectId}/space-models/{spaceModelUuid}';
|
|
||||||
|
|
||||||
static const String roleTypes = '/role/types';
|
static const String roleTypes = '/role/types';
|
||||||
static const String permission = '/permission/{roleUuid}';
|
static const String permission = '/permission/{roleUuid}';
|
||||||
static const String inviteUser = '/invite-user';
|
static const String inviteUser = '/invite-user';
|
||||||
@@ -114,6 +108,7 @@ abstract class ApiEndpoints {
|
|||||||
static const String deleteUser = '/invite-user/{inviteUserUuid}';
|
static const String deleteUser = '/invite-user/{inviteUserUuid}';
|
||||||
static const String changeUserStatus =
|
static const String changeUserStatus =
|
||||||
'/invite-user/{invitedUserUuid}/disable';
|
'/invite-user/{invitedUserUuid}/disable';
|
||||||
|
static const String terms = '/terms';
|
||||||
// static const String updateAutomation = '/automation/{automationId}';
|
static const String policy = '/policy';
|
||||||
|
static const String userAgreements = '/user/agreements/web/{userUuid}';
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,9 +7,13 @@
|
|||||||
#include "generated_plugin_registrant.h"
|
#include "generated_plugin_registrant.h"
|
||||||
|
|
||||||
#include <flutter_secure_storage_linux/flutter_secure_storage_linux_plugin.h>
|
#include <flutter_secure_storage_linux/flutter_secure_storage_linux_plugin.h>
|
||||||
|
#include <url_launcher_linux/url_launcher_plugin.h>
|
||||||
|
|
||||||
void fl_register_plugins(FlPluginRegistry* registry) {
|
void fl_register_plugins(FlPluginRegistry* registry) {
|
||||||
g_autoptr(FlPluginRegistrar) flutter_secure_storage_linux_registrar =
|
g_autoptr(FlPluginRegistrar) flutter_secure_storage_linux_registrar =
|
||||||
fl_plugin_registry_get_registrar_for_plugin(registry, "FlutterSecureStorageLinuxPlugin");
|
fl_plugin_registry_get_registrar_for_plugin(registry, "FlutterSecureStorageLinuxPlugin");
|
||||||
flutter_secure_storage_linux_plugin_register_with_registrar(flutter_secure_storage_linux_registrar);
|
flutter_secure_storage_linux_plugin_register_with_registrar(flutter_secure_storage_linux_registrar);
|
||||||
|
g_autoptr(FlPluginRegistrar) url_launcher_linux_registrar =
|
||||||
|
fl_plugin_registry_get_registrar_for_plugin(registry, "UrlLauncherPlugin");
|
||||||
|
url_launcher_plugin_register_with_registrar(url_launcher_linux_registrar);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
|
|
||||||
list(APPEND FLUTTER_PLUGIN_LIST
|
list(APPEND FLUTTER_PLUGIN_LIST
|
||||||
flutter_secure_storage_linux
|
flutter_secure_storage_linux
|
||||||
|
url_launcher_linux
|
||||||
)
|
)
|
||||||
|
|
||||||
list(APPEND FLUTTER_FFI_PLUGIN_LIST
|
list(APPEND FLUTTER_FFI_PLUGIN_LIST
|
||||||
|
|||||||
@@ -8,9 +8,11 @@ import Foundation
|
|||||||
import flutter_secure_storage_macos
|
import flutter_secure_storage_macos
|
||||||
import path_provider_foundation
|
import path_provider_foundation
|
||||||
import shared_preferences_foundation
|
import shared_preferences_foundation
|
||||||
|
import url_launcher_macos
|
||||||
|
|
||||||
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
|
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
|
||||||
FlutterSecureStoragePlugin.register(with: registry.registrar(forPlugin: "FlutterSecureStoragePlugin"))
|
FlutterSecureStoragePlugin.register(with: registry.registrar(forPlugin: "FlutterSecureStoragePlugin"))
|
||||||
PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin"))
|
PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin"))
|
||||||
SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin"))
|
SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin"))
|
||||||
|
UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin"))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -54,6 +54,8 @@ dependencies:
|
|||||||
time_picker_spinner: ^1.0.0
|
time_picker_spinner: ^1.0.0
|
||||||
intl_phone_field: ^3.2.0
|
intl_phone_field: ^3.2.0
|
||||||
number_pagination: ^1.1.6
|
number_pagination: ^1.1.6
|
||||||
|
url_launcher: ^6.2.5
|
||||||
|
flutter_html: ^3.0.0-beta.2
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_test:
|
flutter_test:
|
||||||
|
|||||||
@@ -7,8 +7,11 @@
|
|||||||
#include "generated_plugin_registrant.h"
|
#include "generated_plugin_registrant.h"
|
||||||
|
|
||||||
#include <flutter_secure_storage_windows/flutter_secure_storage_windows_plugin.h>
|
#include <flutter_secure_storage_windows/flutter_secure_storage_windows_plugin.h>
|
||||||
|
#include <url_launcher_windows/url_launcher_windows.h>
|
||||||
|
|
||||||
void RegisterPlugins(flutter::PluginRegistry* registry) {
|
void RegisterPlugins(flutter::PluginRegistry* registry) {
|
||||||
FlutterSecureStorageWindowsPluginRegisterWithRegistrar(
|
FlutterSecureStorageWindowsPluginRegisterWithRegistrar(
|
||||||
registry->GetRegistrarForPlugin("FlutterSecureStorageWindowsPlugin"));
|
registry->GetRegistrarForPlugin("FlutterSecureStorageWindowsPlugin"));
|
||||||
|
UrlLauncherWindowsRegisterWithRegistrar(
|
||||||
|
registry->GetRegistrarForPlugin("UrlLauncherWindows"));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
|
|
||||||
list(APPEND FLUTTER_PLUGIN_LIST
|
list(APPEND FLUTTER_PLUGIN_LIST
|
||||||
flutter_secure_storage_windows
|
flutter_secure_storage_windows
|
||||||
|
url_launcher_windows
|
||||||
)
|
)
|
||||||
|
|
||||||
list(APPEND FLUTTER_FFI_PLUGIN_LIST
|
list(APPEND FLUTTER_FFI_PLUGIN_LIST
|
||||||
|
|||||||
Reference in New Issue
Block a user