From 1195b54a68036df7cca74d347fb80697f5d89d5b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Fastr=C3=A9?= Date: Wed, 10 Sep 2025 16:28:45 +0000 Subject: [PATCH 01/15] Resolve "user notification preferences are not displayed correctly" --- .../unreleased/Fixed-20250910-180935.yaml | 6 + .../UpdateProfile/UpdateProfileCommand.php | 64 +++++++++++ .../UpdateProfileCommandHandler.php | 27 +++++ .../Controller/UserProfileController.php | 63 ----------- .../UserUpdateProfileController.php | 75 +++++++++++++ src/Bundle/ChillMainBundle/Entity/User.php | 74 ++++++++----- .../DataMapper/NotificationFlagDataMapper.php | 75 ------------- .../Form/Type/NotificationFlagsType.php | 16 +-- ...rProfileType.php => UpdateProfileType.php} | 14 +-- .../Resources/views/User/profile.html.twig | 2 +- .../UpdateProfileCommandHandlerTest.php | 85 +++++++++++++++ .../UpdateProfileCommandTest.php | 103 ++++++++++++++++++ .../Tests/Entity/NotificationTest.php | 6 +- .../UserNotificationFlagsPersistenceTest.php | 82 ++++++++++++++ .../ChillMainBundle/Tests/Entity/UserTest.php | 18 +++ .../Email/NotificationMailerTest.php | 2 +- .../ChillMainBundle/config/services.yaml | 2 + 17 files changed, 529 insertions(+), 185 deletions(-) create mode 100644 .changes/unreleased/Fixed-20250910-180935.yaml create mode 100644 src/Bundle/ChillMainBundle/Action/User/UpdateProfile/UpdateProfileCommand.php create mode 100644 src/Bundle/ChillMainBundle/Action/User/UpdateProfile/UpdateProfileCommandHandler.php delete mode 100644 src/Bundle/ChillMainBundle/Controller/UserProfileController.php create mode 100644 src/Bundle/ChillMainBundle/Controller/UserUpdateProfileController.php delete mode 100644 src/Bundle/ChillMainBundle/Form/DataMapper/NotificationFlagDataMapper.php rename src/Bundle/ChillMainBundle/Form/{UserProfileType.php => UpdateProfileType.php} (75%) create mode 100644 src/Bundle/ChillMainBundle/Tests/Action/User/UpdateProfile/UpdateProfileCommandHandlerTest.php create mode 100644 src/Bundle/ChillMainBundle/Tests/Action/User/UpdateProfile/UpdateProfileCommandTest.php create mode 100644 src/Bundle/ChillMainBundle/Tests/Entity/UserNotificationFlagsPersistenceTest.php diff --git a/.changes/unreleased/Fixed-20250910-180935.yaml b/.changes/unreleased/Fixed-20250910-180935.yaml new file mode 100644 index 000000000..9e0a90cc8 --- /dev/null +++ b/.changes/unreleased/Fixed-20250910-180935.yaml @@ -0,0 +1,6 @@ +kind: Fixed +body: Fix saving notification preferences in user's profile +time: 2025-09-10T18:09:35.525979715+02:00 +custom: + Issue: "" + SchemaChange: No schema change diff --git a/src/Bundle/ChillMainBundle/Action/User/UpdateProfile/UpdateProfileCommand.php b/src/Bundle/ChillMainBundle/Action/User/UpdateProfile/UpdateProfileCommand.php new file mode 100644 index 000000000..f0751cb58 --- /dev/null +++ b/src/Bundle/ChillMainBundle/Action/User/UpdateProfile/UpdateProfileCommand.php @@ -0,0 +1,64 @@ +getPhonenumber()); + + foreach ($flagManager->getAllNotificationFlagProviders() as $provider) { + $updateProfileCommand->setNotificationFlag( + $provider->getFlag(), + User::NOTIF_FLAG_IMMEDIATE_EMAIL, + $user->isNotificationSendImmediately($provider->getFlag()) + ); + $updateProfileCommand->setNotificationFlag( + $provider->getFlag(), + User::NOTIF_FLAG_DAILY_DIGEST, + $user->isNotificationDailyDigest($provider->getFlag()) + ); + } + + return $updateProfileCommand; + } + + /** + * @param User::NOTIF_FLAG_IMMEDIATE_EMAIL|User::NOTIF_FLAG_DAILY_DIGEST $kind + */ + private function setNotificationFlag(string $type, string $kind, bool $value): void + { + if (!array_key_exists($type, $this->notificationFlags)) { + $this->notificationFlags[$type] = ['immediate_email' => true, 'daily_digest' => false]; + } + + $k = match ($kind) { + User::NOTIF_FLAG_IMMEDIATE_EMAIL => 'immediate_email', + User::NOTIF_FLAG_DAILY_DIGEST => 'daily_digest', + }; + + $this->notificationFlags[$type][$k] = $value; + } +} diff --git a/src/Bundle/ChillMainBundle/Action/User/UpdateProfile/UpdateProfileCommandHandler.php b/src/Bundle/ChillMainBundle/Action/User/UpdateProfile/UpdateProfileCommandHandler.php new file mode 100644 index 000000000..4c46e686e --- /dev/null +++ b/src/Bundle/ChillMainBundle/Action/User/UpdateProfile/UpdateProfileCommandHandler.php @@ -0,0 +1,27 @@ +setPhonenumber($command->phonenumber); + + foreach ($command->notificationFlags as $flag => $values) { + $user->setNotificationImmediately($flag, $values['immediate_email']); + $user->setNotificationDailyDigest($flag, $values['daily_digest']); + } + } +} diff --git a/src/Bundle/ChillMainBundle/Controller/UserProfileController.php b/src/Bundle/ChillMainBundle/Controller/UserProfileController.php deleted file mode 100644 index b022a2b60..000000000 --- a/src/Bundle/ChillMainBundle/Controller/UserProfileController.php +++ /dev/null @@ -1,63 +0,0 @@ -security->isGranted('ROLE_USER')) { - throw new AccessDeniedHttpException(); - } - - $user = $this->security->getUser(); - $editForm = $this->createForm(UserProfileType::class, $user); - - $editForm->get('notificationFlags')->setData($user->getNotificationFlags()); - - $editForm->handleRequest($request); - - if ($editForm->isSubmitted() && $editForm->isValid()) { - $notificationFlagsData = $editForm->get('notificationFlags')->getData(); - $user->setNotificationFlags($notificationFlagsData); - - $em = $this->managerRegistry->getManager(); - $em->flush(); - $this->addFlash('success', $this->translator->trans('user.profile.Profile successfully updated!')); - - return $this->redirectToRoute('chill_main_user_profile'); - } - - return $this->render('@ChillMain/User/profile.html.twig', [ - 'user' => $user, - 'form' => $editForm->createView(), - ]); - } -} diff --git a/src/Bundle/ChillMainBundle/Controller/UserUpdateProfileController.php b/src/Bundle/ChillMainBundle/Controller/UserUpdateProfileController.php new file mode 100644 index 000000000..b23c201f0 --- /dev/null +++ b/src/Bundle/ChillMainBundle/Controller/UserUpdateProfileController.php @@ -0,0 +1,75 @@ +security->isGranted('ROLE_USER')) { + throw new AccessDeniedHttpException(); + } + + $user = $this->security->getUser(); + + $command = UpdateProfileCommand::create($user, $this->notificationFlagManager); + $editForm = $this->formFactory->create(UpdateProfileType::class, $command); + + $editForm->handleRequest($request); + + if ($editForm->isSubmitted() && $editForm->isValid()) { + $this->updateProfileCommandHandler->updateProfile($user, $command); + $this->entityManager->flush(); + $session->getFlashBag()->add('success', $this->translator->trans('user.profile.Profile successfully updated!')); + + return new RedirectResponse($this->urlGenerator->generate('chill_main_user_profile')); + } + + return new Response($this->twig->render('@ChillMain/User/profile.html.twig', [ + 'user' => $user, + 'form' => $editForm->createView(), + ])); + } +} diff --git a/src/Bundle/ChillMainBundle/Entity/User.php b/src/Bundle/ChillMainBundle/Entity/User.php index 791391659..70b032238 100644 --- a/src/Bundle/ChillMainBundle/Entity/User.php +++ b/src/Bundle/ChillMainBundle/Entity/User.php @@ -652,42 +652,66 @@ class User implements UserInterface, \Stringable, PasswordAuthenticatedUserInter return true; } - public function getNotificationFlags(): array + private function getNotificationFlagData(string $flag): array { - return $this->notificationFlags; - } - - public function setNotificationFlags(array $notificationFlags) - { - $this->notificationFlags = $notificationFlags; - } - - public function getNotificationFlagData(string $flag): array - { - return $this->notificationFlags[$flag] ?? []; - } - - public function setNotificationFlagData(string $flag, array $data): void - { - $this->notificationFlags[$flag] = $data; + return $this->notificationFlags[$flag] ?? [self::NOTIF_FLAG_IMMEDIATE_EMAIL]; } public function isNotificationSendImmediately(string $type): bool { - if ([] === $this->getNotificationFlagData($type) || in_array(User::NOTIF_FLAG_IMMEDIATE_EMAIL, $this->getNotificationFlagData($type), true)) { - return true; + return $this->isNotificationForElement($type, self::NOTIF_FLAG_IMMEDIATE_EMAIL); + } + + public function setNotificationImmediately(string $type, bool $active): void + { + $this->setNotificationFlagElement($type, $active, self::NOTIF_FLAG_IMMEDIATE_EMAIL); + } + + public function setNotificationDailyDigest(string $type, bool $active): void + { + $this->setNotificationFlagElement($type, $active, self::NOTIF_FLAG_DAILY_DIGEST); + } + + /** + * @param self::NOTIF_FLAG_IMMEDIATE_EMAIL|self::NOTIF_FLAG_DAILY_DIGEST $kind + */ + private function setNotificationFlagElement(string $type, bool $active, string $kind): void + { + $notificationFlags = [...$this->notificationFlags]; + $changed = false; + + if (!isset($notificationFlags[$type])) { + $notificationFlags[$type] = [self::NOTIF_FLAG_IMMEDIATE_EMAIL]; + $changed = true; } - return false; + if ($active) { + if (!in_array($kind, $notificationFlags[$type], true)) { + $notificationFlags[$type] = [...$notificationFlags[$type], $kind]; + $changed = true; + } + } else { + if (in_array($kind, $notificationFlags[$type], true)) { + $notificationFlags[$type] = array_values( + array_filter($notificationFlags[$type], static fn ($k) => $k !== $kind) + ); + $changed = true; + } + } + + if ($changed) { + $this->notificationFlags = [...$notificationFlags]; + } + } + + private function isNotificationForElement(string $type, string $kind): bool + { + return in_array($kind, $this->getNotificationFlagData($type), true); } public function isNotificationDailyDigest(string $type): bool { - if (in_array(User::NOTIF_FLAG_DAILY_DIGEST, $this->getNotificationFlagData($type), true)) { - return true; - } - - return false; + return $this->isNotificationForElement($type, self::NOTIF_FLAG_DAILY_DIGEST); } public function getLocale(): string diff --git a/src/Bundle/ChillMainBundle/Form/DataMapper/NotificationFlagDataMapper.php b/src/Bundle/ChillMainBundle/Form/DataMapper/NotificationFlagDataMapper.php deleted file mode 100644 index d904ed5b5..000000000 --- a/src/Bundle/ChillMainBundle/Form/DataMapper/NotificationFlagDataMapper.php +++ /dev/null @@ -1,75 +0,0 @@ -notificationFlagProviders as $flagProvider) { - $flag = $flagProvider->getFlag(); - - if (isset($formsArray[$flag])) { - $flagForm = $formsArray[$flag]; - - $immediateEmailChecked = in_array(User::NOTIF_FLAG_IMMEDIATE_EMAIL, $viewData[$flag] ?? [], true) - || !array_key_exists($flag, $viewData); - $dailyEmailChecked = in_array(User::NOTIF_FLAG_DAILY_DIGEST, $viewData[$flag] ?? [], true); - - if ($flagForm->has('immediate_email')) { - $flagForm->get('immediate_email')->setData($immediateEmailChecked); - } - if ($flagForm->has('daily_email')) { - $flagForm->get('daily_email')->setData($dailyEmailChecked); - } - } - } - } - - public function mapFormsToData($forms, &$viewData): void - { - $formsArray = iterator_to_array($forms); - $viewData = []; - - foreach ($this->notificationFlagProviders as $flagProvider) { - $flag = $flagProvider->getFlag(); - - if (isset($formsArray[$flag])) { - $flagForm = $formsArray[$flag]; - $viewData[$flag] = []; - - if (true === $flagForm['immediate_email']->getData()) { - $viewData[$flag][] = User::NOTIF_FLAG_IMMEDIATE_EMAIL; - } - - if (true === $flagForm['daily_email']->getData()) { - $viewData[$flag][] = User::NOTIF_FLAG_DAILY_DIGEST; - } - - if ([] === $viewData[$flag]) { - $viewData[$flag][] = User::NOTIF_FLAG_IMMEDIATE_EMAIL; - } - } - } - } -} diff --git a/src/Bundle/ChillMainBundle/Form/Type/NotificationFlagsType.php b/src/Bundle/ChillMainBundle/Form/Type/NotificationFlagsType.php index 4535a4815..b9e15f5d9 100644 --- a/src/Bundle/ChillMainBundle/Form/Type/NotificationFlagsType.php +++ b/src/Bundle/ChillMainBundle/Form/Type/NotificationFlagsType.php @@ -11,11 +11,9 @@ declare(strict_types=1); namespace Chill\MainBundle\Form\Type; -use Chill\MainBundle\Form\DataMapper\NotificationFlagDataMapper; use Chill\MainBundle\Notification\NotificationFlagManager; use Symfony\Component\Form\AbstractType; use Symfony\Component\Form\Extension\Core\Type\CheckboxType; -use Symfony\Component\Form\Extension\Core\Type\FormType; use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\OptionsResolver\OptionsResolver; @@ -30,27 +28,24 @@ class NotificationFlagsType extends AbstractType public function buildForm(FormBuilderInterface $builder, array $options): void { - $builder->setDataMapper(new NotificationFlagDataMapper($this->notificationFlagProviders)); - foreach ($this->notificationFlagProviders as $flagProvider) { $flag = $flagProvider->getFlag(); - $builder->add($flag, FormType::class, [ + $flagBuilder = $builder->create($flag, options: [ 'label' => $flagProvider->getLabel(), - 'required' => false, + 'compound' => true, ]); - $builder->get($flag) + $flagBuilder ->add('immediate_email', CheckboxType::class, [ 'label' => false, 'required' => false, - 'mapped' => false, ]) - ->add('daily_email', CheckboxType::class, [ + ->add('daily_digest', CheckboxType::class, [ 'label' => false, 'required' => false, - 'mapped' => false, ]) ; + $builder->add($flagBuilder); } } @@ -58,6 +53,7 @@ class NotificationFlagsType extends AbstractType { $resolver->setDefaults([ 'data_class' => null, + 'compound' => true, ]); } } diff --git a/src/Bundle/ChillMainBundle/Form/UserProfileType.php b/src/Bundle/ChillMainBundle/Form/UpdateProfileType.php similarity index 75% rename from src/Bundle/ChillMainBundle/Form/UserProfileType.php rename to src/Bundle/ChillMainBundle/Form/UpdateProfileType.php index f9fa65991..6aa8f0943 100644 --- a/src/Bundle/ChillMainBundle/Form/UserProfileType.php +++ b/src/Bundle/ChillMainBundle/Form/UpdateProfileType.php @@ -11,31 +11,29 @@ declare(strict_types=1); namespace Chill\MainBundle\Form; +use Chill\MainBundle\Action\User\UpdateProfile\UpdateProfileCommand; use Chill\MainBundle\Form\Type\ChillPhoneNumberType; use Chill\MainBundle\Form\Type\NotificationFlagsType; use Symfony\Component\Form\AbstractType; use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\OptionsResolver\OptionsResolver; -class UserProfileType extends AbstractType +class UpdateProfileType extends AbstractType { - public function buildForm(FormBuilderInterface $builder, array $options) + public function buildForm(FormBuilderInterface $builder, array $options): void { $builder ->add('phonenumber', ChillPhoneNumberType::class, [ 'required' => false, ]) - ->add('notificationFlags', NotificationFlagsType::class, [ - 'label' => false, - 'mapped' => false, - ]) + ->add('notificationFlags', NotificationFlagsType::class) ; } - public function configureOptions(OptionsResolver $resolver) + public function configureOptions(OptionsResolver $resolver): void { $resolver->setDefaults([ - 'data_class' => \Chill\MainBundle\Entity\User::class, + 'data_class' => UpdateProfileCommand::class, ]); } } diff --git a/src/Bundle/ChillMainBundle/Resources/views/User/profile.html.twig b/src/Bundle/ChillMainBundle/Resources/views/User/profile.html.twig index d25f6645f..0b268af03 100644 --- a/src/Bundle/ChillMainBundle/Resources/views/User/profile.html.twig +++ b/src/Bundle/ChillMainBundle/Resources/views/User/profile.html.twig @@ -64,7 +64,7 @@ {{ form_widget(flag.immediate_email, {'label_attr': { 'class': 'checkbox-inline checkbox-switch'}}) }} - {{ form_widget(flag.daily_email, {'label_attr': { 'class': 'checkbox-inline checkbox-switch'}}) }} + {{ form_widget(flag.daily_digest, {'label_attr': { 'class': 'checkbox-inline checkbox-switch'}}) }} {% endfor %} diff --git a/src/Bundle/ChillMainBundle/Tests/Action/User/UpdateProfile/UpdateProfileCommandHandlerTest.php b/src/Bundle/ChillMainBundle/Tests/Action/User/UpdateProfile/UpdateProfileCommandHandlerTest.php new file mode 100644 index 000000000..e21ebbdb2 --- /dev/null +++ b/src/Bundle/ChillMainBundle/Tests/Action/User/UpdateProfile/UpdateProfileCommandHandlerTest.php @@ -0,0 +1,85 @@ +setNotificationImmediately($flag, true); + $user->setNotificationDailyDigest($flag, true); + + $command = new UpdateProfileCommand(null); + $command->notificationFlags = [ + $flag => [ + 'immediate_email' => false, + 'daily_digest' => false, + ], + ]; + + (new UpdateProfileCommandHandler())->updateProfile($user, $command); + + self::assertNull($user->getPhonenumber(), 'Phone should be set to null'); + self::assertFalse($user->isNotificationSendImmediately($flag)); + self::assertFalse($user->isNotificationDailyDigest($flag)); + } + + public function testUpdateProfileWithPhoneAndMultipleFlags(): void + { + $user = new User(); + + $phone = new PhoneNumber(); + $phone->setCountryCode(33); // France + $phone->setNationalNumber(612345678); + + $command = new UpdateProfileCommand($phone); + $command->notificationFlags = [ + 'reports' => [ + 'immediate_email' => true, + 'daily_digest' => false, + ], + 'activities' => [ + 'immediate_email' => false, + 'daily_digest' => true, + ], + ]; + + (new UpdateProfileCommandHandler())->updateProfile($user, $command); + + // Phone assigned + self::assertInstanceOf(PhoneNumber::class, $user->getPhonenumber()); + self::assertSame(33, $user->getPhonenumber()->getCountryCode()); + self::assertSame('612345678', (string) $user->getPhonenumber()->getNationalNumber()); + + // Flags applied + self::assertTrue($user->isNotificationSendImmediately('reports')); + self::assertFalse($user->isNotificationDailyDigest('reports')); + + self::assertFalse($user->isNotificationSendImmediately('activities')); + self::assertTrue($user->isNotificationDailyDigest('activities')); + } +} diff --git a/src/Bundle/ChillMainBundle/Tests/Action/User/UpdateProfile/UpdateProfileCommandTest.php b/src/Bundle/ChillMainBundle/Tests/Action/User/UpdateProfile/UpdateProfileCommandTest.php new file mode 100644 index 000000000..570c5bb05 --- /dev/null +++ b/src/Bundle/ChillMainBundle/Tests/Action/User/UpdateProfile/UpdateProfileCommandTest.php @@ -0,0 +1,103 @@ +setCountryCode(32); // Belgium + $phone->setNationalNumber(471234567); + $user->setPhonenumber($phone); + + // configure notification flags on the user via helpers + $flagA = 'foo'; + $flagB = 'bar'; + + // For tickets: immediate true, daily false + $user->setNotificationImmediately($flagA, true); + $user->setNotificationDailyDigest($flagA, false); + + // For reports: immediate false, daily true + $user->setNotificationImmediately($flagB, false); + $user->setNotificationDailyDigest($flagB, true); + + // a third flag not explicitly set to validate default behavior from User + $flagC = 'foobar'; // by default immediate-email is true, daily-digest is false per User::getNotificationFlagData + + $manager = $this->createNotificationFlagManager([$flagA, $flagB, $flagC]); + + $command = UpdateProfileCommand::create($user, $manager); + + // phone number transferred + self::assertInstanceOf(PhoneNumber::class, $command->phonenumber); + self::assertSame($phone->getCountryCode(), $command->phonenumber->getCountryCode()); + self::assertSame($phone->getNationalNumber(), $command->phonenumber->getNationalNumber()); + + // flags transferred consistently + self::assertArrayHasKey($flagA, $command->notificationFlags); + self::assertArrayHasKey($flagB, $command->notificationFlags); + self::assertArrayHasKey($flagC, $command->notificationFlags); + + self::assertSame([ + 'immediate_email' => true, + 'daily_digest' => false, + ], $command->notificationFlags[$flagA]); + + self::assertSame([ + 'immediate_email' => false, + 'daily_digest' => true, + ], $command->notificationFlags[$flagB]); + + // default from User::getNotificationFlagData -> immediate true, daily false + self::assertSame([ + 'immediate_email' => true, + 'daily_digest' => false, + ], $command->notificationFlags[$flagC]); + } + + private function createNotificationFlagManager(array $flags): NotificationFlagManager + { + $providers = array_map(fn (string $flag) => new class ($flag) implements NotificationFlagProviderInterface { + public function __construct(private readonly string $flag) {} + + public function getFlag(): string + { + return $this->flag; + } + + public function getLabel(): TranslatableMessage + { + return new TranslatableMessage($this->flag); + } + }, $flags); + + return new NotificationFlagManager($providers); + } +} diff --git a/src/Bundle/ChillMainBundle/Tests/Entity/NotificationTest.php b/src/Bundle/ChillMainBundle/Tests/Entity/NotificationTest.php index e9e7ff760..20dd3d97c 100644 --- a/src/Bundle/ChillMainBundle/Tests/Entity/NotificationTest.php +++ b/src/Bundle/ChillMainBundle/Tests/Entity/NotificationTest.php @@ -96,11 +96,13 @@ final class NotificationTest extends KernelTestCase $this->assertTrue($user->isNotificationSendImmediately($notification->getType()), 'Should return true when no notification flags are set, by default immediate email'); // immediate-email preference - $user->setNotificationFlags(['test_notification_type' => [User::NOTIF_FLAG_IMMEDIATE_EMAIL, User::NOTIF_FLAG_DAILY_DIGEST]]); + $user->setNotificationImmediately('test_notification_type', true); + $user->setNotificationDailyDigest('test_notification_type', true); $this->assertTrue($user->isNotificationSendImmediately($notification->getType()), 'Should return true when preferences contain immediate-email'); // daily-email preference - $user->setNotificationFlags(['test_notification_type' => [User::NOTIF_FLAG_DAILY_DIGEST]]); + $user->setNotificationDailyDigest('test_notification_type', true); + $user->setNotificationImmediately('test_notification_type', false); $this->assertFalse($user->isNotificationSendImmediately($notification->getType()), 'Should return false when preference is daily-email only'); $this->assertTrue($user->isNotificationDailyDigest($notification->getType()), 'Should return true when preference is daily-email'); diff --git a/src/Bundle/ChillMainBundle/Tests/Entity/UserNotificationFlagsPersistenceTest.php b/src/Bundle/ChillMainBundle/Tests/Entity/UserNotificationFlagsPersistenceTest.php new file mode 100644 index 000000000..eefcd22c3 --- /dev/null +++ b/src/Bundle/ChillMainBundle/Tests/Entity/UserNotificationFlagsPersistenceTest.php @@ -0,0 +1,82 @@ +get('doctrine')->getManager(); + + $user = new User(); + $user->setUsername('user_'.bin2hex(random_bytes(4))); + $user->setLabel('Test User'); + $user->setPassword('secret'); + + // Étape 1: créer et persister l’utilisateur + $em->persist($user); + $em->flush(); + $id = $user->getId(); + self::assertNotNull($id, 'User should have an ID after flush'); + + try { + // Sanity check: par défaut, pas de daily digest pour "alerts" + self::assertFalse($user->isNotificationDailyDigest('alerts')); + + // Étape 2: activer le daily digest -> setNotificationFlagElement réassigne la propriété + $user->setNotificationDailyDigest('alerts', true); + $em->flush(); // persist le changement + $em->clear(); // simule un nouveau cycle de requête + + // Étape 3: recharger depuis la base et vérifier la persistance + /** @var User $reloaded */ + $reloaded = $em->find(User::class, $id); + self::assertNotNull($reloaded); + self::assertTrue( + $reloaded->isNotificationDailyDigest('alerts'), + 'Daily digest flag should be persisted' + ); + + // Étape 4: modifier via setNotificationFlagData (remplacement du tableau) + // Cette méthode doit réassigner la propriété (copie -> réassignation) + $reloaded->setNotificationImmediately('alerts', true); + $reloaded->setNotificationDailyDigest('alerts', false); + $em->flush(); + $em->clear(); + + /** @var User $reloaded2 */ + $reloaded2 = $em->find(User::class, $id); + self::assertNotNull($reloaded2); + + // Le daily digest n’est plus actif, seul immediate-email est présent + self::assertFalse($reloaded2->isNotificationDailyDigest('alerts')); + self::assertTrue($reloaded2->isNotificationSendImmediately('alerts')); + } finally { + // Nettoyage + $managed = $em->find(User::class, $id); + if (null !== $managed) { + $em->remove($managed); + $em->flush(); + } + $em->clear(); + } + } +} diff --git a/src/Bundle/ChillMainBundle/Tests/Entity/UserTest.php b/src/Bundle/ChillMainBundle/Tests/Entity/UserTest.php index 254ced51b..76654e3ef 100644 --- a/src/Bundle/ChillMainBundle/Tests/Entity/UserTest.php +++ b/src/Bundle/ChillMainBundle/Tests/Entity/UserTest.php @@ -99,4 +99,22 @@ class UserTest extends TestCase $user->setAbsenceEnd(null); self::assertFalse($user->isAbsent(), 'Should not be absent if start is null'); } + + public function testSetNotification(): void + { + $user = new User(); + + self::assertTrue($user->isNotificationSendImmediately('dummy')); + self::assertFalse($user->isNotificationDailyDigest('dummy')); + + $user->setNotificationImmediately('dummy', false); + self::assertFalse($user->isNotificationSendImmediately('dummy')); + + $user->setNotificationDailyDigest('dummy', true); + self::assertTrue($user->isNotificationDailyDigest('dummy')); + + $user->setNotificationImmediately('dummy', true); + self::assertTrue($user->isNotificationSendImmediately('dummy')); + self::assertTrue($user->isNotificationDailyDigest('dummy')); + } } diff --git a/src/Bundle/ChillMainBundle/Tests/Notification/Email/NotificationMailerTest.php b/src/Bundle/ChillMainBundle/Tests/Notification/Email/NotificationMailerTest.php index 03be565a2..173cb8a0e 100644 --- a/src/Bundle/ChillMainBundle/Tests/Notification/Email/NotificationMailerTest.php +++ b/src/Bundle/ChillMainBundle/Tests/Notification/Email/NotificationMailerTest.php @@ -144,7 +144,7 @@ class NotificationMailerTest extends TestCase $idProperty->setValue($user, 456); // Set notification flags for the user - $user->setNotificationFlags(['test_notification_type' => [User::NOTIF_FLAG_IMMEDIATE_EMAIL]]); + $user->setNotificationImmediately('test_notification_type', true); $messageBus = $this->createMock(MessageBusInterface::class); $messageBus->expects($this->once()) diff --git a/src/Bundle/ChillMainBundle/config/services.yaml b/src/Bundle/ChillMainBundle/config/services.yaml index 14f0f851a..d8d4a475d 100644 --- a/src/Bundle/ChillMainBundle/config/services.yaml +++ b/src/Bundle/ChillMainBundle/config/services.yaml @@ -113,3 +113,5 @@ services: Chill\MainBundle\Service\EntityInfo\ViewEntityInfoManager: arguments: $vienEntityInfoProviders: !tagged_iterator chill_main.entity_info_provider + + Chill\MainBundle\Action\User\UpdateProfile\UpdateProfileCommandHandler: ~ From 5b90d23367be270349721e0e4ecc131dab9a591e Mon Sep 17 00:00:00 2001 From: LenaertsJ Date: Thu, 11 Sep 2025 11:01:16 +0000 Subject: [PATCH 02/15] =?UTF-8?q?Resolve=20"Dupliquer=20une=20document=20d?= =?UTF-8?q?'une=20=C3=A9valuation=20vers=20une=20autre"=20+=20"D=C3=A9plac?= =?UTF-8?q?er=20un=20document=20vers=20une=20autre=20=C3=A9valuation"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../unreleased/Feature-20250211-142243.yaml | 6 + .../unreleased/Feature-20250403-100311.yaml | 6 + .../unreleased/Feature-20250403-100857.yaml | 6 + .../vuejs/DropFileWidget/DropFileModal.vue | 7 +- .../translations/messages.fr.yml | 2 + .../Resources/views/Form/fields.html.twig | 14 +- ...ompanyingPeriodWorkDuplicateController.php | 6 +- ...kEvaluationDocumentDuplicateController.php | 40 +- ...odWorkEvaluationDocumentMoveController.php | 58 + ...companyingPeriodWorkEvaluationDocument.php | 10 - .../Form/FindAccompanyingPeriodWorkType.php | 2 +- .../PickLinkedAccompanyingPeriodWorkType.php | 9 + .../AccompanyingPeriodWorkSelector.ts | 3 +- .../Resources/public/types.ts | 2 +- .../vuejs/AccompanyingCourseWorkEdit/App.vue | 2 +- .../components/CommentInput.vue | 31 + .../components/DateInputs.vue | 71 ++ .../components/DocumentActions.vue | 51 + .../components/DocumentsList.vue | 361 ++++++ .../components/FormEvaluation.vue | 1008 ++++++----------- .../components/TimeSpentInput.vue | 32 + .../vuejs/AccompanyingCourseWorkEdit/store.js | 85 +- ...ccompanyingCourseWorkEvaluationDocument.ts | 20 + .../AccompanyingPeriodWorkEvaluationItem.vue | 70 ++ .../AccompanyingPeriodWorkEvaluationList.vue | 47 + .../AccompanyingPeriodWorkList.vue | 18 +- .../AccompanyingPeriodWorkSelectorModal.vue | 107 +- .../_details.html.twig | 4 +- .../assign_acpw_duplicate.html.twig | 6 +- ...PeriodWorkEvaluationDocumentDuplicator.php | 14 + .../ChillPersonBundle/chill.api.specs.yaml | 30 + .../translations/messages.fr.yml | 40 +- 32 files changed, 1414 insertions(+), 754 deletions(-) create mode 100644 .changes/unreleased/Feature-20250211-142243.yaml create mode 100644 .changes/unreleased/Feature-20250403-100311.yaml create mode 100644 .changes/unreleased/Feature-20250403-100857.yaml create mode 100644 src/Bundle/ChillPersonBundle/Controller/AccompanyingPeriodWorkEvaluationDocumentMoveController.php create mode 100644 src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourseWorkEdit/components/CommentInput.vue create mode 100644 src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourseWorkEdit/components/DateInputs.vue create mode 100644 src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourseWorkEdit/components/DocumentActions.vue create mode 100644 src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourseWorkEdit/components/DocumentsList.vue create mode 100644 src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourseWorkEdit/components/TimeSpentInput.vue create mode 100644 src/Bundle/ChillPersonBundle/Resources/public/vuejs/_components/AccompanyingPeriodWorkSelector/AccompanyingPeriodWorkEvaluationItem.vue create mode 100644 src/Bundle/ChillPersonBundle/Resources/public/vuejs/_components/AccompanyingPeriodWorkSelector/AccompanyingPeriodWorkEvaluationList.vue diff --git a/.changes/unreleased/Feature-20250211-142243.yaml b/.changes/unreleased/Feature-20250211-142243.yaml new file mode 100644 index 000000000..4f0b25e88 --- /dev/null +++ b/.changes/unreleased/Feature-20250211-142243.yaml @@ -0,0 +1,6 @@ +kind: Feature +body: Allow the merge of two accompanying period works +time: 2025-02-11T14:22:43.134106669+01:00 +custom: + Issue: "359" + SchemaChange: No schema change diff --git a/.changes/unreleased/Feature-20250403-100311.yaml b/.changes/unreleased/Feature-20250403-100311.yaml new file mode 100644 index 000000000..1076efc1a --- /dev/null +++ b/.changes/unreleased/Feature-20250403-100311.yaml @@ -0,0 +1,6 @@ +kind: Feature +body: Duplication of a document to another accompanying period work evaluation +time: 2025-04-03T10:03:11.796736107+02:00 +custom: + Issue: "369" + SchemaChange: No schema change diff --git a/.changes/unreleased/Feature-20250403-100857.yaml b/.changes/unreleased/Feature-20250403-100857.yaml new file mode 100644 index 000000000..113ae8a2a --- /dev/null +++ b/.changes/unreleased/Feature-20250403-100857.yaml @@ -0,0 +1,6 @@ +kind: Feature +body: Fusion of two accompanying period works +time: 2025-04-03T10:08:57.25079018+02:00 +custom: + Issue: "359" + SchemaChange: No schema change diff --git a/src/Bundle/ChillDocStoreBundle/Resources/public/vuejs/DropFileWidget/DropFileModal.vue b/src/Bundle/ChillDocStoreBundle/Resources/public/vuejs/DropFileWidget/DropFileModal.vue index 956b3e859..4ca2d49a1 100644 --- a/src/Bundle/ChillDocStoreBundle/Resources/public/vuejs/DropFileWidget/DropFileModal.vue +++ b/src/Bundle/ChillDocStoreBundle/Resources/public/vuejs/DropFileWidget/DropFileModal.vue @@ -4,6 +4,7 @@ import { StoredObject, StoredObjectVersion } from "../../types"; import DropFileWidget from "ChillDocStoreAssets/vuejs/DropFileWidget/DropFileWidget.vue"; import { computed, reactive } from "vue"; import { useToast } from "vue-toast-notification"; +import { DOCUMENT_REPLACE, DOCUMENT_ADD, trans } from "translator"; interface DropFileConfig { allowRemove: boolean; @@ -75,10 +76,10 @@ function closeModal(): void { @click="openModal" class="btn btn-create" > - Ajouter un document + {{ trans(DOCUMENT_ADD) }} - {% endblock %} -{% block pick_linked_entities_widget %} - -
+{% block pick_linked_entities_widget %} + +
{% endblock %} {% block pick_postal_code_widget %} diff --git a/src/Bundle/ChillPersonBundle/Controller/AccompanyingPeriodWorkDuplicateController.php b/src/Bundle/ChillPersonBundle/Controller/AccompanyingPeriodWorkDuplicateController.php index db890cd4d..43daa9343 100644 --- a/src/Bundle/ChillPersonBundle/Controller/AccompanyingPeriodWorkDuplicateController.php +++ b/src/Bundle/ChillPersonBundle/Controller/AccompanyingPeriodWorkDuplicateController.php @@ -18,6 +18,7 @@ use Chill\PersonBundle\Repository\AccompanyingPeriod\AccompanyingPeriodWorkRepos use Chill\PersonBundle\Service\AccompanyingPeriodWork\AccompanyingPeriodWorkMergeService; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpFoundation\Session\Session; use Symfony\Component\Routing\Annotation\Route; use Symfony\Component\Translation\TranslatableMessage; @@ -32,7 +33,7 @@ class AccompanyingPeriodWorkDuplicateController extends AbstractController * @ParamConverter("accompanyingPeriodWork", options={"id": "acpw_id"}) */ #[Route(path: '{_locale}/person/accompanying-period/work/{id}/assign-duplicate', name: 'chill_person_accompanying_period_work_assign_duplicate')] - public function assignDuplicate(AccompanyingPeriodWork $acpw, Request $request) + public function assignDuplicate(AccompanyingPeriodWork $acpw, Request $request): Response { $accompanyingPeriod = $acpw->getAccompanyingPeriod(); @@ -79,7 +80,7 @@ class AccompanyingPeriodWorkDuplicateController extends AbstractController * @ParamConverter("acpw2", options={"id": "acpw2_id"}) */ #[Route(path: '/{_locale}/person/{acpw1_id}/acpw-duplicate/{acpw2_id}/confirm', name: 'chill_person_acpw_duplicate_confirm')] - public function confirmAction(AccompanyingPeriodWork $acpw1, AccompanyingPeriodWork $acpw2, Request $request) + public function confirmAction(AccompanyingPeriodWork $acpw1, AccompanyingPeriodWork $acpw2, Request $request): Response { $accompanyingPeriod = $acpw1->getAccompanyingPeriod(); @@ -98,6 +99,7 @@ class AccompanyingPeriodWorkDuplicateController extends AbstractController $session->getFlashBag()->add('success', new TranslatableMessage('acpw_duplicate.Successfully merged')); } + return $this->redirectToRoute('chill_person_accompanying_period_work_show', ['id' => $acpw1->getId()]); } diff --git a/src/Bundle/ChillPersonBundle/Controller/AccompanyingPeriodWorkEvaluationDocumentDuplicateController.php b/src/Bundle/ChillPersonBundle/Controller/AccompanyingPeriodWorkEvaluationDocumentDuplicateController.php index 033533b05..db3045602 100644 --- a/src/Bundle/ChillPersonBundle/Controller/AccompanyingPeriodWorkEvaluationDocumentDuplicateController.php +++ b/src/Bundle/ChillPersonBundle/Controller/AccompanyingPeriodWorkEvaluationDocumentDuplicateController.php @@ -12,6 +12,7 @@ declare(strict_types=1); namespace Chill\PersonBundle\Controller; use Chill\MainBundle\Routing\ChillUrlGeneratorInterface; +use Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodWorkEvaluation; use Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodWorkEvaluationDocument; use Chill\PersonBundle\Security\Authorization\AccompanyingPeriodWorkVoter; use Chill\PersonBundle\Service\AccompanyingPeriodWorkEvaluationDocument\AccompanyingPeriodWorkEvaluationDocumentDuplicator; @@ -24,15 +25,16 @@ use Symfony\Component\Routing\Annotation\Route; use Symfony\Component\Security\Core\Security; use Symfony\Component\Serializer\Normalizer\AbstractNormalizer; use Symfony\Component\Serializer\SerializerInterface; +use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter; -class AccompanyingPeriodWorkEvaluationDocumentDuplicateController +readonly class AccompanyingPeriodWorkEvaluationDocumentDuplicateController { public function __construct( - private readonly AccompanyingPeriodWorkEvaluationDocumentDuplicator $duplicator, - private readonly Security $security, - private readonly SerializerInterface $serializer, - private readonly EntityManagerInterface $entityManager, - private readonly ChillUrlGeneratorInterface $urlGenerator, + private AccompanyingPeriodWorkEvaluationDocumentDuplicator $duplicator, + private Security $security, + private SerializerInterface $serializer, + private EntityManagerInterface $entityManager, + private ChillUrlGeneratorInterface $urlGenerator, ) {} #[Route('/api/1.0/person/accompanying-course-work-evaluation-document/{id}/duplicate', methods: ['POST'])] @@ -56,6 +58,32 @@ class AccompanyingPeriodWorkEvaluationDocumentDuplicateController ); } + /** + * @ParamConverter("document", options={"id": "document_id"}) + * @ParamConverter("evaluation", options={"id": "evaluation_id"}) + */ + #[Route('/api/1.0/person/accompanying-course-work-evaluation-document/{document_id}/evaluation/{evaluation_id}/duplicate', methods: ['POST'])] + public function duplicateToEvaluationApi(AccompanyingPeriodWorkEvaluationDocument $document, AccompanyingPeriodWorkEvaluation $evaluation): Response + { + $work = $evaluation->getAccompanyingPeriodWork(); + + if (!$this->security->isGranted(AccompanyingPeriodWorkVoter::UPDATE, $work)) { + throw new AccessDeniedHttpException('not allowed to edit this accompanying period work'); + } + + $duplicatedDocument = $this->duplicator->duplicateToEvaluation($document, $evaluation); + + $this->entityManager->persist($duplicatedDocument); + $this->entityManager->persist($duplicatedDocument->getStoredObject()); + $this->entityManager->persist($evaluation); + $this->entityManager->flush(); + + return new JsonResponse( + $this->serializer->serialize($duplicatedDocument, 'json', [AbstractNormalizer::GROUPS => ['read']]), + json: true + ); + } + #[Route('/{_locale}/person/accompanying-course-work-evaluation-document/{id}/duplicate', name: 'chill_person_accompanying_period_work_evaluation_document_duplicate', methods: ['POST'])] public function duplicate(AccompanyingPeriodWorkEvaluationDocument $document): Response { diff --git a/src/Bundle/ChillPersonBundle/Controller/AccompanyingPeriodWorkEvaluationDocumentMoveController.php b/src/Bundle/ChillPersonBundle/Controller/AccompanyingPeriodWorkEvaluationDocumentMoveController.php new file mode 100644 index 000000000..5e777f3e0 --- /dev/null +++ b/src/Bundle/ChillPersonBundle/Controller/AccompanyingPeriodWorkEvaluationDocumentMoveController.php @@ -0,0 +1,58 @@ +getAccompanyingPeriodWork(); + + if (!$this->security->isGranted(AccompanyingPeriodWorkVoter::UPDATE, $work)) { + throw new AccessDeniedHttpException('not allowed to edit this accompanying period work'); + } + + $document->setAccompanyingPeriodWorkEvaluation($evaluation); + + $this->entityManager->persist($document); + $this->entityManager->flush(); + + return new JsonResponse( + $this->serializer->serialize($document, 'json', [AbstractNormalizer::GROUPS => ['read']]), + json: true + ); + } +} diff --git a/src/Bundle/ChillPersonBundle/Entity/AccompanyingPeriod/AccompanyingPeriodWorkEvaluationDocument.php b/src/Bundle/ChillPersonBundle/Entity/AccompanyingPeriod/AccompanyingPeriodWorkEvaluationDocument.php index 01c3ed69d..10329c400 100644 --- a/src/Bundle/ChillPersonBundle/Entity/AccompanyingPeriod/AccompanyingPeriodWorkEvaluationDocument.php +++ b/src/Bundle/ChillPersonBundle/Entity/AccompanyingPeriod/AccompanyingPeriodWorkEvaluationDocument.php @@ -98,16 +98,6 @@ class AccompanyingPeriodWorkEvaluationDocument implements \Chill\MainBundle\Doct public function setAccompanyingPeriodWorkEvaluation(?AccompanyingPeriodWorkEvaluation $accompanyingPeriodWorkEvaluation): AccompanyingPeriodWorkEvaluationDocument { - // if an evaluation is already associated, we cannot change the association (removing the association, - // by setting a null value, is allowed. - if ( - $this->accompanyingPeriodWorkEvaluation instanceof AccompanyingPeriodWorkEvaluation - && $accompanyingPeriodWorkEvaluation instanceof AccompanyingPeriodWorkEvaluation - ) { - if ($this->accompanyingPeriodWorkEvaluation !== $accompanyingPeriodWorkEvaluation) { - throw new \RuntimeException('It is not allowed to change the evaluation for a document'); - } - } $this->accompanyingPeriodWorkEvaluation = $accompanyingPeriodWorkEvaluation; return $this; diff --git a/src/Bundle/ChillPersonBundle/Form/FindAccompanyingPeriodWorkType.php b/src/Bundle/ChillPersonBundle/Form/FindAccompanyingPeriodWorkType.php index ab78cb541..9b3acf156 100644 --- a/src/Bundle/ChillPersonBundle/Form/FindAccompanyingPeriodWorkType.php +++ b/src/Bundle/ChillPersonBundle/Form/FindAccompanyingPeriodWorkType.php @@ -24,7 +24,7 @@ class FindAccompanyingPeriodWorkType extends AbstractType { $builder ->add('acpw', PickLinkedAccompanyingPeriodWorkType::class, [ - 'label' => 'Social action', + 'label' => 'Accompanying period work', 'multiple' => false, 'accompanyingPeriod' => $options['accompanyingPeriod'], ]) diff --git a/src/Bundle/ChillPersonBundle/Form/Type/PickLinkedAccompanyingPeriodWorkType.php b/src/Bundle/ChillPersonBundle/Form/Type/PickLinkedAccompanyingPeriodWorkType.php index 55eda49e6..6f8fc7665 100644 --- a/src/Bundle/ChillPersonBundle/Form/Type/PickLinkedAccompanyingPeriodWorkType.php +++ b/src/Bundle/ChillPersonBundle/Form/Type/PickLinkedAccompanyingPeriodWorkType.php @@ -16,18 +16,26 @@ use Symfony\Component\Form\AbstractType; use Symfony\Component\Form\FormInterface; use Symfony\Component\Form\FormView; use Symfony\Component\OptionsResolver\OptionsResolver; +use Symfony\Component\Serializer\Normalizer\NormalizerInterface; class PickLinkedAccompanyingPeriodWorkType extends AbstractType { + public function __construct(private readonly NormalizerInterface $normalizer) {} + public function buildView(FormView $view, FormInterface $form, array $options) { $view->vars['multiple'] = $options['multiple']; $view->vars['types'] = ['acpw']; $view->vars['uniqid'] = uniqid('pick_acpw_dyn'); + $view->vars['suggested'] = []; $view->vars['as_id'] = true === $options['as_id'] ? '1' : '0'; $view->vars['submit_on_adding_new_entity'] = false; $view->vars['pick-entities-type'] = 'acpw'; $view->vars['attr']['data-accompanying-period-id'] = $options['accompanyingPeriod']->getId(); + + foreach ($options['suggested'] as $suggestion) { + $view->vars['suggested'][] = $this->normalizer->normalize($suggestion, 'json', ['groups' => 'read']); + } } public function configureOptions(OptionsResolver $resolver) @@ -38,6 +46,7 @@ class PickLinkedAccompanyingPeriodWorkType extends AbstractType ->setDefault('multiple', false) ->setAllowedTypes('multiple', ['bool']) ->setDefault('compound', false) + ->setDefault('suggested', []) ->setDefault('as_id', false) ->setAllowedTypes('as_id', ['bool']) ->setDefault('submit_on_adding_new_entity', false) diff --git a/src/Bundle/ChillPersonBundle/Resources/public/mod/DuplicateSelector/AccompanyingPeriodWorkSelector.ts b/src/Bundle/ChillPersonBundle/Resources/public/mod/DuplicateSelector/AccompanyingPeriodWorkSelector.ts index 8f117d6ef..8e955070a 100644 --- a/src/Bundle/ChillPersonBundle/Resources/public/mod/DuplicateSelector/AccompanyingPeriodWorkSelector.ts +++ b/src/Bundle/ChillPersonBundle/Resources/public/mod/DuplicateSelector/AccompanyingPeriodWorkSelector.ts @@ -41,7 +41,8 @@ document.addEventListener("DOMContentLoaded", () => { methods: { pickWork: function (payload: { work: AccompanyingPeriodWork }) { console.log("payload", payload); - input.value = payload.work.id.toString(); + + input.value = payload.work.id?.toString() ?? ""; }, }, }); diff --git a/src/Bundle/ChillPersonBundle/Resources/public/types.ts b/src/Bundle/ChillPersonBundle/Resources/public/types.ts index 7d641c859..13a9fe5c9 100644 --- a/src/Bundle/ChillPersonBundle/Resources/public/types.ts +++ b/src/Bundle/ChillPersonBundle/Resources/public/types.ts @@ -84,7 +84,7 @@ export interface AccompanyingPeriodWorkEvaluationDocument { } export interface AccompanyingPeriodWork { - id: number; + id?: number; accompanyingPeriod?: AccompanyingPeriod; accompanyingPeriodWorkEvaluations: AccompanyingPeriodWorkEvaluation[]; createdAt?: string; diff --git a/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourseWorkEdit/App.vue b/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourseWorkEdit/App.vue index f3404e2de..bf403291e 100644 --- a/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourseWorkEdit/App.vue +++ b/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourseWorkEdit/App.vue @@ -972,7 +972,7 @@ div#workEditor { font-size: 85%; } - i.fa { + & > i.fa { padding: 0.25rem; color: $white; diff --git a/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourseWorkEdit/components/CommentInput.vue b/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourseWorkEdit/components/CommentInput.vue new file mode 100644 index 000000000..84a27a5ed --- /dev/null +++ b/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourseWorkEdit/components/CommentInput.vue @@ -0,0 +1,31 @@ + + + diff --git a/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourseWorkEdit/components/DateInputs.vue b/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourseWorkEdit/components/DateInputs.vue new file mode 100644 index 000000000..ac16b467f --- /dev/null +++ b/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourseWorkEdit/components/DateInputs.vue @@ -0,0 +1,71 @@ + + + diff --git a/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourseWorkEdit/components/DocumentActions.vue b/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourseWorkEdit/components/DocumentActions.vue new file mode 100644 index 000000000..592e9a7fd --- /dev/null +++ b/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourseWorkEdit/components/DocumentActions.vue @@ -0,0 +1,51 @@ + + + + + diff --git a/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourseWorkEdit/components/DocumentsList.vue b/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourseWorkEdit/components/DocumentsList.vue new file mode 100644 index 000000000..4132b388e --- /dev/null +++ b/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourseWorkEdit/components/DocumentsList.vue @@ -0,0 +1,361 @@ + + + diff --git a/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourseWorkEdit/components/FormEvaluation.vue b/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourseWorkEdit/components/FormEvaluation.vue index b54baf09a..0a39150bf 100644 --- a/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourseWorkEdit/components/FormEvaluation.vue +++ b/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourseWorkEdit/components/FormEvaluation.vue @@ -1,699 +1,344 @@ - diff --git a/src/Bundle/ChillPersonBundle/Resources/public/vuejs/_components/AccompanyingPeriodWorkSelector/AccompanyingPeriodWorkList.vue b/src/Bundle/ChillPersonBundle/Resources/public/vuejs/_components/AccompanyingPeriodWorkSelector/AccompanyingPeriodWorkList.vue index b2ebc5f1c..02f26656e 100644 --- a/src/Bundle/ChillPersonBundle/Resources/public/vuejs/_components/AccompanyingPeriodWorkSelector/AccompanyingPeriodWorkList.vue +++ b/src/Bundle/ChillPersonBundle/Resources/public/vuejs/_components/AccompanyingPeriodWorkSelector/AccompanyingPeriodWorkList.vue @@ -26,14 +26,24 @@ import AccompanyingPeriodWorkItem from "./AccompanyingPeriodWorkItem.vue"; import { AccompanyingPeriodWork } from "../../../types"; import { defineProps, ref, watch } from "vue"; -// eslint-disable-next-line @typescript-eslint/no-unused-vars const props = defineProps<{ accompanyingPeriodWorks: AccompanyingPeriodWork[]; + selectedAcpw?: AccompanyingPeriodWork | null; }>(); -const selectedAcpw = ref(null); +const selectedAcpw = ref( + props.selectedAcpw ?? null, +); -// eslint-disable-next-line vue/valid-define-emits -const emit = defineEmits(); +const emit = defineEmits<{ + "update:selectedAcpw": [value: AccompanyingPeriodWork | null]; +}>(); + +watch( + () => props.selectedAcpw, + (val) => { + selectedAcpw.value = val ?? null; + }, +); watch(selectedAcpw, (newValue) => { emit("update:selectedAcpw", newValue); diff --git a/src/Bundle/ChillPersonBundle/Resources/public/vuejs/_components/AccompanyingPeriodWorkSelector/AccompanyingPeriodWorkSelectorModal.vue b/src/Bundle/ChillPersonBundle/Resources/public/vuejs/_components/AccompanyingPeriodWorkSelector/AccompanyingPeriodWorkSelectorModal.vue index 4b4f3ddba..254595cb3 100644 --- a/src/Bundle/ChillPersonBundle/Resources/public/vuejs/_components/AccompanyingPeriodWorkSelector/AccompanyingPeriodWorkSelectorModal.vue +++ b/src/Bundle/ChillPersonBundle/Resources/public/vuejs/_components/AccompanyingPeriodWorkSelector/AccompanyingPeriodWorkSelectorModal.vue @@ -1,6 +1,6 @@