diff --git a/changelog.md b/changelog.md
index 50114c7..ce60c61 100644
--- a/changelog.md
+++ b/changelog.md
@@ -63,6 +63,9 @@
### Next
+#### Feature
+- Add tasks
+
#### Improve
- Move columns in activity table
- Reditect to activity after creating one
\ No newline at end of file
diff --git a/config/packages/mapping/TaskEntity.orm.xml b/config/packages/mapping/TaskEntity.orm.xml
new file mode 100644
index 0000000..a4a58f5
--- /dev/null
+++ b/config/packages/mapping/TaskEntity.orm.xml
@@ -0,0 +1,15 @@
+
', $errors),
+ 'code' => Response::HTTP_BAD_REQUEST,
+ ]);
+ }
+ }
+
// Update fields
$activityEditForm->updateFields($activity);
+ // Get tasks
+ $tasks = $taskRepository->findBy([
+ 'activity' => $activity,
+ ]);
+
return $this->render('activity/activity.html.twig', [
'activity' => $activity,
+ 'tasks' => $tasks,
'activityEditForm' => $activityEditForm,
'activityDeleteForm' => $activityDeleteForm,
+ 'taskAddForm' => $taskAddForm,
]);
}
}
\ No newline at end of file
diff --git a/src/Controller/ActorController.php b/src/Controller/ActorController.php
index da4a3eb..d2c414d 100644
--- a/src/Controller/ActorController.php
+++ b/src/Controller/ActorController.php
@@ -5,8 +5,10 @@
use Symfony\Component\HttpFoundation\Response;
use App\Form\Actors\ActorAddForm;
use App\Repository\ActorRepository;
+use App\Repository\TaskRepository;
use App\Entity\ActivityEntity;
use App\Entity\ActorEntity;
+use App\Entity\TaskEntity;
use App\Form\Actors\ActorDeleteForm;
use Symfony\Component\Routing\Generator\UrlGenerator;
use App\Form\Actors\ActorEditForm;
@@ -105,6 +107,8 @@
$actorRepository = $entityManager->getRepository(ActorEntity::class);
/** @var ActivityRepository $activityRepository */
$activityRepository = $entityManager->getRepository(ActivityEntity::class);
+ /** @var TaskRepository $taskRepository */
+ $taskRepository = $entityManager->getRepository(TaskEntity::class);
// Get actor
$actor = $actorRepository->find($id);
@@ -160,9 +164,16 @@
'status' => StatusEnum::ACTIVE_STATUS
]);
+ // Get tasks
+ $tasks = $taskRepository->findBy([
+ 'actor' => $actor,
+ 'status' => StatusEnum::ACTIVE_STATUS,
+ ]);
+
return $this->render('actor/actor.html.twig', [
'actor' => $actor,
'activities' => $activities,
+ 'tasks' => $tasks,
'actorEditForm' => $actorEditForm,
'actorDeleteForm' => $actorDeleteForm,
]);
diff --git a/src/Controller/MainController.php b/src/Controller/MainController.php
index 3c3a050..9c4a944 100644
--- a/src/Controller/MainController.php
+++ b/src/Controller/MainController.php
@@ -2,7 +2,10 @@
namespace App\Controller;
use App\Entity\ActivityEntity;
+use App\Entity\TaskEntity;
+use App\Enum\StatusEnum;
use App\Repository\ActivityRepository;
+use App\Repository\TaskRepository;
use Symfony\Component\HttpFoundation\Response;
/**
@@ -22,12 +25,20 @@
$entityManager = $this->getDoctrine()->getManager();
/** @var ActivityRepository $activityRepository */
$activityRepository = $entityManager->getRepository(ActivityEntity::class);
+ /** @var TaskRepository $taskRepository */
+ $taskRepository = $entityManager->getRepository(TaskEntity::class);
// Get activities
$activities = $activityRepository->findActiveFollowed();
+ // Get tasks
+ $tasks = $taskRepository->findBy([
+ 'status' => StatusEnum::ACTIVE_STATUS,
+ ]);
+
return $this->render('home.html.twig', [
- 'activities' => $activities
+ 'activities' => $activities,
+ 'tasks' => $tasks,
]);
}
}
\ No newline at end of file
diff --git a/src/Controller/TaskController.php b/src/Controller/TaskController.php
new file mode 100644
index 0000000..398d841
--- /dev/null
+++ b/src/Controller/TaskController.php
@@ -0,0 +1,177 @@
+getDoctrine()->getManager();
+ /** @var ActorRepository $actorRepository */
+ $actorRepository = $entityManager->getRepository(ActorEntity::class);
+ /** @var TaskRepository $taskRepository */
+ $taskRepository = $entityManager->getRepository(TaskEntity::class);
+
+ // Get actors
+ $actors = $actorRepository->findAll();
+
+ // Task add form
+ /** @var TaskAddForm $taskAddForm */
+ $taskAddForm = $this->createNamedCustomForm('taskAdd', TaskAddForm::class, [
+ 'actors' => $actors,
+ ]);
+ $taskAddForm->handleRequest($request);
+ if ($taskAddForm->isSubmitted() && $taskAddForm->isValid()) {
+ $errors = $taskAddForm->validate();
+ if (empty($errors)) {
+ $task = $taskAddForm->getTask();
+ $entityManager->persist($task);
+ $entityManager->flush();
+ return $this->ajaxFormAnswer([
+ 'refresh' => true,
+ 'reset' => true,
+ ]);
+ } else {
+ return $this->ajaxFormAnswer([
+ 'error' => join('
', $errors),
+ 'code' => Response::HTTP_BAD_REQUEST,
+ ]);
+ }
+ }
+
+ // Task delete form
+ /** @var TaskDeleteForm $taskDeleteForm */
+ $taskDeleteForm = $this->createNamedCustomForm('taskDelete', TaskDeleteForm::class);
+ $taskDeleteForm->handleRequest($request);
+ if ($taskDeleteForm->isSubmitted() && $taskDeleteForm->isValid()) {
+ $errors = $taskDeleteForm->validate();
+ if (empty($errors)) {
+ $task = $taskDeleteForm->getTask($taskRepository);
+ if (!is_null($task)) {
+ $entityManager->remove($task);
+ $entityManager->flush();
+ return $this->ajaxFormAnswer([
+ 'refresh' => true,
+ ]);
+ }
+ return $this->ajaxFormAnswer([
+ 'error' => 'Tâche non trouvé',
+ 'code' => Response::HTTP_BAD_REQUEST,
+ ]);
+ } else {
+ return $this->ajaxFormAnswer([
+ 'error' => join('
', $errors),
+ 'code' => Response::HTTP_BAD_REQUEST,
+ ]);
+ }
+ }
+
+ // Get tasks
+ $tasks = $taskRepository->findAll();
+
+ return $this->render('task/tasks.html.twig', [
+ 'tasks' => $tasks,
+ 'taskAddForm' => $taskAddForm,
+ 'taskDeleteForm' => $taskDeleteForm,
+ ]);
+ }
+
+ /**
+ * Page for editing task
+ *
+ * @param string $id
+ * @param Request $request
+ * @return Response
+ */
+ public function task(string $id, Request $request): Response
+ {
+ // Get repositories
+ $entityManager = $this->getDoctrine()->getManager();
+ /** @var ActorRepository $actorRepository */
+ $actorRepository = $entityManager->getRepository(ActorEntity::class);
+ /** @var TaskRepository $taskRepository */
+ $taskRepository = $entityManager->getRepository(TaskEntity::class);
+
+ // Get task
+ $task = $taskRepository->find($id);
+
+ // Get actors
+ $actors = $actorRepository->findAll();
+
+ // Task edit form
+ /** @var TaskEditForm $taskEditForm */
+ $taskEditForm = $this->createNamedCustomForm('taskEdit', TaskEditForm::class, [
+ 'actors' => $actors
+ ]);
+ $taskEditForm->handleRequest($request);
+ if ($taskEditForm->isSubmitted() && $taskEditForm->isValid()) {
+ $errors = $taskEditForm->validate();
+ if (empty($errors)) {
+ $taskEditForm->updateTask($task);
+ $entityManager->persist($task);
+ $entityManager->flush();
+ return $this->ajaxFormAnswer([
+ 'refresh' => true,
+ ]);
+ } else {
+ return $this->ajaxFormAnswer([
+ 'error' => 'Tâche non trouvé',
+ 'code' => Response::HTTP_BAD_REQUEST,
+ ]);
+ }
+ }
+
+ // Task delete form
+ /** @var TaskDeleteForm $taskDeleteForm */
+ $taskDeleteForm = $this->createNamedCustomForm('taskDelete', TaskDeleteForm::class);
+ $taskDeleteForm->handleRequest($request);
+ if ($taskDeleteForm->isSubmitted() && $taskDeleteForm->isValid()) {
+ $errors = $taskDeleteForm->validate();
+ if (empty($errors)) {
+ $entityManager->remove($task);
+ $entityManager->flush();
+ if (is_null($task->getActivity())) {
+ return $this->ajaxFormAnswer([
+ 'redirect' => $this->generateUrl('task_tasks', [], UrlGenerator::ABSOLUTE_PATH),
+ ]);
+ }
+ return $this->ajaxFormAnswer([
+ 'error' => 'Tâche non trouvé',
+ 'code' => Response::HTTP_BAD_REQUEST,
+ ]);
+ } else {
+ return $this->ajaxFormAnswer([
+ 'error' => join('
', $errors),
+ 'code' => Response::HTTP_BAD_REQUEST,
+ ]);
+ }
+ }
+
+ // Update fields
+ $taskEditForm->updateFields($task);
+
+ return $this->render('task/task.html.twig', [
+ 'task' => $task,
+ 'taskEditForm' => $taskEditForm,
+ 'taskDeleteForm' => $taskDeleteForm,
+ ]);
+ }
+}
\ No newline at end of file
diff --git a/src/Entity/TaskEntity.php b/src/Entity/TaskEntity.php
new file mode 100644
index 0000000..c7eb860
--- /dev/null
+++ b/src/Entity/TaskEntity.php
@@ -0,0 +1,226 @@
+id;
+ }
+
+ /**
+ * Set the ID of the entity
+ *
This should not be done after creation
+ *
+ * @param string $id
+ * @return self
+ */
+ public function setId(string $id): self
+ {
+ $this->id = $id;
+
+ return $this;
+ }
+
+ /**
+ * Generate and save a random ID
+ *
+ * @return string Generated ID
+ */
+ public function generateId(): string
+ {
+ // Generate ID
+ $id = StringGenerationHelper::generateString(array_merge(range('a', 'z'), range('A', 'Z'), range('0', '9')), 8);
+
+ // Save and return
+ $this->setId($id);
+ return $id;
+ }
+
+ /**
+ * Get the task name
+ *
+ * @return string
+ */
+ public function getName(): string
+ {
+ return $this->name;
+ }
+
+ /**
+ * Set a new name for the task
+ *
+ * @param string $name
+ * @return self
+ */
+ public function setName(string $name): self
+ {
+ $this->name = $name;
+
+ return $this;
+ }
+
+ /**
+ * Get the planified execution date
+ *
+ * @return DateTime|NULL
+ */
+ public function getPlanifiedDate(): ?DateTime
+ {
+ return $this->planifiedDate;
+ }
+
+ /**
+ * Set the planified execution date
+ *
+ * @param DateTime $planifiedDate
+ * @return self
+ */
+ public function setPlanifiedDate(?DateTime $planifiedDate): self
+ {
+ $this->planifiedDate = $planifiedDate;
+
+ return $this;
+ }
+
+ /**
+ * Get the effective execution date
+ *
+ * @return DateTime|NULL
+ */
+ public function getExecutionDate(): ?DateTime
+ {
+ return $this->executionDate;
+ }
+
+ /**
+ * Set the effective execution date
+ *
+ * @param DateTime $executionDate
+ * @return self
+ */
+ public function setExecutionDate(?DateTime $executionDate): self
+ {
+ $this->executionDate = $executionDate;
+
+ return $this;
+ }
+
+ /**
+ * Get the related actor
+ *
+ * @return ActorEntity|NULL
+ */
+ public function getActor(): ?ActorEntity
+ {
+ return $this->actor;
+ }
+
+ /**
+ * Set the new related actor
+ *
+ * @param ActorEntity|NULL $actor
+ * @return self
+ */
+ public function setActor(?ActorEntity $actor): self
+ {
+ $this->actor = $actor;
+
+ return $this;
+ }
+
+ /**
+ * Get the related activity
+ *
+ * @return ActivityEntity|NULL
+ */
+ public function getActivity(): ?ActivityEntity
+ {
+ return $this->activity;
+ }
+
+ /**
+ * Set the related activity
+ *
+ * @param ActivityEntity $activity
+ * @return self
+ */
+ public function setActivity(?ActivityEntity $activity): self
+ {
+ $this->activity = $activity;
+
+ return $this;
+ }
+
+ /**
+ * Get the status of the task
+ *
+ * @return string
+ * @see StatusEnum
+ */
+ public function getStatus(): string
+ {
+ return $this->status;
+ }
+
+ /**
+ * Set a new status for the task
+ *
+ * @param string $status
+ * @return self
+ * @see StatusEnum
+ */
+ public function setStatus(string $status): self
+ {
+ if (!in_array($status, $this::VALID_STATUS)) {
+ throw new InvalidEnumKeyException();
+ }
+ $this->status = $status;
+
+ return $this;
+ }
+}
\ No newline at end of file
diff --git a/src/Form/Task/TaskAddForm.php b/src/Form/Task/TaskAddForm.php
new file mode 100644
index 0000000..49e5dfe
--- /dev/null
+++ b/src/Form/Task/TaskAddForm.php
@@ -0,0 +1,97 @@
+actors = $data['actors'];
+ parent::__construct($formBuilder, $data, $usesToken);
+ }
+
+ /**
+ * Create and return taks entity from data
+ *
+ * @return TaskEntity
+ */
+ public function getTask(): TaskEntity
+ {
+ $data = $this->form->getData();
+ $task = new TaskEntity();
+ $task->generateId();
+ $task->setName($data['name']);
+ $task->setStatus($data['status']);
+ $task->setActor($data['actor']);
+ $task->setPlanifiedDate($data['planifiedDate']);
+ $task->setExecutionDate($data['executionDate']);
+
+ return $task;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getTemplate(): string
+ {
+ return '_includes/html/form/task/add.html.twig';
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function addFields($formBuilder, $options): void
+ {
+ $formBuilder->add('name', TextType::class, [
+ 'required' => true
+ ])
+ ->add('actor', EntityType::class, [
+ 'class' => ActorEntity::class,
+ 'choices' => $this->actors,
+ 'group_by' => function (ActorEntity $choice) {
+ if ($choice->getFollowed()) {
+ return 'Suivi';
+ }
+ if ($choice->getActive()) {
+ return 'Actif';
+ }
+
+ return 'Inactif';
+ },
+ 'choice_label' => 'displayName',
+ 'required' => false,
+ ])
+ ->add('status', ChoiceType::class, [
+ 'choices' => TaskEntity::VALID_STATUS,
+ 'choice_label' => function ($choice, $key, $value) {
+ return StatusEnum::STATUS_NAME[$choice];
+ },
+ ])
+ ->add('planifiedDate', DateType::class, [
+ 'required' => false,
+ 'widget' => 'single_text',
+ ])
+ ->add('executionDate', DateType::class, [
+ 'required' => false,
+ 'widget' => 'single_text',
+ ])
+ ->add('submit', SubmitType::class);
+ }
+}
\ No newline at end of file
diff --git a/src/Form/Task/TaskDeleteForm.php b/src/Form/Task/TaskDeleteForm.php
new file mode 100644
index 0000000..1e208e0
--- /dev/null
+++ b/src/Form/Task/TaskDeleteForm.php
@@ -0,0 +1,40 @@
+form->getData();
+ return $taskRepository->find($data['id']);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getTemplate(): string
+ {
+ return '_includes/html/form/task/delete.html.twig';
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function addFields($formBuilder, $options): void
+ {
+ $formBuilder->add('id', HiddenType::class)->add('submit', SubmitType::class);
+ }
+}
\ No newline at end of file
diff --git a/src/Form/Task/TaskEditForm.php b/src/Form/Task/TaskEditForm.php
new file mode 100644
index 0000000..033c264
--- /dev/null
+++ b/src/Form/Task/TaskEditForm.php
@@ -0,0 +1,115 @@
+actors = $data['actors'];
+ parent::__construct($formBuilder, $data, $usesToken);
+ }
+
+ /**
+ * Update fields with task data
+ *
+ * @param TaskEntity $task
+ * @return self
+ */
+ public function updateFields(TaskEntity $task): self
+ {
+ $this->form->setData([
+ 'name' => $task->getName(),
+ 'status' => $task->getStatus(),
+ 'actor' => $task->getActor(),
+ 'planifiedDate' => $task->getPlanifiedDate(),
+ 'executionDate' => $task->getExecutionDate(),
+ ]);
+
+ return $this;
+ }
+
+ /**
+ * Update task
+ *
+ * @param TaskEntity $task
+ * @return self
+ */
+ public function updateTask(TaskEntity $task): self
+ {
+ $data = $this->form->getData();
+ $task->setName($data['name']);
+ $task->setStatus($data['status']);
+ $task->setActor($data['actor']);
+ $task->setPlanifiedDate($data['planifiedDate']);
+ $task->setExecutionDate($data['executionDate']);
+
+ return $this;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getTemplate(): string
+ {
+ return '_includes/html/form/task/edit.html.twig';
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function addFields($formBuilder, $options): void
+ {
+ $formBuilder->add('name', TextType::class, [
+ 'required' => true
+ ])
+ ->add('actor', EntityType::class, [
+ 'class' => ActorEntity::class,
+ 'choices' => $this->actors,
+ 'group_by' => function (ActorEntity $choice) {
+ if ($choice->getFollowed()) {
+ return 'Suivi';
+ }
+ if ($choice->getActive()) {
+ return 'Actif';
+ }
+
+ return 'Inactif';
+ },
+ 'choice_label' => 'displayName',
+ 'required' => false,
+ ])
+ ->add('status', ChoiceType::class, [
+ 'choices' => TaskEntity::VALID_STATUS,
+ 'choice_label' => function ($choice, $key, $value) {
+ return StatusEnum::STATUS_NAME[$choice];
+ },
+ ])
+ ->add('planifiedDate', DateType::class, [
+ 'required' => false,
+ 'widget' => 'single_text',
+ ])
+ ->add('executionDate', DateType::class, [
+ 'required' => false,
+ 'widget' => 'single_text',
+ ])
+ ->add('submit', SubmitType::class);
+ }
+}
\ No newline at end of file
diff --git a/src/Repository/ActivityRepository.php b/src/Repository/ActivityRepository.php
index c569fd0..eba568f 100644
--- a/src/Repository/ActivityRepository.php
+++ b/src/Repository/ActivityRepository.php
@@ -23,9 +23,9 @@
/**
* {@inheritdoc}
*/
- public function findAll(): array
+ public function findBy(array $criteria, ?array $orderBy = self::DEFAULT_ORDER, $limit = null, $offset = null)
{
- return $this->findBy([], ActivityRepository::DEFAULT_ORDER);
+ return parent::findBy($criteria, $orderBy, $limit, $offset);
}
/**
diff --git a/src/Repository/ActorRepository.php b/src/Repository/ActorRepository.php
index 81d187e..d20dae7 100644
--- a/src/Repository/ActorRepository.php
+++ b/src/Repository/ActorRepository.php
@@ -28,7 +28,15 @@
if ($onlyActive) {
$critera['active'] = True;
}
- return $this->findBy($critera, ActorRepository::DEFAULT_ORDER);
+ return $this->findBy($critera);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function findBy(array $criteria, ?array $orderBy = self::DEFAULT_ORDER, $limit = null, $offset = null)
+ {
+ return parent::findBy($criteria, $orderBy, $limit, $offset);
}
/**
diff --git a/src/Repository/TaskRepository.php b/src/Repository/TaskRepository.php
new file mode 100644
index 0000000..0f7c96e
--- /dev/null
+++ b/src/Repository/TaskRepository.php
@@ -0,0 +1,46 @@
+ 'asc',
+ 'activity' => 'desc',
+ 'name' => 'asc',
+ ];
+
+ /**
+ * {@inheritdoc}
+ */
+ public function find($id, $lockMode = null, $lockVersion = null): ?TaskEntity
+ {
+ return parent::find($id, $lockMode, $lockVersion);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function findBy(array $criteria, ?array $orderBy = self::DEFAULT_ORDER, $limit = null, $offset = null)
+ {
+ return parent::findBy($criteria, $orderBy, $limit, $offset);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function createQueryBuilder($alias, $indexBy = null): QueryBuilder
+ {
+ $queryBuilder = parent::createQueryBuilder($alias, $indexBy);
+ foreach ($this::DEFAULT_ORDER as $field => $order) {
+ $queryBuilder->addOrderBy($alias . '.' . $field, $order);
+ }
+
+ return $queryBuilder;
+ }
+}
\ No newline at end of file
diff --git a/templates/_includes/html/arrays/task.html.twig b/templates/_includes/html/arrays/task.html.twig
new file mode 100644
index 0000000..e27a088
--- /dev/null
+++ b/templates/_includes/html/arrays/task.html.twig
@@ -0,0 +1,47 @@
+{% import '_includes/macros/status.html.twig' as statusTools %}
+{% import '_includes/macros/date.html.twig' as dateTools %}
+{% set hideActor=hideActor|default(false) %}
+{% set hideActivity=hideActivity|default(false) %}
+
+
Activité | + {% endif %} +Nom | +Status | + {% if not hideActor %} +Acteur | + {% endif %} +Planifié | +Executé | +Actions | +
{% if task.activity is not null %} {{ task.activity.name }} {% endif %} | + {% endif %} +{{ task.name }} | +{{ statusTools.statusName(task.status) }} | + {% if not hideActor %} +{% if task.actor is not null %}{{ task.actor.displayName }} + {% endif %} | + {% endif %} +{% if task.planifiedDate is not null %} + {{ dateTools.euro(task.planifiedDate) }} {% endif %} | +{% if task.executionDate is not null %} + {{ dateTools.euro(task.executionDate) }} {% endif %} | +{% if taskDeleteForm is defined %} {% include '_includes/html/genericForm.html.twig' with {'form': taskDeleteForm, 'data': {task: task, variant: 'icon'}} only %} + {% endif %} | +