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 84d5fea0b..56d53be92 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}/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 @@