src/Nolimit/Task/Controller/TaskController.php line 118

Open in your IDE?
  1. <?php
  2. declare(strict_types=1);
  3. namespace App\Nolimit\Task\Controller;
  4. use App\Entity\Project;
  5. use App\Entity\ProjectStatus;
  6. use App\Entity\ProjectType;
  7. use App\Entity\Task;
  8. use App\Entity\TaskStatus;
  9. use App\Entity\TaskType;
  10. use App\Entity\User;
  11. use App\Nolimit\Task\Manager\TaskManager;
  12. use App\Nolimit\Task\Persister\TaskLogPersister;
  13. use App\Nolimit\Task\Persister\TaskPersister;
  14. use App\Nolimit\Task\Provider\TaskDatatableProvider;
  15. use App\Nolimit\Task\Service\TaskTimeImportService;
  16. use App\Repository\ProjectRepository;
  17. use App\Repository\ProjectTypeRepository;
  18. use App\Repository\TaskLogRepository;
  19. use App\Repository\TaskRepository;
  20. use App\Repository\TaskStatusRepository;
  21. use App\Repository\TaskTypeRepository;
  22. use App\Repository\UserRepository;
  23. use Doctrine\ORM\EntityManagerInterface;
  24. use Sonata\AdminBundle\Controller\CRUDController;
  25. use Symfony\Component\HttpFoundation\File\UploadedFile;
  26. use Symfony\Component\HttpFoundation\JsonResponse;
  27. use Symfony\Component\HttpFoundation\Request;
  28. use Symfony\Component\HttpFoundation\Response;
  29. use Symfony\Component\Routing\Annotation\Route;
  30. use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
  31. use Symfony\Contracts\Translation\TranslatorInterface;
  32. class TaskController extends CRUDController{
  33.     private EntityManagerInterface $entity;
  34.     private TaskDatatableProvider $datatableProvider;
  35.     private TranslatorInterface $translator;
  36.     private TaskManager $taskManager;
  37.     private TaskLogRepository $taskLogRepository;
  38.     private TaskRepository $taskRepository;
  39.     private TaskLogPersister $taskLogPersister;
  40.     private TaskPersister $taskPersister;
  41.     private TaskDatatableProvider $taskDatatableProvider;
  42.     private ProjectRepository $projectRepository;
  43.     private ProjectTypeRepository $projectTypeRepository;
  44.     private TaskTypeRepository $taskTypeRepository;
  45.     private TaskStatusRepository $taskStatusRepository;
  46.     private UserRepository $userRepository;
  47.     private TaskTimeImportService $taskTimeImportService;
  48.     /**
  49.      * @var TokenStorageInterface
  50.      */
  51.     private $tokenStorage;
  52.     public function __construct(EntityManagerInterface $entity,
  53.                                 TaskDatatableProvider $datatableProvider,
  54.                                 TranslatorInterface $translator,
  55.                                 TaskManager $taskManager,TaskLogRepository $taskLogRepository,TaskRepository $taskRepository,TaskLogPersister $taskLogPersister,TaskPersister $taskPersister,TaskDatatableProvider $taskDatatableProvider,ProjectRepository $projectRepository,ProjectTypeRepository $projectTypeRepository,TaskTypeRepository $taskTypeRepository,TaskStatusRepository $taskStatusRepository,UserRepository $userRepository,TokenStorageInterface $tokenStorage, TaskTimeImportService $taskTimeImportService)
  56.     {
  57.         $this->entity = $entity;
  58.         $this->datatableProvider = $datatableProvider;
  59.         $this->translator = $translator;
  60.         $this->taskManager = $taskManager;
  61.         $this->taskLogRepository = $taskLogRepository;
  62.         $this->taskRepository = $taskRepository;
  63.         $this->taskLogPersister = $taskLogPersister;
  64.         $this->taskPersister = $taskPersister;
  65.         $this->taskDatatableProvider = $taskDatatableProvider;
  66.         $this->projectRepository = $projectRepository;
  67.         $this->projectTypeRepository = $projectTypeRepository;
  68.         $this->taskTypeRepository = $taskTypeRepository;
  69.         $this->taskStatusRepository = $taskStatusRepository;
  70.         $this->userRepository = $userRepository;
  71.         $this->tokenStorage = $tokenStorage;
  72.         $this->taskTimeImportService = $taskTimeImportService;
  73.     }
  74.     public function listAction(Request $request): Response
  75.     {
  76. //        $notActiveProjetStatu=[ProjectStatus::PROJECT_STATUS_BEFEJEZETT, ProjectStatus::PROJECT_STATUS_FELFUGESZTETT, ProjectStatus::PROJECT_STATUS_VISSZAUTASITOTT];
  77. //        $projects=$this->projectRepository->getProjectByStatusNotIn($notActiveProjetStatu);
  78.         $this->denyAccessUnlessGranted('IS_AUTHENTICATED_FULLY');
  79.         $projects=$this->projectRepository->findAll();
  80.         $projectTypes=$this->projectTypeRepository->findAll();
  81.         $taskTypes=$this->taskTypeRepository->findAll();
  82.         $taskStatus=$this->taskStatusRepository->findAll();
  83.         $user=$this->tokenStorage->getToken()->getUser();
  84.         $user_id=$user->getId();
  85.         $isSuperAdmin = in_array("ROLE_SUPER_ADMIN", $user->getRoles());
  86.         $selectedStatus = $isSuperAdmin
  87.             ? [TaskStatus::TASK_STATUS_LETREHOZOTT, TaskStatus::TASK_STATUS_FEJLESZTESALATT, TaskStatus::TASK_STATUS_BEFEJEZETT]
  88.             : [TaskStatus::TASK_STATUS_LETREHOZOTT, TaskStatus::TASK_STATUS_FEJLESZTESALATT];
  89.         $users=$this->userRepository->findAll();
  90.         return $this->renderWithExtraParams('/task/task_view.html.twig', array(
  91.             'projects'=>$projects,
  92.             'projectTypes'=>$projectTypes,
  93.             'taskTypes'=>$taskTypes,
  94.             'taskStatus'=>$taskStatus,
  95.             'selectedStatus'=>$selectedStatus,
  96.             'user_id'=>$user_id,
  97.             'current_user_id'=>$user_id,
  98.             'is_super_admin'=>$isSuperAdmin,
  99.             'users'=>$users
  100.         ));
  101.     }
  102.     /**
  103.      * @Route("/admin/app/task/task_list", name="task_list", methods={"POST"} )
  104.      */
  105.     public function taskListAction()
  106.     {
  107.         $user = $this->tokenStorage->getToken()->getUser();
  108.         $user_id = $user->getId();
  109.         $isSuperAdmin = in_array("ROLE_SUPER_ADMIN", $user->getRoles());
  110.         $selectedStatus = $isSuperAdmin
  111.             ? [TaskStatus::TASK_STATUS_LETREHOZOTT, TaskStatus::TASK_STATUS_FEJLESZTESALATT,  TaskStatus::TASK_STATUS_BEFEJEZETT]
  112.             : [TaskStatus::TASK_STATUS_LETREHOZOTT, TaskStatus::TASK_STATUS_FEJLESZTESALATT];
  113.         $adata['data'] = $this->datatableProvider->datatableRow($selectedStatus, $user_id);
  114.         return $this->json($adata);
  115.     }
  116.     /**
  117.      * @Route("/admin/app/apv/task_filtered", name="task_filtered", methods={"POST"} )
  118.      */
  119.     public function taskFilteredAction(Request $request): Response
  120.     {
  121.         $adata['adata'] = $this->taskDatatableProvider->getFilteredData($request);
  122.         return $this->json($adata);
  123.     }
  124.     /**
  125.      * @Route("/admin/app/task/task_new", name="task_new", methods={"POST"} )
  126.      */
  127.     public function taskNewAction(Request $request): Response
  128.     {
  129.         if (!$this->isGranted('IS_AUTHENTICATED_FULLY')) {
  130.             // AJAX-kérés esetén 401-es válasz küldése
  131.             if ($request->isXmlHttpRequest()) {
  132.                 return new JsonResponse(['error' => 'Unauthorized'], 401);
  133.             }
  134.             // Nem AJAX-kérés esetén hozzáférés megtagadása
  135.             throw new AccessDeniedException('Hozzáférés megtagadva');
  136.         }
  137.         $this->denyAccessUnlessGranted('IS_AUTHENTICATED_FULLY');
  138.         $currentUser = $this->tokenStorage->getToken()->getUser();
  139.         $isSuperAdmin = in_array("ROLE_SUPER_ADMIN", $currentUser->getRoles());
  140.         $project = $this->projectRepository->getProjectsForTaskAdd($isSuperAdmin);
  141.         $user = $this->entity->getRepository(User::class)->findAll();
  142.         $taskType = $this->entity->getRepository(TaskType::class)->findAll();
  143.         $taskStatus = $this->entity->getRepository(TaskStatus::class)->findAll();
  144.         $lastWorkedDay = $this->taskManager->getLastWorkedDay();
  145.         $defaultProject = !empty($project) ? reset($project) : null;
  146.         $nextOrderTask = $defaultProject ? $this->taskRepository->getNextOrderTaskByProjectId($defaultProject->getId()) : 1;
  147.         return $this->renderWithExtraParams('/task/task_new_edit.html.twig', array(
  148.             'project'=>$project,
  149.             'user'=>$user,
  150.             'taskType'=>$taskType,
  151.             'title'=>$this->translator->trans('New task'),
  152.             'edited'=>false,
  153.             'lastWorkedDay'=>$lastWorkedDay,
  154.             'taskStatus'=>$taskStatus,
  155.             'nextOrderTask'=>$nextOrderTask
  156.         ));
  157.     }
  158.     /**
  159.      * @Route("/admin/app/task/task_edit", name="task_edit", methods={"POST"} )
  160.      */
  161.     public function taskEditAction(Request $request): Response
  162.     {
  163.         $this->denyAccessUnlessGranted('IS_AUTHENTICATED_FULLY');
  164.         $id = $request->get('id');
  165.         $task = $this->entity->getRepository(Task::class)->find($id);
  166.         
  167.         // Ellenőrzés: Ha befejezett task, karbantartási projekt és van tranche_id, csak super admin módosíthatja
  168.         $currentUser = $this->tokenStorage->getToken()->getUser();
  169.         $isSuperAdmin = in_array("ROLE_SUPER_ADMIN", $currentUser->getRoles());
  170.         
  171.         if ($task->getTaskStatus()->getId() == TaskStatus::TASK_STATUS_BEFEJEZETT &&
  172.             in_array($task->getProject()->getProjectType()->getId(), [ProjectType::PROJECT_TYPE_KARBANTARTAS, ProjectType::PROJECT_TYPE_HOSTING]) &&
  173.             $task->getTranche() !== null &&
  174.             !$isSuperAdmin) {
  175.             return new JsonResponse(['error' => 'Ez a feladat nem módosítható. Csak Super Admin módosíthatja a lezárt karbantartási vagy hosting feladatokat, amelyekhez tartozik tranche.'], 403);
  176.         }
  177.         // Átadott vagy befejezett projekt + befejezett feladat esetén csak super admin módosíthat
  178.         if ($task->getTaskStatus()->getId() == TaskStatus::TASK_STATUS_BEFEJEZETT &&
  179.             $task->getProject()->getStatus() &&
  180.             in_array($task->getProject()->getStatus()->getId(), [ProjectStatus::PROJECT_STATUS_ATADOTT, ProjectStatus::PROJECT_STATUS_BEFEJEZETT]) &&
  181.             !$isSuperAdmin) {
  182.             return new JsonResponse(['error' => 'Ez a feladat nem módosítható, mert a projekt átadott vagy befejezett és a feladat be van fejezve. Csak Super Admin módosíthatja.'], 403);
  183.         }
  184.         // ERP / WEB fejlesztés + befejezett feladat esetén csak super admin módosíthat
  185.         if ($task->getTaskStatus()->getId() == TaskStatus::TASK_STATUS_BEFEJEZETT &&
  186.             $task->getProject()->getProjectType() &&
  187.             in_array($task->getProject()->getProjectType()->getId(), [ProjectType::PROJECT_TYPE_ERP_FEJLESZTES, ProjectType::PROJECT_TYPE_WEB_FEJLESZTES]) &&
  188.             !$isSuperAdmin) {
  189.             return new JsonResponse(['error' => 'Ez a feladat nem módosítható, mert ERP vagy WEB fejlesztési projekt befejezett feladata. Csak Super Admin módosíthatja.'], 403);
  190.         }
  191.         
  192.         $project = $this->projectRepository->getProjectsForTaskAdd($isSuperAdmin);
  193.         // Szerkesztéskor a feladat jelenlegi projektje mindig szerepeljen a listában (ha nem Super Admin)
  194.         $currentProject = $task->getProject();
  195.         if ($currentProject && !in_array($currentProject, $project, true)) {
  196.             $project = array_merge([$currentProject], $project);
  197.         }
  198.         $taskStatus = $this->entity->getRepository(TaskStatus::class)->findAll();
  199.         $user = $this->entity->getRepository(User::class)->findAll();
  200.         $taskType = $this->entity->getRepository(TaskType::class)->findAll();
  201.         return $this->renderWithExtraParams('/task/task_new_edit.html.twig', array(
  202.             'project'=>$project,
  203.             'user'=>$user,
  204.             'task'=>$task,
  205.             'title'=>$this->translator->trans('Edit task'),
  206.             'taskStatus'=>$taskStatus,
  207.             'taskType'=>$taskType,
  208.             'edited'=>true
  209.         ));
  210.     }
  211.     /**
  212.      * @Route("/admin/app/task/task_save", name="task_save", methods={"POST"} )
  213.      */
  214.     public function taskSaveAction(Request $request): Response
  215.     {
  216.         $task = $this->taskPersister->insertUpdateTask($request);
  217.         $data[] = $this->taskDatatableProvider->getRow($task);
  218.         return new JsonResponse(array('msg' => true,'datatableRow' =>$data[0]),  200);
  219.     }
  220.     /**
  221.      * @Route("/admin/app/task/task_next_order", name="task_next_order", methods={"POST"} )
  222.      */
  223.     public function taskNextOrderAction(Request $request): JsonResponse
  224.     {
  225.         $this->denyAccessUnlessGranted('IS_AUTHENTICATED_FULLY');
  226.         $projectId = (int) $request->get('projectId');
  227.         if ($projectId <= 0) {
  228.             return new JsonResponse(['nextOrderTask' => 1]);
  229.         }
  230.         return new JsonResponse([
  231.             'nextOrderTask' => $this->taskRepository->getNextOrderTaskByProjectId($projectId),
  232.         ]);
  233.     }
  234.     /**
  235.      * @Route("/admin/app/task/task_new_log", name="task_new_log", methods={"POST"} )
  236.      */
  237.     public function taskNewLogAction(Request $request): Response
  238.     {
  239.         $id = $request->get('id');
  240.         $task = $this->entity->getRepository(Task::class)->find($id);
  241.         $lastWorkedDay = $this->taskManager->getLastWorkedDay();
  242.         return $this->renderWithExtraParams('/task/task_new_edit_log.html.twig', array(
  243.             'task'=>$task,
  244.             'title'=>$this->translator->trans('New log'),
  245.             'edited'=>false,
  246.             'lastWorkedDay'=>$lastWorkedDay
  247.         ));
  248.     }
  249.     /**
  250.      * @Route("/admin/app/task/task_edit_log", name="task_edit_log", methods={"POST"} )
  251.      */
  252.     public function taskEditLogAction(Request $request): Response
  253.     {
  254.         $this->denyAccessUnlessGranted('IS_AUTHENTICATED_FULLY');
  255.         $id = (int)$request->get('id');
  256.         $taskLog = $this->taskLogRepository->find($id);
  257.         if (!$taskLog) {
  258.             return new JsonResponse(['error' => 'A log nem található.'], 404);
  259.         }
  260.         $currentUser = $this->tokenStorage->getToken()->getUser();
  261.         $isSuperAdmin = in_array('ROLE_SUPER_ADMIN', $currentUser->getRoles());
  262.         if (!$isSuperAdmin && $taskLog->getUser()->getId() !== $currentUser->getId()) {
  263.             return new JsonResponse(['error' => 'Nincs jogosultsága a log módosításához.'], 403);
  264.         }
  265.         return $this->renderWithExtraParams('/task/task_new_edit_log.html.twig', array(
  266.             'task'=>$taskLog->getTask(),
  267.             'title'=>$this->translator->trans('Edit log'),
  268.             'taskLog'=>$taskLog,
  269.             'workedHourMin'=>$this->taskManager->convertMinInHourMin($taskLog->getWorkedMinutes()),
  270.             'descriptionValue'=>$this->taskDatatableProvider->sanitizeTextForDatatable($taskLog->getDescription()),
  271.             'edited'=>true
  272.         ));
  273.     }
  274.     /**
  275.      * @Route("/admin/app/task/task_save_log", name="task_save_log", methods={"POST"} )
  276.      */
  277.     public function taskSaveLogAction(Request $request): Response
  278.     {
  279.         $this->taskLogPersister->insertUpdateTaskLog($request);
  280.         $task = $this->taskRepository->find($request->get('taskId'));
  281.         $data[] = $this->datatableProvider->getRow($task);
  282.         return new JsonResponse(array('msg' => true,'datatableRow' =>$data[0]),  200);
  283.     }
  284.     /**
  285.      * @Route("/admin/app/task/task_delete_log", name="task_delete_log", methods={"POST"} )
  286.      */
  287.     public function taskDeleteLogAction(Request $request): Response
  288.     {
  289.         $logId = (int)$request->get('logId');
  290.         $taskId = (int)$request->get('taskId');
  291.         $taskLog = $this->taskLogRepository->find($logId);
  292.         $this->entity->remove($taskLog);
  293.         $this->entity->flush();
  294.         $task = $this->taskRepository->find($taskId);
  295.         $data[] = $this->taskDatatableProvider->getRow($task);
  296.         return new JsonResponse(array('msg' => true,'datatableRow' =>$data[0]),  200);
  297.     }
  298.     /**
  299.      * @Route("/admin/app/task/task_move_log", name="task_move_log", methods={"POST"} )
  300.      */
  301.     public function taskMoveLogAction(Request $request): Response
  302.     {
  303.         $this->denyAccessUnlessGranted('IS_AUTHENTICATED_FULLY');
  304.         $currentUser = $this->tokenStorage->getToken()->getUser();
  305.         if (!in_array('ROLE_SUPER_ADMIN', $currentUser->getRoles())) {
  306.             return new JsonResponse(['error' => 'Csak Super Admin helyezheti át a logot.'], 403);
  307.         }
  308.         $logId = (int)$request->get('logId');
  309.         $taskLog = $this->taskLogRepository->find($logId);
  310.         if (!$taskLog) {
  311.             return new JsonResponse(['error' => 'A log nem található.'], 404);
  312.         }
  313.         $task = $taskLog->getTask();
  314.         $projects = $this->projectRepository->findBy([], ['name' => 'ASC']);
  315.         $taskItems = $this->formatTasksForSelect(
  316.             $task->getProject()->getId(),
  317.             $task->getId()
  318.         );
  319.         return $this->renderWithExtraParams('/task/task_move_log.html.twig', [
  320.             'taskLog' => $taskLog,
  321.             'task' => $task,
  322.             'taskName' => $this->taskDatatableProvider->sanitizeTextForDatatable($task->getName()),
  323.             'projects' => $projects,
  324.             'tasks' => $taskItems,
  325.             'title' => $this->translator->trans('Move log'),
  326.         ]);
  327.     }
  328.     /**
  329.      * @Route("/admin/app/task/task_move_log_tasks", name="task_move_log_tasks", methods={"POST"} )
  330.      */
  331.     public function taskMoveLogTasksAction(Request $request): JsonResponse
  332.     {
  333.         $this->denyAccessUnlessGranted('IS_AUTHENTICATED_FULLY');
  334.         $currentUser = $this->tokenStorage->getToken()->getUser();
  335.         if (!in_array('ROLE_SUPER_ADMIN', $currentUser->getRoles())) {
  336.             return new JsonResponse(['error' => 'Csak Super Admin.'], 403);
  337.         }
  338.         $projectId = (int)$request->get('project_id');
  339.         $sourceTaskId = (int)$request->get('source_task_id');
  340.         return new JsonResponse([
  341.             'tasks' => $this->formatTasksForSelect($projectId, $sourceTaskId),
  342.         ]);
  343.     }
  344.     /**
  345.      * @Route("/admin/app/task/task_move_log_save", name="task_move_log_save", methods={"POST"} )
  346.      */
  347.     public function taskMoveLogSaveAction(Request $request): JsonResponse
  348.     {
  349.         $this->denyAccessUnlessGranted('IS_AUTHENTICATED_FULLY');
  350.         $currentUser = $this->tokenStorage->getToken()->getUser();
  351.         if (!in_array('ROLE_SUPER_ADMIN', $currentUser->getRoles())) {
  352.             return new JsonResponse(['error' => 'Csak Super Admin helyezheti át a logot.'], 403);
  353.         }
  354.         try {
  355.             $result = $this->taskLogPersister->moveTaskLog($request);
  356.             $rows = [];
  357.             $taskIds = [];
  358.             foreach ([$result['sourceTask'], $result['targetTask']] as $t) {
  359.                 $id = $t->getId();
  360.                 if (!in_array($id, $taskIds, true)) {
  361.                     $taskIds[] = $id;
  362.                     $rows[] = $this->taskDatatableProvider->getRow($t);
  363.                 }
  364.             }
  365.             return new JsonResponse([
  366.                 'msg' => true,
  367.                 'datatableRows' => $rows,
  368.                 'sourceTaskId' => $result['sourceTask']->getId(),
  369.             ]);
  370.         } catch (\Exception $e) {
  371.             return new JsonResponse(['error' => $e->getMessage()], 400);
  372.         }
  373.     }
  374.     /**
  375.      * @Route("/admin/app/task/task_delete", name="task_delete", methods={"POST"} )
  376.      */
  377.     public function taskDeleteAction(Request $request): Response
  378.     {
  379.         $id = (int)$request->get('id');
  380.         $task = $this->taskRepository->find($id);
  381.         $this->entity->remove($task);
  382.         $this->entity->flush();
  383.         return new JsonResponse(array('msg' => true),  200);
  384.     }
  385.     /**
  386.      * @Route("/admin/app/task/task_import", name="task_import", methods={"POST"} )
  387.      */
  388.     public function taskImportAction(Request $request): Response
  389.     {
  390.         $this->denyAccessUnlessGranted('IS_AUTHENTICATED_FULLY');
  391.         $currentUser = $this->tokenStorage->getToken()->getUser();
  392.         return $this->renderWithExtraParams('/task/task_import_modal.html.twig', $this->importModalParams(
  393.             $currentUser->getName(),
  394.             'Import time from PDF',
  395.             'Select PDF file',
  396.             '.pdf,application/pdf',
  397.             'pdf_file',
  398.             'task_import_parse',
  399.             '',
  400.             'Time import select pdf',
  401.             'Time import no rows'
  402.         ));
  403.     }
  404.     /**
  405.      * @Route("/admin/app/task/task_import_toptracker", name="task_import_toptracker", methods={"POST"} )
  406.      */
  407.     public function taskImportToptrackerAction(Request $request): Response
  408.     {
  409.         $this->denyAccessUnlessGranted('IS_AUTHENTICATED_FULLY');
  410.         $currentUser = $this->tokenStorage->getToken()->getUser();
  411.         return $this->renderWithExtraParams('/task/task_import_modal.html.twig', $this->importModalParams(
  412.             $currentUser->getName(),
  413.             'Import time from TopTracker',
  414.             'Select TopTracker CSV file',
  415.             '.csv,text/csv',
  416.             'toptracker_file',
  417.             'task_import_toptracker_parse',
  418.             'Time import toptracker upload hint',
  419.             'Time import select toptracker',
  420.             'Time import toptracker no rows'
  421.         ));
  422.     }
  423.     /**
  424.      * @Route("/admin/app/task/task_import_toptracker_parse", name="task_import_toptracker_parse", methods={"POST"} )
  425.      */
  426.     public function taskImportToptrackerParseAction(Request $request): JsonResponse
  427.     {
  428.         $this->denyAccessUnlessGranted('IS_AUTHENTICATED_FULLY');
  429.         /** @var UploadedFile|null $file */
  430.         $file = $request->files->get('toptracker_file');
  431.         if ($file === null || !$file->isValid()) {
  432.             return new JsonResponse(['error' => 'Érvénytelen vagy hiányzó TopTracker CSV fájl.'], 400);
  433.         }
  434.         $extension = strtolower((string) $file->getClientOriginalExtension());
  435.         if (!in_array($extension, ['csv', 'txt'], true)) {
  436.             return new JsonResponse(['error' => 'A TopTracker kivonat CSV fájl legyen.'], 400);
  437.         }
  438.         $content = file_get_contents($file->getPathname());
  439.         if ($content === false) {
  440.             return new JsonResponse(['error' => 'A fájl nem olvasható.'], 400);
  441.         }
  442.         $currentUser = $this->tokenStorage->getToken()->getUser();
  443.         $isSuperAdmin = in_array('ROLE_SUPER_ADMIN', $currentUser->getRoles(), true);
  444.         try {
  445.             $result = $this->taskTimeImportService->parseAndMatchTopTracker($content, $isSuperAdmin);
  446.         } catch (\InvalidArgumentException $e) {
  447.             return new JsonResponse(['error' => $e->getMessage()], 400);
  448.         }
  449.         return new JsonResponse($result, 200);
  450.     }
  451.     /**
  452.      * @return array<string, string>
  453.      */
  454.     private function importModalParams(
  455.         string $importUserName,
  456.         string $importTitle,
  457.         string $fileLabel,
  458.         string $fileAccept,
  459.         string $fileField,
  460.         string $parseRoute,
  461.         string $uploadHint,
  462.         string $missingFileMessage,
  463.         string $noRowsMessage
  464.     ): array {
  465.         return [
  466.             'importUserName' => $importUserName,
  467.             'importTitle' => $importTitle,
  468.             'fileLabel' => $fileLabel,
  469.             'fileAccept' => $fileAccept,
  470.             'fileField' => $fileField,
  471.             'parseUrl' => $this->admin->generateUrl($parseRoute),
  472.             'uploadHint' => $uploadHint,
  473.             'missingFileMessage' => $missingFileMessage,
  474.             'noRowsMessage' => $noRowsMessage,
  475.         ];
  476.     }
  477.     /**
  478.      * @Route("/admin/app/task/task_import_parse", name="task_import_parse", methods={"POST"} )
  479.      */
  480.     public function taskImportParseAction(Request $request): JsonResponse
  481.     {
  482.         $this->denyAccessUnlessGranted('IS_AUTHENTICATED_FULLY');
  483.         /** @var UploadedFile|null $file */
  484.         $file = $request->files->get('pdf_file');
  485.         if ($file === null || !$file->isValid()) {
  486.             return new JsonResponse(['error' => 'Érvénytelen vagy hiányzó PDF fájl.'], 400);
  487.         }
  488.         $content = file_get_contents($file->getPathname());
  489.         if ($content === false) {
  490.             return new JsonResponse(['error' => 'A fájl nem olvasható.'], 400);
  491.         }
  492.         $currentUser = $this->tokenStorage->getToken()->getUser();
  493.         $isSuperAdmin = in_array('ROLE_SUPER_ADMIN', $currentUser->getRoles(), true);
  494.         $result = $this->taskTimeImportService->parseAndMatch($content, $isSuperAdmin);
  495.         return new JsonResponse($result, 200);
  496.     }
  497.     /**
  498.      * @Route("/admin/app/task/task_import_tasks", name="task_import_tasks", methods={"POST"} )
  499.      */
  500.     public function taskImportTasksAction(Request $request): JsonResponse
  501.     {
  502.         $this->denyAccessUnlessGranted('IS_AUTHENTICATED_FULLY');
  503.         $projectId = (int) $request->get('project_id');
  504.         $currentUser = $this->tokenStorage->getToken()->getUser();
  505.         $isSuperAdmin = in_array('ROLE_SUPER_ADMIN', $currentUser->getRoles(), true);
  506.         return new JsonResponse([
  507.             'tasks' => $this->taskTimeImportService->formatTasks($projectId, $isSuperAdmin),
  508.         ]);
  509.     }
  510.     /**
  511.      * @Route("/admin/app/task/task_import_save", name="task_import_save", methods={"POST"} )
  512.      */
  513.     public function taskImportSaveAction(Request $request): JsonResponse
  514.     {
  515.         $this->denyAccessUnlessGranted('IS_AUTHENTICATED_FULLY');
  516.         $data = json_decode($request->getContent(), true);
  517.         $rows = $data['rows'] ?? [];
  518.         if (!is_array($rows) || $rows === []) {
  519.             return new JsonResponse(['error' => 'Nincs mentendő sor.'], 400);
  520.         }
  521.         try {
  522.             $result = $this->taskTimeImportService->saveRows($rows);
  523.             return new JsonResponse($result, 200);
  524.         } catch (\Exception $e) {
  525.             return new JsonResponse(['error' => $e->getMessage()], 400);
  526.         }
  527.     }
  528.     private function formatTasksForSelect(int $projectId, int $excludeTaskId): array
  529.     {
  530.         $items = [];
  531.         foreach ($this->taskRepository->getTasksByProjectId($projectId) as $t) {
  532.             if ($t->getId() === $excludeTaskId) {
  533.                 continue;
  534.             }
  535.             $items[] = [
  536.                 'id' => $t->getId(),
  537.                 'name' => $this->taskDatatableProvider->sanitizeTextForDatatable($t->getName()),
  538.             ];
  539.         }
  540.         return $items;
  541.     }
  542. }