diff --git a/lib/pages/space_management_v2/modules/tags/data/services/remote_tags_service.dart b/lib/pages/space_management_v2/modules/tags/data/services/remote_tags_service.dart index b5545bd3..76ceec71 100644 --- a/lib/pages/space_management_v2/modules/tags/data/services/remote_tags_service.dart +++ b/lib/pages/space_management_v2/modules/tags/data/services/remote_tags_service.dart @@ -1,10 +1,9 @@ import 'package:dio/dio.dart'; +import 'package:syncrow_web/pages/common/bloc/project_manager.dart'; import 'package:syncrow_web/pages/space_management_v2/modules/tags/domain/models/tag.dart'; -import 'package:syncrow_web/pages/space_management_v2/modules/tags/domain/params/load_tags_param.dart'; import 'package:syncrow_web/pages/space_management_v2/modules/tags/domain/services/tags_service.dart'; import 'package:syncrow_web/services/api/api_exception.dart'; import 'package:syncrow_web/services/api/http_service.dart'; -import 'package:syncrow_web/utils/constants/api_const.dart'; final class RemoteTagsService implements TagsService { const RemoteTagsService(this._httpService); @@ -14,17 +13,10 @@ final class RemoteTagsService implements TagsService { static const _defaultErrorMessage = 'Failed to load tags'; @override - Future> loadTags(LoadTagsParam param) async { - if (param.projectUuid == null) { - throw Exception('Project UUID is required'); - } - + Future> loadTags() async { try { final response = await _httpService.get( - path: ApiEndpoints.listTags.replaceAll( - '{projectUuid}', - param.projectUuid!, - ), + path: await _makeUrl(), expectedResponseModel: (json) { final result = json as Map; final data = result['data'] as List; @@ -46,4 +38,12 @@ final class RemoteTagsService implements TagsService { throw APIException(formattedErrorMessage); } } + + Future _makeUrl() async { + final projectUuid = await ProjectManager.getProjectUUID(); + if (projectUuid == null || projectUuid.isEmpty) { + throw APIException('Project UUID is required'); + } + return '/projects/$projectUuid/tags'; + } } diff --git a/lib/pages/space_management_v2/modules/tags/domain/services/tags_service.dart b/lib/pages/space_management_v2/modules/tags/domain/services/tags_service.dart index ae097020..cf36527a 100644 --- a/lib/pages/space_management_v2/modules/tags/domain/services/tags_service.dart +++ b/lib/pages/space_management_v2/modules/tags/domain/services/tags_service.dart @@ -1,6 +1,5 @@ import 'package:syncrow_web/pages/space_management_v2/modules/tags/domain/models/tag.dart'; -import 'package:syncrow_web/pages/space_management_v2/modules/tags/domain/params/load_tags_param.dart'; abstract interface class TagsService { - Future> loadTags(LoadTagsParam param); + Future> loadTags(); } diff --git a/lib/pages/space_management_v2/modules/tags/presentation/bloc/tags_bloc.dart b/lib/pages/space_management_v2/modules/tags/presentation/bloc/tags_bloc.dart index e51884cb..b81fcb76 100644 --- a/lib/pages/space_management_v2/modules/tags/presentation/bloc/tags_bloc.dart +++ b/lib/pages/space_management_v2/modules/tags/presentation/bloc/tags_bloc.dart @@ -1,7 +1,6 @@ import 'package:bloc/bloc.dart'; import 'package:equatable/equatable.dart'; import 'package:syncrow_web/pages/space_management_v2/modules/tags/domain/models/tag.dart'; -import 'package:syncrow_web/pages/space_management_v2/modules/tags/domain/params/load_tags_param.dart'; import 'package:syncrow_web/pages/space_management_v2/modules/tags/domain/services/tags_service.dart'; import 'package:syncrow_web/services/api/api_exception.dart'; @@ -21,7 +20,7 @@ class TagsBloc extends Bloc { ) async { emit(TagsLoading()); try { - final tags = await _tagsService.loadTags(event.param); + final tags = await _tagsService.loadTags(); emit(TagsLoaded(tags)); } on APIException catch (e) { emit(TagsFailure(e.message)); diff --git a/lib/pages/space_management_v2/modules/tags/presentation/bloc/tags_event.dart b/lib/pages/space_management_v2/modules/tags/presentation/bloc/tags_event.dart index 99134cab..8965b7b0 100644 --- a/lib/pages/space_management_v2/modules/tags/presentation/bloc/tags_event.dart +++ b/lib/pages/space_management_v2/modules/tags/presentation/bloc/tags_event.dart @@ -8,10 +8,5 @@ abstract class TagsEvent extends Equatable { } class LoadTags extends TagsEvent { - final LoadTagsParam param; - - const LoadTags(this.param); - - @override - List get props => [param]; + const LoadTags(); } diff --git a/lib/pages/space_management_v2/modules/tags/presentation/widgets/add_device_type_widget.dart b/lib/pages/space_management_v2/modules/tags/presentation/widgets/add_device_type_widget.dart index 673efe7e..f77f10b5 100644 --- a/lib/pages/space_management_v2/modules/tags/presentation/widgets/add_device_type_widget.dart +++ b/lib/pages/space_management_v2/modules/tags/presentation/widgets/add_device_type_widget.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:syncrow_web/pages/space_management_v2/modules/products/data/services/remote_products_service.dart'; import 'package:syncrow_web/pages/space_management_v2/modules/products/presentation/bloc/products_bloc.dart'; +import 'package:syncrow_web/pages/space_management_v2/modules/space_details/presentation/widgets/space_details_action_buttons.dart'; import 'package:syncrow_web/pages/space_management_v2/modules/tags/presentation/widgets/products_grid.dart'; import 'package:syncrow_web/services/api/http_service.dart'; import 'package:syncrow_web/utils/color_manager.dart'; @@ -32,6 +33,13 @@ class AddDeviceTypeWidget extends StatelessWidget { ), }, ), + actions: [ + SpaceDetailsActionButtons( + onSave: () {}, + onCancel: Navigator.of(context).pop, + saveButtonLabel: 'Next', + ), + ], ), ), ); diff --git a/lib/pages/space_management_v2/modules/tags/presentation/widgets/assign_tags_table.dart b/lib/pages/space_management_v2/modules/tags/presentation/widgets/assign_tags_table.dart index 196b4102..3d92109d 100644 --- a/lib/pages/space_management_v2/modules/tags/presentation/widgets/assign_tags_table.dart +++ b/lib/pages/space_management_v2/modules/tags/presentation/widgets/assign_tags_table.dart @@ -1,8 +1,11 @@ import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:syncrow_web/common/dialog_dropdown.dart'; import 'package:syncrow_web/pages/space_management_v2/modules/space_details/domain/models/space_details_model.dart'; -import 'package:syncrow_web/pages/space_management_v2/modules/tags/domain/models/tag.dart'; +import 'package:syncrow_web/pages/space_management_v2/modules/tags/data/services/remote_tags_service.dart'; +import 'package:syncrow_web/pages/space_management_v2/modules/tags/presentation/bloc/tags_bloc.dart'; import 'package:syncrow_web/pages/space_management_v2/modules/tags/presentation/widgets/product_tag_field.dart'; +import 'package:syncrow_web/services/api/http_service.dart'; import 'package:syncrow_web/utils/color_manager.dart'; import 'package:syncrow_web/utils/extension/build_context_x.dart'; import 'package:uuid/uuid.dart'; @@ -48,123 +51,138 @@ class _AssignTagsTableState extends State { @override Widget build(BuildContext context) { - return ClipRRect( - borderRadius: BorderRadius.circular(20), - child: DataTable( - headingRowColor: WidgetStateProperty.all(ColorsManager.dataHeaderGrey), - key: ValueKey(widget.productAllocations.length), - border: TableBorder.all( - color: ColorsManager.dataHeaderGrey, - width: 1, - borderRadius: BorderRadius.circular(20), - ), - columns: [ - _buildDataColumn('#'), - _buildDataColumn('Device'), - _buildDataColumn('Tag'), - _buildDataColumn('Location'), - ], - rows: widget.productAllocations.isEmpty - ? [ - DataRow( - cells: [ - DataCell( - Center( - child: SelectableText( - 'No Devices Available', - style: context.textTheme.bodyMedium?.copyWith( - color: ColorsManager.lightGrayColor, - ), - ), - ), - ), - DataCell.empty, - DataCell.empty, - DataCell.empty, + return BlocProvider( + create: (BuildContext context) => TagsBloc( + RemoteTagsService(HTTPService()), + )..add(const LoadTags()), + child: BlocBuilder( + builder: (context, state) { + return switch (state) { + TagsLoading() || TagsInitial() => const Center( + child: CircularProgressIndicator(), + ), + TagsFailure(:final message) => Center( + child: Text(message), + ), + TagsLoaded(:final tags) => ClipRRect( + borderRadius: BorderRadius.circular(20), + child: DataTable( + headingRowColor: WidgetStateProperty.all( + ColorsManager.dataHeaderGrey, + ), + key: ValueKey(widget.productAllocations.length), + border: TableBorder.all( + color: ColorsManager.dataHeaderGrey, + width: 1, + borderRadius: BorderRadius.circular(20), + ), + columns: [ + _buildDataColumn('#'), + _buildDataColumn('Device'), + _buildDataColumn('Tag'), + _buildDataColumn('Location'), ], - ), - ] - : List.generate(widget.productAllocations.length, (index) { - final productAllocation = widget.productAllocations[index]; - final controller = _controllers[index]; + rows: widget.productAllocations.isEmpty + ? [ + DataRow( + cells: [ + DataCell( + Center( + child: SelectableText( + 'No Devices Available', + style: context.textTheme.bodyMedium?.copyWith( + color: ColorsManager.lightGrayColor, + ), + ), + ), + ), + DataCell.empty, + DataCell.empty, + DataCell.empty, + ], + ), + ] + : List.generate(widget.productAllocations.length, (index) { + final productAllocation = widget.productAllocations[index]; + final controller = _controllers[index]; - return DataRow( - cells: [ - DataCell(Text((index + 1).toString())), - DataCell( - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Expanded( - child: Text( - productAllocation.product.name, - overflow: TextOverflow.ellipsis, - )), - const SizedBox(width: 10), - Container( - width: 20, - height: 20, - decoration: BoxDecoration( - shape: BoxShape.circle, - border: Border.all( - color: ColorsManager.lightGrayColor, - width: 1, + return DataRow( + cells: [ + DataCell(Text((index + 1).toString())), + DataCell( + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Expanded( + child: Text( + productAllocation.product.name, + overflow: TextOverflow.ellipsis, + )), + const SizedBox(width: 10), + Container( + width: 20, + height: 20, + decoration: BoxDecoration( + shape: BoxShape.circle, + border: Border.all( + color: ColorsManager.lightGrayColor, + width: 1, + ), + ), + child: IconButton( + icon: const Icon( + Icons.close, + color: ColorsManager.lightGreyColor, + size: 16, + ), + onPressed: () { + // TODO: Delete the product allocation + }, + tooltip: 'Delete Tag', + padding: EdgeInsets.zero, + constraints: const BoxConstraints(), + ), + ), + ], + ), ), - ), - child: IconButton( - icon: const Icon( - Icons.close, - color: ColorsManager.lightGreyColor, - size: 16, + DataCell( + Container( + alignment: Alignment.centerLeft, + width: double.infinity, + child: ProductTagField( + key: ValueKey( + 'dropdown_${const Uuid().v4()}_$index'), + productName: productAllocation.product.uuid, + initialValue: null, + onSelected: (value) { + controller.text = value.name; + }, + items: tags, + ), + ), ), - onPressed: () { - // TODO: Delete the product allocation - }, - tooltip: 'Delete Tag', - padding: EdgeInsets.zero, - constraints: const BoxConstraints(), - ), - ), - ], - ), - ), - DataCell( - Container( - alignment: Alignment.centerLeft, - width: double.infinity, - child: ProductTagField( - key: ValueKey('dropdown_${const Uuid().v4()}_$index'), - productName: productAllocation.product.uuid, - initialValue: null, - onSelected: (value) { - controller.text = value.name; - }, - items: const [ - Tag( - uuid: '', - name: 'Tag', - createdAt: '', - updatedAt: '', - ), - ], - ), - ), - ), - DataCell( - SizedBox( - width: double.infinity, - child: DialogDropdown( - items: const [], - // items: widget.locations, - selectedValue: productAllocation.tag.name.isEmpty - ? 'Main Space' - : productAllocation.tag.name, - onSelected: (value) {}, - )), - ), - ], - ); - }), + DataCell( + SizedBox( + width: double.infinity, + child: DialogDropdown( + items: const [], + // items: widget.locations, + selectedValue: + productAllocation.tag.name.isEmpty + ? 'Main Space' + : productAllocation.tag.name, + onSelected: (value) {}, + )), + ), + ], + ); + }), + ), + ), + _ => const SizedBox.shrink(), + }; + }, ), ); }