From 82cd77678b821cc48fd22d4be691479f3f6cad4e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Fastr=C3=A9?= Date: Thu, 26 Sep 2024 15:10:34 +0200 Subject: [PATCH] Create a PickUserGroupOrUserDynamicType - add necessary vue component to render usergroup within the component AddPersons; - add necessary normalization and denormalization process for matching the selected usergroup with entities in database --- .../ChillMainBundle/Entity/UserGroup.php | 7 -- .../EntityToJsonTransformer.php | 2 + .../Type/PickUserGroupOrUserDynamicType.php | 68 +++++++++++++++++++ .../ChillMainBundle/Resources/public/types.ts | 8 ++- .../_components/Entity/UserGroupRenderBox.vue | 26 +++++++ .../Normalizer/UserGroupDenormalizer.php | 37 ++++++++++ .../Normalizer/UserGroupNormalizer.php | 41 +++++++++++ .../Normalizer/UserGroupDenormalizerTest.php | 62 +++++++++++++++++ .../Normalizer/UserGroupNormalizerTest.php | 56 +++++++++++++++ .../Resources/public/chill/scss/badge.scss | 4 ++ .../AddPersons/PersonSuggestion.vue | 7 ++ .../_components/AddPersons/TypeUserGroup.vue | 30 ++++++++ 12 files changed, 340 insertions(+), 8 deletions(-) create mode 100644 src/Bundle/ChillMainBundle/Form/Type/PickUserGroupOrUserDynamicType.php create mode 100644 src/Bundle/ChillMainBundle/Resources/public/vuejs/_components/Entity/UserGroupRenderBox.vue create mode 100644 src/Bundle/ChillMainBundle/Serializer/Normalizer/UserGroupDenormalizer.php create mode 100644 src/Bundle/ChillMainBundle/Serializer/Normalizer/UserGroupNormalizer.php create mode 100644 src/Bundle/ChillMainBundle/Tests/Serializer/Normalizer/UserGroupDenormalizerTest.php create mode 100644 src/Bundle/ChillMainBundle/Tests/Serializer/Normalizer/UserGroupNormalizerTest.php create mode 100644 src/Bundle/ChillPersonBundle/Resources/public/vuejs/_components/AddPersons/TypeUserGroup.vue diff --git a/src/Bundle/ChillMainBundle/Entity/UserGroup.php b/src/Bundle/ChillMainBundle/Entity/UserGroup.php index 03da74326..588013b65 100644 --- a/src/Bundle/ChillMainBundle/Entity/UserGroup.php +++ b/src/Bundle/ChillMainBundle/Entity/UserGroup.php @@ -14,21 +14,17 @@ namespace Chill\MainBundle\Entity; use Doctrine\Common\Collections\ArrayCollection; use Doctrine\Common\Collections\Collection; use Doctrine\ORM\Mapping as ORM; -use Symfony\Component\Serializer\Annotation as Serializer; #[ORM\Entity] #[ORM\Table(name: 'chill_main_user_group')] -#[Serializer\DiscriminatorMap(typeProperty: 'type', mapping: ['user_group' => UserGroup::class])] class UserGroup { #[ORM\Id] #[ORM\GeneratedValue] #[ORM\Column(type: \Doctrine\DBAL\Types\Types::INTEGER, nullable: false)] - #[Serializer\Groups(['read'])] private ?int $id = null; #[ORM\Column(type: \Doctrine\DBAL\Types\Types::JSON, nullable: false, options: ['default' => '[]'])] - #[Serializer\Groups(['read'])] private array $label = []; /** @@ -39,11 +35,9 @@ class UserGroup private Collection $users; #[ORM\Column(type: \Doctrine\DBAL\Types\Types::TEXT, nullable: false, options: ['default' => '#ffffffff'])] - #[Serializer\Groups(['read'])] private string $backgroundColor = '#ffffffff'; #[ORM\Column(type: \Doctrine\DBAL\Types\Types::TEXT, nullable: false, options: ['default' => '#000000ff'])] - #[Serializer\Groups(['read'])] private string $foregroundColor = '#000000ff'; /** @@ -53,7 +47,6 @@ class UserGroup * An empty string means "no exclusion" */ #[ORM\Column(type: \Doctrine\DBAL\Types\Types::TEXT, nullable: false, options: ['default' => ''])] - #[Serializer\Groups(['read'])] private string $excludeKey = ''; public function __construct() diff --git a/src/Bundle/ChillMainBundle/Form/Type/DataTransformer/EntityToJsonTransformer.php b/src/Bundle/ChillMainBundle/Form/Type/DataTransformer/EntityToJsonTransformer.php index d193ea2ef..737c4683f 100644 --- a/src/Bundle/ChillMainBundle/Form/Type/DataTransformer/EntityToJsonTransformer.php +++ b/src/Bundle/ChillMainBundle/Form/Type/DataTransformer/EntityToJsonTransformer.php @@ -12,6 +12,7 @@ declare(strict_types=1); namespace Chill\MainBundle\Form\Type\DataTransformer; use Chill\MainBundle\Entity\User; +use Chill\MainBundle\Entity\UserGroup; use Chill\PersonBundle\Entity\Person; use Chill\ThirdPartyBundle\Entity\ThirdParty; use Symfony\Component\Form\DataTransformerInterface; @@ -74,6 +75,7 @@ class EntityToJsonTransformer implements DataTransformerInterface 'user' => User::class, 'person' => Person::class, 'thirdparty' => ThirdParty::class, + 'user_group' => UserGroup::class, default => throw new \UnexpectedValueException('This type is not supported'), }; diff --git a/src/Bundle/ChillMainBundle/Form/Type/PickUserGroupOrUserDynamicType.php b/src/Bundle/ChillMainBundle/Form/Type/PickUserGroupOrUserDynamicType.php new file mode 100644 index 000000000..00d783a97 --- /dev/null +++ b/src/Bundle/ChillMainBundle/Form/Type/PickUserGroupOrUserDynamicType.php @@ -0,0 +1,68 @@ +addViewTransformer(new EntityToJsonTransformer($this->denormalizer, $this->serializer, $options['multiple'], 'user_group')); + } + + public function buildView(FormView $view, FormInterface $form, array $options) + { + $view->vars['multiple'] = $options['multiple']; + $view->vars['types'] = ['user-group', 'user']; + $view->vars['uniqid'] = uniqid('pick_usergroup_dyn'); + $view->vars['suggested'] = []; + $view->vars['as_id'] = true === $options['as_id'] ? '1' : '0'; + $view->vars['submit_on_adding_new_entity'] = true === $options['submit_on_adding_new_entity'] ? '1' : '0'; + + foreach ($options['suggested'] as $userGroup) { + $view->vars['suggested'][] = $this->normalizer->normalize($userGroup, 'json', ['groups' => 'read']); + } + } + + public function configureOptions(OptionsResolver $resolver) + { + $resolver + ->setDefault('multiple', false) + ->setAllowedTypes('multiple', ['bool']) + ->setDefault('compound', false) + ->setDefault('suggested', []) + // if set to true, only the id will be set inside the content. The denormalization will not work. + ->setDefault('as_id', false) + ->setAllowedTypes('as_id', ['bool']) + ->setDefault('submit_on_adding_new_entity', false) + ->setAllowedTypes('submit_on_adding_new_entity', ['bool']); + } + + public function getBlockPrefix() + { + return 'pick_entity_dynamic'; + } +} diff --git a/src/Bundle/ChillMainBundle/Resources/public/types.ts b/src/Bundle/ChillMainBundle/Resources/public/types.ts index 8d2ea2ded..9df338549 100644 --- a/src/Bundle/ChillMainBundle/Resources/public/types.ts +++ b/src/Bundle/ChillMainBundle/Resources/public/types.ts @@ -30,6 +30,11 @@ export interface Scope { }; } +export interface ResultItem { + result: T; + relevance: number; +} + export interface User { type: "user"; id: number; @@ -43,12 +48,13 @@ export interface User { } export interface UserGroup { - type: "chill_main_user_group" | "user_group"; + type: "user_group"; id: number; label: TranslatableString; backgroundColor: string; foregroundColor: string; excludeKey: string; + text: string; } export type UserGroupOrUser = User | UserGroup; diff --git a/src/Bundle/ChillMainBundle/Resources/public/vuejs/_components/Entity/UserGroupRenderBox.vue b/src/Bundle/ChillMainBundle/Resources/public/vuejs/_components/Entity/UserGroupRenderBox.vue new file mode 100644 index 000000000..d25100776 --- /dev/null +++ b/src/Bundle/ChillMainBundle/Resources/public/vuejs/_components/Entity/UserGroupRenderBox.vue @@ -0,0 +1,26 @@ + + + + + diff --git a/src/Bundle/ChillMainBundle/Serializer/Normalizer/UserGroupDenormalizer.php b/src/Bundle/ChillMainBundle/Serializer/Normalizer/UserGroupDenormalizer.php new file mode 100644 index 000000000..0960ea218 --- /dev/null +++ b/src/Bundle/ChillMainBundle/Serializer/Normalizer/UserGroupDenormalizer.php @@ -0,0 +1,37 @@ +userGroupRepository->find($data['id']); + } + + public function supportsDenormalization($data, string $type, ?string $format = null): bool + { + return UserGroup::class === $type + && 'json' === $format + && is_array($data) + && array_key_exists('id', $data) + && 'user_group' === ($data['type'] ?? false) + && 2 === count(array_keys($data)) + ; + } +} diff --git a/src/Bundle/ChillMainBundle/Serializer/Normalizer/UserGroupNormalizer.php b/src/Bundle/ChillMainBundle/Serializer/Normalizer/UserGroupNormalizer.php new file mode 100644 index 000000000..8738167eb --- /dev/null +++ b/src/Bundle/ChillMainBundle/Serializer/Normalizer/UserGroupNormalizer.php @@ -0,0 +1,41 @@ + 'user_group', + 'id' => $object->getId(), + 'label' => $object->getLabel(), + 'backgroundColor' => $object->getBackgroundColor(), + 'foregroundColor' => $object->getForegroundColor(), + 'excludeKey' => $object->getExcludeKey(), + 'text' => $this->userGroupRender->renderString($object, []), + ]; + } + + public function supportsNormalization($data, ?string $format = null) + { + return $data instanceof UserGroup; + } +} diff --git a/src/Bundle/ChillMainBundle/Tests/Serializer/Normalizer/UserGroupDenormalizerTest.php b/src/Bundle/ChillMainBundle/Tests/Serializer/Normalizer/UserGroupDenormalizerTest.php new file mode 100644 index 000000000..95a633519 --- /dev/null +++ b/src/Bundle/ChillMainBundle/Tests/Serializer/Normalizer/UserGroupDenormalizerTest.php @@ -0,0 +1,62 @@ +createMock(UserGroupRepositoryInterface::class); + $denormalizer = new UserGroupDenormalizer($repository); + + $actual = $denormalizer->supportsDenormalization($data, $type, 'json'); + + self::assertSame($expected, $actual); + } + + public static function provideSupportsDenormalization(): iterable + { + yield [['type' => 'user_group', 'id' => 10], UserGroup::class, true]; + yield [['type' => 'person', 'id' => 10], UserGroup::class, false]; + yield [['type' => 'user_group', 'id' => 10], \stdClass::class, false]; + } + + public function testDenormalize(): void + { + $repository = $this->createMock(UserGroupRepositoryInterface::class); + $repository->expects($this->once()) + ->method('find') + ->with(10) + ->willReturn($userGroup = new UserGroup()); + + $denormalizer = new UserGroupDenormalizer($repository); + + $actual = $denormalizer->denormalize(['type' => 'user_group', 'id' => 10], UserGroup::class, 'json'); + + self::assertSame($userGroup, $actual); + } +} diff --git a/src/Bundle/ChillMainBundle/Tests/Serializer/Normalizer/UserGroupNormalizerTest.php b/src/Bundle/ChillMainBundle/Tests/Serializer/Normalizer/UserGroupNormalizerTest.php new file mode 100644 index 000000000..eb85c99a7 --- /dev/null +++ b/src/Bundle/ChillMainBundle/Tests/Serializer/Normalizer/UserGroupNormalizerTest.php @@ -0,0 +1,56 @@ +setLabel(['fr' => 'test']) + ->setExcludeKey('top') + ->setForegroundColor('#123456') + ->setBackgroundColor('#456789'); + + $entityRender = $this->createMock(UserGroupRenderInterface::class); + $entityRender->expects($this->once()) + ->method('renderString') + ->with($userGroup, []) + ->willReturn('text'); + + $normalizer = new UserGroupNormalizer($entityRender); + + $actual = $normalizer->normalize($userGroup, 'json', [AbstractNormalizer::GROUPS => ['read']]); + + self::assertEqualsCanonicalizing([ + 'type' => 'user_group', + 'text' => 'text', + 'label' => ['fr' => 'test'], + 'excludeKey' => 'top', + 'foregroundColor' => '#123456', + 'backgroundColor' => '#456789', + 'id' => null, + ], $actual); + } +} diff --git a/src/Bundle/ChillPersonBundle/Resources/public/chill/scss/badge.scss b/src/Bundle/ChillPersonBundle/Resources/public/chill/scss/badge.scss index d1faf5614..cec272a3a 100644 --- a/src/Bundle/ChillPersonBundle/Resources/public/chill/scss/badge.scss +++ b/src/Bundle/ChillPersonBundle/Resources/public/chill/scss/badge.scss @@ -238,6 +238,10 @@ div[class*='budget-'] { background-color: $chill-ll-gray; color: $chill-blue; } + &.bg-user-group { + background-color: $chill-l-gray; + color: $chill-blue; + } &.bg-confidential { background-color: $chill-ll-gray; color: $chill-red; diff --git a/src/Bundle/ChillPersonBundle/Resources/public/vuejs/_components/AddPersons/PersonSuggestion.vue b/src/Bundle/ChillPersonBundle/Resources/public/vuejs/_components/AddPersons/PersonSuggestion.vue index b605cd5b5..ec9556a95 100644 --- a/src/Bundle/ChillPersonBundle/Resources/public/vuejs/_components/AddPersons/PersonSuggestion.vue +++ b/src/Bundle/ChillPersonBundle/Resources/public/vuejs/_components/AddPersons/PersonSuggestion.vue @@ -27,6 +27,11 @@ v-bind:item="item"> + + > + @@ -41,6 +46,7 @@ import SuggestionPerson from './TypePerson'; import SuggestionThirdParty from './TypeThirdParty'; import SuggestionUser from './TypeUser'; import SuggestionHousehold from './TypeHousehold'; +import SuggestionUserGroup from './TypeUserGroup'; export default { name: 'PersonSuggestion', @@ -49,6 +55,7 @@ export default { SuggestionThirdParty, SuggestionUser, SuggestionHousehold, + SuggestionUserGroup, }, props: [ 'item', diff --git a/src/Bundle/ChillPersonBundle/Resources/public/vuejs/_components/AddPersons/TypeUserGroup.vue b/src/Bundle/ChillPersonBundle/Resources/public/vuejs/_components/AddPersons/TypeUserGroup.vue new file mode 100644 index 000000000..f1a83ff22 --- /dev/null +++ b/src/Bundle/ChillPersonBundle/Resources/public/vuejs/_components/AddPersons/TypeUserGroup.vue @@ -0,0 +1,30 @@ + + + + +