Compare commits

...
Author SHA1 Message Date
Faris Armoush ce253b2034 Remove unused parameters from CreateSubSpaceDialog constructor 2025-04-17 11:27:37 +03:00
Faris Armoush 024fbcdb83 Refactor CreateSubSpaceDialog to convert to StatefulWidget and manage text controller lifecycle 2025-04-17 11:25:39 +03:00
Faris Armoush 2a77483f46 Refactor CreateSubSpaceDialog to improve widget structure and readability 2025-04-17 09:45:14 +03:00
Faris Armoush a6fc99443b Refactor CreateSubSpaceDialog layout for improved readability and maintainability 2025-04-17 09:38:54 +03:00
Faris Armoush ae95d06482 Fix constructor parameter order in CreateSubSpaceDialog 2025-04-17 09:35:47 +03:00
Faris Armoush 18c886753d added trailing commas wherever neccessary in CreateSubSpaceDialog. 2025-04-17 09:35:33 +03:00
Faris Armoush 62bf4f2944 Refactor CreateSubSpaceDialog to use context extension for screen width calculations 2025-04-17 09:34:03 +03:00
Faris Armoush 726c173a76 SP-1433-FE-Text-Alignment-Issue-in-UI-Component-in-adding-subspace-in-a-space 2025-04-17 09:33:10 +03:00
mohammadnemer1 d538b3667e Merge pull request #146 from SyncrowIOT/Fix-Factory-Reset-Model
Refactor FactoryResetModel and MainDoorSensorBatchView
2025-04-17 09:18:43 +03:00
mohammad 72ae3b1727 Refactor FactoryResetModel and MainDoorSensorBatchView
- Refactor FactoryResetModel to include 'operationType' in toJson and toMap methods.
- Refactor MainDoorSensorBatchView to use BlocProvider and Builder for better state management.
2025-04-16 15:06:50 +03:00
7 changed files with 210 additions and 228 deletions
@@ -19,6 +19,7 @@ class FactoryResetModel {
Map<String, dynamic> toJson() { Map<String, dynamic> toJson() {
return { return {
'devicesUuid': devicesUuid, 'devicesUuid': devicesUuid,
'operationType': operationType,
}; };
} }
@@ -33,6 +34,7 @@ class FactoryResetModel {
Map<String, dynamic> toMap() { Map<String, dynamic> toMap() {
return { return {
'devicesUuid': devicesUuid, 'devicesUuid': devicesUuid,
'operationType': operationType,
}; };
} }
@@ -56,3 +58,4 @@ class FactoryResetModel {
@override @override
int get hashCode => devicesUuid.hashCode; int get hashCode => devicesUuid.hashCode;
} }
@@ -4,7 +4,6 @@ import 'package:syncrow_web/pages/device_managment/all_devices/models/factory_re
import 'package:syncrow_web/pages/device_managment/main_door_sensor/bloc/main_door_sensor_bloc.dart'; import 'package:syncrow_web/pages/device_managment/main_door_sensor/bloc/main_door_sensor_bloc.dart';
import 'package:syncrow_web/pages/device_managment/main_door_sensor/bloc/main_door_sensor_event.dart'; import 'package:syncrow_web/pages/device_managment/main_door_sensor/bloc/main_door_sensor_event.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';
class MainDoorSensorBatchView extends StatelessWidget { class MainDoorSensorBatchView extends StatelessWidget {
const MainDoorSensorBatchView({super.key, required this.devicesIds}); const MainDoorSensorBatchView({super.key, required this.devicesIds});
@@ -13,26 +12,19 @@ class MainDoorSensorBatchView extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return BlocProvider(
create: (context) => MainDoorSensorBloc(),
child: Builder(
builder: (innerContext) {
return Row( return Row(
mainAxisAlignment: MainAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center,
children: [ children: [
// SizedBox(
// width: 170,
// height: 140,
// child: FirmwareUpdateWidget(
// deviceId: devicesIds.first,
// version: 12,
// ),
// ),
// const SizedBox(
// width: 12,
// ),
SizedBox( SizedBox(
width: 170, width: 170,
height: 140, height: 140,
child: FactoryResetWidget( child: FactoryResetWidget(
callFactoryReset: () { callFactoryReset: () {
BlocProvider.of<MainDoorSensorBloc>(context).add( BlocProvider.of<MainDoorSensorBloc>(innerContext).add(
MainDoorSensorFactoryReset( MainDoorSensorFactoryReset(
deviceId: devicesIds.first, deviceId: devicesIds.first,
factoryReset: FactoryResetModel(devicesUuid: devicesIds), factoryReset: FactoryResetModel(devicesUuid: devicesIds),
@@ -43,5 +35,8 @@ class MainDoorSensorBatchView extends StatelessWidget {
), ),
], ],
); );
},
),
);
} }
} }
@@ -592,8 +592,6 @@ class CreateSpaceDialogState extends State<CreateSpaceDialog> {
return CreateSubSpaceDialog( return CreateSubSpaceDialog(
spaceName: name, spaceName: name,
dialogTitle: isEdit ? 'Edit Sub-space' : 'Create Sub-space', dialogTitle: isEdit ? 'Edit Sub-space' : 'Create Sub-space',
spaceTags: spaceTags,
isEdit: isEdit,
products: products, products: products,
existingSubSpaces: existingSubSpaces, existingSubSpaces: existingSubSpaces,
onSave: (slectedSubspaces) { onSave: (slectedSubspaces) {
@@ -4,169 +4,155 @@ 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/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/product_model.dart';
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/subspace_model.dart'; import 'package:syncrow_web/pages/spaces_management/all_spaces/model/subspace_model.dart';
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/tag.dart';
import 'package:syncrow_web/pages/spaces_management/create_subspace/bloc/subspace_bloc.dart'; import 'package:syncrow_web/pages/spaces_management/create_subspace/bloc/subspace_bloc.dart';
import 'package:syncrow_web/pages/spaces_management/create_subspace/bloc/subspace_event.dart'; import 'package:syncrow_web/pages/spaces_management/create_subspace/bloc/subspace_event.dart';
import 'package:syncrow_web/pages/spaces_management/create_subspace/bloc/subspace_state.dart'; import 'package:syncrow_web/pages/spaces_management/create_subspace/bloc/subspace_state.dart';
import 'package:syncrow_web/pages/spaces_management/create_subspace_model/widgets/subspace_chip.dart';
import 'package:syncrow_web/pages/spaces_management/space_model/models/subspace_template_model.dart';
import 'package:syncrow_web/utils/color_manager.dart'; import 'package:syncrow_web/utils/color_manager.dart';
import 'package:syncrow_web/utils/extension/build_context_x.dart';
class CreateSubSpaceDialog extends StatelessWidget { class CreateSubSpaceDialog extends StatefulWidget {
final bool isEdit;
final String dialogTitle; final String dialogTitle;
final List<SubspaceModel>? existingSubSpaces; final List<SubspaceModel>? existingSubSpaces;
final String? spaceName; final String? spaceName;
final List<Tag>? spaceTags;
final List<ProductModel>? products; final List<ProductModel>? products;
final Function(List<SubspaceModel>?)? onSave; final void Function(List<SubspaceModel>?)? onSave;
const CreateSubSpaceDialog( const CreateSubSpaceDialog({
{Key? key,
required this.isEdit,
required this.dialogTitle, required this.dialogTitle,
this.existingSubSpaces,
required this.spaceName, required this.spaceName,
required this.spaceTags,
required this.products, required this.products,
required this.onSave}) required this.onSave,
: super(key: key); this.existingSubSpaces,
super.key,
});
@override
State<CreateSubSpaceDialog> createState() => _CreateSubSpaceDialogState();
}
class _CreateSubSpaceDialogState extends State<CreateSubSpaceDialog> {
late final TextEditingController _subspaceNameController;
@override
void initState() {
_subspaceNameController = TextEditingController();
super.initState();
}
@override
void dispose() {
_subspaceNameController.dispose();
super.dispose();
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final screenWidth = MediaQuery.of(context).size.width; return BlocProvider(
final textController = TextEditingController();
return Dialog(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(20),
),
child: BlocProvider(
create: (_) { create: (_) {
final bloc = SubSpaceBloc(); final bloc = SubSpaceBloc();
if (existingSubSpaces != null) { if (widget.existingSubSpaces != null) {
for (var subSpace in existingSubSpaces!) { for (final subSpace in widget.existingSubSpaces ?? []) {
bloc.add(AddSubSpace(subSpace)); bloc.add(AddSubSpace(subSpace));
} }
} }
return bloc; return bloc;
}, },
child: Dialog(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(20),
),
child: BlocBuilder<SubSpaceBloc, SubSpaceState>( child: BlocBuilder<SubSpaceBloc, SubSpaceState>(
builder: (context, state) { builder: (context, state) {
return Container( return Container(
width: context.screenWidth * 0.35,
color: ColorsManager.whiteColors, color: ColorsManager.whiteColors,
child: SizedBox( padding: const EdgeInsets.all(16),
width: screenWidth * 0.35,
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
children: [ children: [
Text( Text(
dialogTitle, widget.dialogTitle,
style: Theme.of(context) style: context.textTheme.headlineLarge?.copyWith(
.textTheme color: ColorsManager.blackColor,
.headlineLarge ),
?.copyWith(color: ColorsManager.blackColor),
), ),
const SizedBox(height: 16), const SizedBox(height: 16),
Container( Container(
width: screenWidth * 0.35, width: context.screenWidth * 0.35,
padding: const EdgeInsets.symmetric( padding: const EdgeInsets.symmetric(
vertical: 10.0, horizontal: 16.0), vertical: 10,
horizontal: 16,
),
decoration: BoxDecoration( decoration: BoxDecoration(
color: ColorsManager.boxColor, color: ColorsManager.boxColor,
borderRadius: BorderRadius.circular(10), borderRadius: BorderRadius.circular(10),
), ),
child: Wrap( child: Wrap(
spacing: 8.0, spacing: 8,
runSpacing: 8.0, runSpacing: 8,
alignment: WrapAlignment.start,
crossAxisAlignment: WrapCrossAlignment.center,
children: [ children: [
...state.subSpaces.asMap().entries.map( ...state.subSpaces.asMap().entries.map(
(entry) { (entry) {
final index = entry.key; final index = entry.key;
final subSpace = entry.value; final subSpace = entry.value;
final lowerName = final lowerName = subSpace.subspaceName.toLowerCase();
subSpace.subspaceName.toLowerCase();
final duplicateIndices = state.subSpaces final duplicateIndices = state.subSpaces
.asMap() .asMap()
.entries .entries
.where((e) => .where((e) =>
e.value.subspaceName.toLowerCase() == e.value.subspaceName.toLowerCase() == lowerName)
lowerName)
.map((e) => e.key) .map((e) => e.key)
.toList(); .toList();
final isDuplicate = final isDuplicate = duplicateIndices.length > 1 &&
duplicateIndices.length > 1 &&
duplicateIndices.indexOf(index) != 0; duplicateIndices.indexOf(index) != 0;
return SubspaceChip(
return Chip( subSpace: SubspaceTemplateModel(
label: Text(subSpace.subspaceName, subspaceName: entry.value.subspaceName,
style: Theme.of(context) disabled: entry.value.disabled,
.textTheme
.bodyMedium
?.copyWith(
color:
ColorsManager.spaceColor)),
backgroundColor: ColorsManager.whiteColors,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10),
side: BorderSide(
color: isDuplicate
? ColorsManager.red
: ColorsManager.transparentColor,
width: 0,
), ),
isDuplicate: isDuplicate,
onDeleted: () => context.read<SubSpaceBloc>().add(
RemoveSubSpace(subSpace),
), ),
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<SubSpaceBloc>()
.add(RemoveSubSpace(subSpace)),
); );
}, },
), ),
SizedBox( SizedBox(
width: 200, width: 200,
child: TextField( child: TextField(
controller: textController, controller: _subspaceNameController,
decoration: InputDecoration( decoration: InputDecoration(
border: InputBorder.none, border: InputBorder.none,
hintText: state.subSpaces.isEmpty hintText: state.subSpaces.isEmpty
? 'Please enter the name' ? 'Please enter the name'
: null, : null,
hintStyle: Theme.of(context) hintStyle: context.textTheme.bodySmall?.copyWith(
.textTheme color: ColorsManager.lightGrayColor,
.bodySmall ),
?.copyWith( ),
color: ColorsManager
.lightGrayColor)),
onSubmitted: (value) { onSubmitted: (value) {
if (value.trim().isNotEmpty) { final trimmedValue = value.trim();
if (trimmedValue.isNotEmpty) {
context.read<SubSpaceBloc>().add( context.read<SubSpaceBloc>().add(
AddSubSpace(SubspaceModel( AddSubSpace(
subspaceName: value.trim(), SubspaceModel(
disabled: false))); subspaceName: trimmedValue,
textController.clear(); disabled: false,
),
),
);
_subspaceNameController.clear();
} }
}, },
style: style: context.textTheme.bodyMedium,
Theme.of(context).textTheme.bodyMedium), ),
), ),
], ],
), ),
@@ -174,13 +160,12 @@ class CreateSubSpaceDialog extends StatelessWidget {
if (state.errorMessage.isNotEmpty) if (state.errorMessage.isNotEmpty)
Padding( Padding(
padding: const EdgeInsets.only(top: 8.0), padding: const EdgeInsets.only(top: 8.0),
child: Text(state.errorMessage, child: Text(
style: Theme.of(context) state.errorMessage,
.textTheme style: context.textTheme.bodySmall?.copyWith(
.bodySmall
?.copyWith(
color: ColorsManager.warningRed, color: ColorsManager.warningRed,
)), ),
),
), ),
const SizedBox(height: 16), const SizedBox(height: 16),
Row( Row(
@@ -196,16 +181,14 @@ class CreateSubSpaceDialog extends StatelessWidget {
const SizedBox(width: 10), const SizedBox(width: 10),
Expanded( Expanded(
child: DefaultButton( child: DefaultButton(
onPressed: (state.errorMessage.isNotEmpty) onPressed: state.errorMessage.isEmpty
? null ? () {
: () async { final subSpacesBloc = context.read<SubSpaceBloc>();
final subSpaces = context final subSpaces = subSpacesBloc.state.subSpaces;
.read<SubSpaceBloc>() widget.onSave?.call(subSpaces);
.state
.subSpaces;
onSave!(subSpaces);
Navigator.of(context).pop(); Navigator.of(context).pop();
}, }
: null,
backgroundColor: ColorsManager.secondaryColor, backgroundColor: ColorsManager.secondaryColor,
borderRadius: 10, borderRadius: 10,
foregroundColor: state.errorMessage.isNotEmpty foregroundColor: state.errorMessage.isNotEmpty
@@ -218,8 +201,7 @@ class CreateSubSpaceDialog extends StatelessWidget {
), ),
], ],
), ),
), );
));
}, },
), ),
), ),
@@ -1,4 +1,7 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.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/widgets/subspace_chip.dart'; import 'package:syncrow_web/pages/spaces_management/create_subspace_model/widgets/subspace_chip.dart';
import 'package:syncrow_web/pages/spaces_management/create_subspace_model/widgets/subspaces_textfield.dart'; import 'package:syncrow_web/pages/spaces_management/create_subspace_model/widgets/subspaces_textfield.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/subspace_template_model.dart';
@@ -51,6 +54,9 @@ class CreateSubspaceModelChipsBox extends StatelessWidget {
return SubspaceChip( return SubspaceChip(
subSpace: subSpace, subSpace: subSpace,
isDuplicate: isDuplicate, isDuplicate: isDuplicate,
onDeleted: () => context.read<SubSpaceModelBloc>().add(
RemoveSubSpaceModel(subSpace),
),
); );
}, },
), ),
@@ -1,7 +1,4 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.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/space_model/models/subspace_template_model.dart'; import 'package:syncrow_web/pages/spaces_management/space_model/models/subspace_template_model.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';
@@ -11,10 +8,12 @@ class SubspaceChip extends StatelessWidget {
required this.subSpace, required this.subSpace,
required this.isDuplicate, required this.isDuplicate,
super.key, super.key,
required this.onDeleted,
}); });
final SubspaceTemplateModel subSpace; final SubspaceTemplateModel subSpace;
final bool isDuplicate; final bool isDuplicate;
final void Function() onDeleted;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
@@ -50,9 +49,7 @@ class SubspaceChip extends StatelessWidget {
), ),
), ),
), ),
onDeleted: () => context.read<SubSpaceModelBloc>().add( onDeleted: onDeleted,
RemoveSubSpaceModel(subSpace),
),
); );
} }
} }
+2 -1
View File
@@ -321,13 +321,14 @@ class DevicesManagementApi {
Future<bool> factoryReset(FactoryResetModel factoryReset, String uuid) async { Future<bool> factoryReset(FactoryResetModel factoryReset, String uuid) async {
try { try {
final response = await HTTPService().post( final response = await HTTPService().post(
path: ApiEndpoints.factoryReset.replaceAll('{deviceUuid}', uuid), path: ApiEndpoints.factoryReset,
body: factoryReset.toMap(), body: factoryReset.toMap(),
showServerMessage: true, showServerMessage: true,
expectedResponseModel: (json) { expectedResponseModel: (json) {
return json['success'] ?? false; return json['success'] ?? false;
}, },
); );
return response; return response;
} catch (e) { } catch (e) {
debugPrint('Error fetching $e'); debugPrint('Error fetching $e');