diff --git a/src/Bundle/ChillDocStoreBundle/Workflow/AccompanyingCourseDocumentWorkflowHandler.php b/src/Bundle/ChillDocStoreBundle/Workflow/AccompanyingCourseDocumentWorkflowHandler.php index a615ddda2..33fba8d3f 100644 --- a/src/Bundle/ChillDocStoreBundle/Workflow/AccompanyingCourseDocumentWorkflowHandler.php +++ b/src/Bundle/ChillDocStoreBundle/Workflow/AccompanyingCourseDocumentWorkflowHandler.php @@ -14,19 +14,46 @@ namespace Chill\DocStoreBundle\Workflow; use Chill\DocStoreBundle\Entity\AccompanyingCourseDocument; use Chill\MainBundle\Entity\Workflow\EntityWorkflow; use Chill\MainBundle\Workflow\EntityWorkflowHandlerInterface; +use Chill\PersonBundle\Entity\AccompanyingPeriodParticipation; use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\EntityRepository; +use Symfony\Contracts\Translation\TranslatorInterface; class AccompanyingCourseDocumentWorkflowHandler implements EntityWorkflowHandlerInterface { private EntityRepository $repository; + private TranslatorInterface $translator; + /** * TODO: injecter le repository directement. */ - public function __construct(EntityManagerInterface $em) + public function __construct(EntityManagerInterface $em, TranslatorInterface $translator) { $this->repository = $em->getRepository(AccompanyingCourseDocument::class); + $this->translator = $translator; + } + + public function getEntityData(EntityWorkflow $entityWorkflow, array $options = []): array + { + $course = $this->getRelatedEntity($entityWorkflow) + ->getCourse(); + $persons = []; + + if (null !== $course) { + $persons = $course->getCurrentParticipations()->map(static function (AccompanyingPeriodParticipation $participation) { + return $participation->getPerson(); + })->toArray(); + } + + return [ + 'persons' => array_values($persons), + ]; + } + + public function getEntityTitle(EntityWorkflow $entityWorkflow, array $options = []): string + { + return $this->translator->trans('workflow.Document (n°%doc%)', ['%doc%' => $entityWorkflow->getRelatedEntityId()]); } public function getRelatedEntity(EntityWorkflow $entityWorkflow): ?AccompanyingCourseDocument diff --git a/src/Bundle/ChillMainBundle/Controller/WorkflowApiController.php b/src/Bundle/ChillMainBundle/Controller/WorkflowApiController.php index dda787708..afb85658b 100644 --- a/src/Bundle/ChillMainBundle/Controller/WorkflowApiController.php +++ b/src/Bundle/ChillMainBundle/Controller/WorkflowApiController.php @@ -11,7 +11,12 @@ declare(strict_types=1); namespace Chill\MainBundle\Controller; +use Chill\MainBundle\Entity\User; use Chill\MainBundle\Entity\Workflow\EntityWorkflow; +use Chill\MainBundle\Pagination\PaginatorFactory; +use Chill\MainBundle\Repository\Workflow\EntityWorkflowRepository; +use Chill\MainBundle\Serializer\Model\Collection; +use Chill\MainBundle\Serializer\Model\Counter; use Doctrine\ORM\EntityManagerInterface; use LogicException; use Symfony\Component\HttpFoundation\JsonResponse; @@ -21,17 +26,71 @@ use Symfony\Component\HttpKernel\Exception\BadRequestHttpException; use Symfony\Component\Routing\Annotation\Route; use Symfony\Component\Security\Core\Exception\AccessDeniedException; use Symfony\Component\Security\Core\Security; +use Symfony\Component\Serializer\SerializerInterface; class WorkflowApiController { private EntityManagerInterface $entityManager; + private EntityWorkflowRepository $entityWorkflowRepository; + + private PaginatorFactory $paginatorFactory; + private Security $security; - public function __construct(Security $security, EntityManagerInterface $entityManager) - { + private SerializerInterface $serializer; + + public function __construct( + EntityManagerInterface $entityManager, + EntityWorkflowRepository $entityWorkflowRepository, + PaginatorFactory $paginatorFactory, + Security $security, + SerializerInterface $serializer + ) { $this->entityManager = $entityManager; + $this->entityWorkflowRepository = $entityWorkflowRepository; + $this->paginatorFactory = $paginatorFactory; $this->security = $security; + $this->serializer = $serializer; + } + + /** + * Return a list of workflow which are waiting an action for the user. + * + * @Route("/api/1.0/main/workflow/my", methods={"GET"}) + */ + public function myWorkflow(Request $request): JsonResponse + { + if (!$this->security->isGranted('ROLE_USER') || !$this->security->getUser() instanceof User) { + throw new AccessDeniedException(); + } + + $total = $this->entityWorkflowRepository->countByDest($this->security->getUser()); + + if ($request->query->getBoolean('countOnly', false)) { + return new JsonResponse( + $this->serializer->serialize(new Counter($total), 'json'), + JsonResponse::HTTP_OK, + [], + true + ); + } + + $paginator = $this->paginatorFactory->create($total); + + $workflows = $this->entityWorkflowRepository->findByDest( + $this->security->getUser(), + ['id' => 'DESC'], + $paginator->getItemsPerPage(), + $paginator->getCurrentPageFirstItemNumber() + ); + + return new JsonResponse( + $this->serializer->serialize(new Collection($workflows, $paginator), 'json', ['groups' => ['read']]), + JsonResponse::HTTP_OK, + [], + true + ); } /** diff --git a/src/Bundle/ChillMainBundle/Resources/public/vuejs/HomepageWidget/App.vue b/src/Bundle/ChillMainBundle/Resources/public/vuejs/HomepageWidget/App.vue index da3b9ba71..343b638da 100644 --- a/src/Bundle/ChillMainBundle/Resources/public/vuejs/HomepageWidget/App.vue +++ b/src/Bundle/ChillMainBundle/Resources/public/vuejs/HomepageWidget/App.vue @@ -1,7 +1,7 @@ @@ -85,6 +95,7 @@ import MyEvaluations from './MyEvaluations'; import MyTasks from './MyTasks'; import MyAccompanyingCourses from './MyAccompanyingCourses'; import MyNotifications from './MyNotifications'; +import MyWorkflows from './MyWorkflows.vue'; import TabCounter from './TabCounter'; import { mapState } from "vuex"; @@ -95,6 +106,7 @@ export default { MyWorks, MyEvaluations, MyTasks, + MyWorkflows, MyAccompanyingCourses, MyNotifications, TabCounter, @@ -126,6 +138,7 @@ export default { 'MyWorks', 'MyEvaluations', 'MyTasks', + 'MyWorkflows', ]) { this.$store.dispatch('getByTab', { tab: m, param: "countOnly=1" }); } diff --git a/src/Bundle/ChillMainBundle/Resources/public/vuejs/HomepageWidget/MyNotifications.vue b/src/Bundle/ChillMainBundle/Resources/public/vuejs/HomepageWidget/MyNotifications.vue index 897ea3a9a..06683f8fc 100644 --- a/src/Bundle/ChillMainBundle/Resources/public/vuejs/HomepageWidget/MyNotifications.vue +++ b/src/Bundle/ChillMainBundle/Resources/public/vuejs/HomepageWidget/MyNotifications.vue @@ -66,6 +66,8 @@ export default { return appMessages.fr.the_activity; case 'Chill\\PersonBundle\\Entity\\AccompanyingPeriod': return appMessages.fr.the_course; + case 'Chill\\MainBundle\\Entity\\Workflow\\EntityWorkflow': + return appMessages.fr.the_workflow; default: throw 'notification type unknown'; } @@ -76,6 +78,8 @@ export default { return `/fr/activity/${n.relatedEntityId}/show` case 'Chill\\PersonBundle\\Entity\\AccompanyingPeriod': return `/fr/parcours/${n.relatedEntityId}` + case 'Chill\\MainBundle\\Entity\\Workflow\\EntityWorkflow': + return `/fr/main/workflow/${n.relatedEntityId}/show` default: throw 'notification type unknown'; } diff --git a/src/Bundle/ChillMainBundle/Resources/public/vuejs/HomepageWidget/MyWorkflows.vue b/src/Bundle/ChillMainBundle/Resources/public/vuejs/HomepageWidget/MyWorkflows.vue new file mode 100644 index 000000000..1272a3c19 --- /dev/null +++ b/src/Bundle/ChillMainBundle/Resources/public/vuejs/HomepageWidget/MyWorkflows.vue @@ -0,0 +1,88 @@ + + + + + diff --git a/src/Bundle/ChillMainBundle/Resources/public/vuejs/HomepageWidget/js/i18n.js b/src/Bundle/ChillMainBundle/Resources/public/vuejs/HomepageWidget/js/i18n.js index 0845d0f02..b5ae78e34 100644 --- a/src/Bundle/ChillMainBundle/Resources/public/vuejs/HomepageWidget/js/i18n.js +++ b/src/Bundle/ChillMainBundle/Resources/public/vuejs/HomepageWidget/js/i18n.js @@ -22,6 +22,10 @@ const appMessages = { tab: "Mes notifications", description: "Liste des notifications reçues et non lues.", }, + my_workflows: { + tab: "Mes workflows", + description: "Liste des workflows en attente d'une action." + }, opening_date: "Date d'ouverture", social_issues: "Problématiques sociales", concerned_persons: "Usagers concernés", @@ -33,12 +37,16 @@ const appMessages = { From: "Expéditeur", Subject: "Objet", Entity: "Associé à", + Step: "Étape", + concerned_users: "Usagers concernés", + Object_workflow: "Objet du workflow", show_entity: "Voir {entity}", the_activity: "l'échange", the_course: "le parcours", the_action: "l'action", the_evaluation: "l'évaluation", the_task: "la tâche", + the_workflow: "le workflow", StartDate: "Date d'ouverture", SocialAction: "Action d'accompagnement", no_data: "Aucun résultats", @@ -61,4 +69,4 @@ Object.assign(appMessages.fr); export { appMessages -}; \ No newline at end of file +}; diff --git a/src/Bundle/ChillMainBundle/Resources/public/vuejs/HomepageWidget/js/store.js b/src/Bundle/ChillMainBundle/Resources/public/vuejs/HomepageWidget/js/store.js index 9507d5085..c33ec2123 100644 --- a/src/Bundle/ChillMainBundle/Resources/public/vuejs/HomepageWidget/js/store.js +++ b/src/Bundle/ChillMainBundle/Resources/public/vuejs/HomepageWidget/js/store.js @@ -1,12 +1,6 @@ import 'es6-promise/auto'; import { createStore } from 'vuex'; import { makeFetch } from "ChillMainAssets/lib/api/apiMethods"; -import MyCustoms from "../MyCustoms"; -import MyWorks from "../MyWorks"; -import MyEvaluations from "../MyEvaluations"; -import MyTasks from "../MyTasks"; -import MyAccompanyingCourses from "../MyAccompanyingCourses"; -import MyNotifications from "../MyNotifications"; const debug = process.env.NODE_ENV !== 'production'; @@ -27,6 +21,7 @@ const store = createStore({ }, accompanyingCourses: {}, notifications: {}, + workflows: {}, errorMsg: [], loading: false }, @@ -49,6 +44,9 @@ const store = createStore({ isNotificationsLoaded(state) { return !isEmpty(state.notifications); }, + isWorkflowsLoaded(state) { + return !isEmpty(state.workflows); + }, counter(state) { return { works: state.works.count, @@ -57,6 +55,7 @@ const store = createStore({ tasksAlert: state.tasks.alert.count, accompanyingCourses: state.accompanyingCourses.count, notifications: state.notifications.count, + workflows: state.workflows.count } } }, @@ -85,6 +84,9 @@ const store = createStore({ //console.log('addNotifications', notifications); state.notifications = notifications; }, + addWorkflows(state, workflows) { + state.workflows = workflows; + }, setLoading(state, bool) { state.loading = bool; }, @@ -180,14 +182,30 @@ const store = createStore({ const url = `/api/1.0/main/notification/my/unread${'?'+ param}`; makeFetch('GET', url) .then((response) => { + console.log('notifications', response) commit('addNotifications', response); commit('setLoading', false); }) .catch((error) => { commit('catchError', error); throw error; - }) - ; + }); + } + break; + case 'MyWorkflows': + if (!getters.isWorflowsLoaded) { + commit('setLoading', true); + const url = '/api/1.0/main/workflow/my'; + makeFetch('GET', url) + .then((response) => { + console.log('workflows', response) + commit('addWorkflows', response); + commit('setLoading', false); + }) + .catch((error) => { + commit('catchError', error); + throw error; + }); } break; default: diff --git a/src/Bundle/ChillMainBundle/Serializer/Normalizer/EntityWorkflowNormalizer.php b/src/Bundle/ChillMainBundle/Serializer/Normalizer/EntityWorkflowNormalizer.php index bc053b3cf..b74520436 100644 --- a/src/Bundle/ChillMainBundle/Serializer/Normalizer/EntityWorkflowNormalizer.php +++ b/src/Bundle/ChillMainBundle/Serializer/Normalizer/EntityWorkflowNormalizer.php @@ -12,6 +12,7 @@ declare(strict_types=1); namespace Chill\MainBundle\Serializer\Normalizer; use Chill\MainBundle\Entity\Workflow\EntityWorkflow; +use Chill\MainBundle\Workflow\EntityWorkflowManager; use Chill\MainBundle\Workflow\Helper\MetadataExtractor; use Symfony\Component\Serializer\Normalizer\NormalizerAwareInterface; use Symfony\Component\Serializer\Normalizer\NormalizerAwareTrait; @@ -22,12 +23,18 @@ class EntityWorkflowNormalizer implements NormalizerInterface, NormalizerAwareIn { use NormalizerAwareTrait; + private EntityWorkflowManager $entityWorkflowManager; + private MetadataExtractor $metadataExtractor; private Registry $registry; - public function __construct(MetadataExtractor $metadataExtractor, Registry $registry) - { + public function __construct( + EntityWorkflowManager $entityWorkflowManager, + MetadataExtractor $metadataExtractor, + Registry $registry + ) { + $this->entityWorkflowManager = $entityWorkflowManager; $this->metadataExtractor = $metadataExtractor; $this->registry = $registry; } @@ -40,6 +47,7 @@ class EntityWorkflowNormalizer implements NormalizerInterface, NormalizerAwareIn public function normalize($object, ?string $format = null, array $context = []) { $workflow = $this->registry->get($object, $object->getWorkflowName()); + $handler = $this->entityWorkflowManager->getHandler($object); return [ 'type' => 'entity_workflow', @@ -49,6 +57,8 @@ class EntityWorkflowNormalizer implements NormalizerInterface, NormalizerAwareIn 'workflow' => $this->metadataExtractor->buildArrayPresentationForWorkflow($workflow), 'currentStep' => $this->normalizer->normalize($object->getCurrentStep(), $format, $context), 'steps' => $this->normalizer->normalize($object->getStepsChained(), $format, $context), + 'datas' => $this->normalizer->normalize($handler->getEntityData($object), $format, $context), + 'title' => $handler->getEntityTitle($object), ]; } diff --git a/src/Bundle/ChillMainBundle/Workflow/EntityWorkflowHandlerInterface.php b/src/Bundle/ChillMainBundle/Workflow/EntityWorkflowHandlerInterface.php index 2e97f77af..551a17747 100644 --- a/src/Bundle/ChillMainBundle/Workflow/EntityWorkflowHandlerInterface.php +++ b/src/Bundle/ChillMainBundle/Workflow/EntityWorkflowHandlerInterface.php @@ -15,6 +15,10 @@ use Chill\MainBundle\Entity\Workflow\EntityWorkflow; interface EntityWorkflowHandlerInterface { + public function getEntityData(EntityWorkflow $entityWorkflow, array $options = []): array; + + public function getEntityTitle(EntityWorkflow $entityWorkflow, array $options = []): string; + public function getRelatedEntity(EntityWorkflow $entityWorkflow): ?object; /** diff --git a/src/Bundle/ChillMainBundle/chill.api.specs.yaml b/src/Bundle/ChillMainBundle/chill.api.specs.yaml index b2d6cb7de..607e7fc73 100644 --- a/src/Bundle/ChillMainBundle/chill.api.specs.yaml +++ b/src/Bundle/ChillMainBundle/chill.api.specs.yaml @@ -125,6 +125,11 @@ components: type: object type: type: string + Workflow: + type: object + properties: + id: + type: integer paths: /1.0/search.json: @@ -795,4 +800,20 @@ paths: application/json: schema: $ref: '#/components/schemas/UserJob' + /1.0/main/workflow/my: + get: + tags: + - workflow + summary: Return a list of workflows awaiting for user's action + responses: + 200: + description: "ok" + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/Workflow' + 403: + description: "Unauthorized" diff --git a/src/Bundle/ChillPersonBundle/Workflow/AccompanyingPeriodWorkEvaluationWorkflowHandler.php b/src/Bundle/ChillPersonBundle/Workflow/AccompanyingPeriodWorkEvaluationWorkflowHandler.php index 46aed46b4..1be9f87bf 100644 --- a/src/Bundle/ChillPersonBundle/Workflow/AccompanyingPeriodWorkEvaluationWorkflowHandler.php +++ b/src/Bundle/ChillPersonBundle/Workflow/AccompanyingPeriodWorkEvaluationWorkflowHandler.php @@ -16,14 +16,32 @@ use Chill\MainBundle\Workflow\EntityWorkflowHandlerInterface; use Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodWorkEvaluation; use Chill\PersonBundle\Repository\AccompanyingPeriod\AccompanyingPeriodWorkEvaluationRepository; use Chill\PersonBundle\Security\Authorization\AccompanyingPeriodWorkEvaluationVoter; +use Symfony\Contracts\Translation\TranslatorInterface; class AccompanyingPeriodWorkEvaluationWorkflowHandler implements EntityWorkflowHandlerInterface { private AccompanyingPeriodWorkEvaluationRepository $repository; - public function __construct(AccompanyingPeriodWorkEvaluationRepository $repository) + private TranslatorInterface $translator; + + public function __construct(AccompanyingPeriodWorkEvaluationRepository $repository, TranslatorInterface $translator) { $this->repository = $repository; + $this->translator = $translator; + } + + public function getEntityData(EntityWorkflow $entityWorkflow, array $options = []): array + { + $evaluation = $this->getRelatedEntity($entityWorkflow); + + return [ + 'persons' => $evaluation->getAccompanyingPeriodWork()->getPersons(), + ]; + } + + public function getEntityTitle(EntityWorkflow $entityWorkflow, array $options = []): string + { + return $this->translator->trans('workflow.Evaluation (n°%eval%)', ['%eval%' => $entityWorkflow->getRelatedEntityId()]); } public function getRelatedEntity(EntityWorkflow $entityWorkflow): ?AccompanyingPeriodWorkEvaluation diff --git a/src/Bundle/ChillPersonBundle/Workflow/AccompanyingPeriodWorkWorkflowHandler.php b/src/Bundle/ChillPersonBundle/Workflow/AccompanyingPeriodWorkWorkflowHandler.php index cdb70f49c..3b6a1e9aa 100644 --- a/src/Bundle/ChillPersonBundle/Workflow/AccompanyingPeriodWorkWorkflowHandler.php +++ b/src/Bundle/ChillPersonBundle/Workflow/AccompanyingPeriodWorkWorkflowHandler.php @@ -15,17 +15,34 @@ use Chill\MainBundle\Entity\Workflow\EntityWorkflow; use Chill\MainBundle\Workflow\EntityWorkflowHandlerInterface; use Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodWork; use Chill\PersonBundle\Repository\AccompanyingPeriod\AccompanyingPeriodWorkRepository; +use Symfony\Contracts\Translation\TranslatorInterface; class AccompanyingPeriodWorkWorkflowHandler implements EntityWorkflowHandlerInterface { private AccompanyingPeriodWorkRepository $repository; - public function __construct(AccompanyingPeriodWorkRepository $repository) + private TranslatorInterface $translator; + + public function __construct(AccompanyingPeriodWorkRepository $repository, TranslatorInterface $translator) { $this->repository = $repository; + $this->translator = $translator; } - public function getRelatedEntity(EntityWorkflow $entityWorkflow): ?object + public function getEntityData(EntityWorkflow $entityWorkflow, array $options = []): array + { + return [ + 'persons' => $this->getRelatedEntity($entityWorkflow) + ->getPersons(), + ]; + } + + public function getEntityTitle(EntityWorkflow $entityWorkflow, array $options = []): string + { + return $this->translator->trans('workflow.Work (n°%w%)', ['%w%' => $entityWorkflow->getRelatedEntityId()]); + } + + public function getRelatedEntity(EntityWorkflow $entityWorkflow): ?AccompanyingPeriodWork { return $this->repository->find($entityWorkflow->getRelatedEntityId()); }