From 1e07eb1d64e0cf5414e33d51017c3059f1cb7080 Mon Sep 17 00:00:00 2001 From: matthieu Date: Tue, 10 Mar 2026 22:22:34 +0100 Subject: [PATCH] feat(time-tracking) : add ActiveTimeEntryProvider, fixtures, and serialization groups - ActiveTimeEntryProvider returns active timer for current user - TimeEntry fixtures with 10 sample entries for the SIRH project - Add time_entry:read group to Project, User, and TaskType for embedded serialization Co-Authored-By: Claude Opus 4.6 --- src/DataFixtures/AppFixtures.php | 32 ++++++++++++++++++++++++++ src/Entity/Project.php | 6 ++--- src/Entity/TaskType.php | 6 ++--- src/Entity/User.php | 4 ++-- src/State/ActiveTimeEntryProvider.php | 33 +++++++++++++++++++++++++++ 5 files changed, 73 insertions(+), 8 deletions(-) create mode 100644 src/State/ActiveTimeEntryProvider.php diff --git a/src/DataFixtures/AppFixtures.php b/src/DataFixtures/AppFixtures.php index d3b18cf..c758c35 100644 --- a/src/DataFixtures/AppFixtures.php +++ b/src/DataFixtures/AppFixtures.php @@ -12,7 +12,10 @@ use App\Entity\TaskGroup; use App\Entity\TaskPriority; use App\Entity\TaskStatus; use App\Entity\TaskType; +use App\Entity\TimeEntry; use App\Entity\User; +use DateTimeImmutable; +use DateTimeZone; use Doctrine\Bundle\FixturesBundle\Fixture; use Doctrine\Persistence\ObjectManager; use Symfony\Component\PasswordHasher\Hasher\UserPasswordHasherInterface; @@ -250,6 +253,35 @@ class AppFixtures extends Fixture $task6->addType($typeAuth); $manager->persist($task6); + // --- Time Entries (SIRH project, admin user) --- + $timeEntryData = [ + ['title' => 'Réunion', 'project' => $projectSirh, 'type' => $typeAuth, 'start' => '09:00', 'stop' => '09:45', 'day' => 1], + ['title' => 'Page accueil', 'project' => $projectSirh, 'type' => $typePassword, 'start' => '10:00', 'stop' => '12:00', 'day' => 0], + ['title' => 'Design admin', 'project' => $projectSirh, 'type' => $typeAuth, 'start' => '09:30', 'stop' => '11:00', 'day' => 2], + ['title' => 'Page accueil', 'project' => $projectSirh, 'type' => $typePassword, 'start' => '10:30', 'stop' => '12:15', 'day' => 1], + ['title' => 'System os', 'project' => $projectSirh, 'type' => $typeCalendar, 'start' => '13:00', 'stop' => '15:30', 'day' => 0], + ['title' => 'Login', 'project' => $projectSirh, 'type' => $typePassword, 'start' => '13:00', 'stop' => '15:00', 'day' => 1], + ['title' => 'Script vault', 'project' => $projectSirh, 'type' => $typeCalendar, 'start' => '10:00', 'stop' => '12:00', 'day' => 3], + ['title' => 'Script backup BDD', 'project' => $projectSirh, 'type' => $typeAuth, 'start' => '13:30', 'stop' => '15:00', 'day' => 3], + ['title' => 'Maquette', 'project' => $projectSirh, 'type' => null, 'start' => '09:00', 'stop' => '11:00', 'day' => 4], + ['title' => 'PC compta', 'project' => $projectSirh, 'type' => null, 'start' => '13:30', 'stop' => '15:30', 'day' => 4], + ]; + + $monday = new DateTimeImmutable('monday this week', new DateTimeZone('UTC')); + + foreach ($timeEntryData as $data) { + $entry = new TimeEntry(); + $entry->setTitle($data['title']); + $entry->setUser($admin); + $entry->setProject($data['project']); + $entry->setStartedAt($monday->modify("+{$data['day']} days")->modify($data['start'])); + $entry->setStoppedAt($monday->modify("+{$data['day']} days")->modify($data['stop'])); + if ($data['type']) { + $entry->addType($data['type']); + } + $manager->persist($entry); + } + $manager->flush(); } } diff --git a/src/Entity/Project.php b/src/Entity/Project.php index b5acd6d..dd2eaa9 100644 --- a/src/Entity/Project.php +++ b/src/Entity/Project.php @@ -32,11 +32,11 @@ class Project #[ORM\Id] #[ORM\GeneratedValue] #[ORM\Column] - #[Groups(['project:read'])] + #[Groups(['project:read', 'time_entry:read'])] private ?int $id = null; #[ORM\Column(length: 255)] - #[Groups(['project:read', 'project:write'])] + #[Groups(['project:read', 'project:write', 'time_entry:read'])] private ?string $name = null; #[ORM\Column(type: 'text', nullable: true)] @@ -44,7 +44,7 @@ class Project private ?string $description = null; #[ORM\Column(length: 7)] - #[Groups(['project:read', 'project:write'])] + #[Groups(['project:read', 'project:write', 'time_entry:read'])] private ?string $color = '#222783'; #[ORM\ManyToOne(targetEntity: Client::class, inversedBy: 'projects')] diff --git a/src/Entity/TaskType.php b/src/Entity/TaskType.php index 6e87866..e198edc 100644 --- a/src/Entity/TaskType.php +++ b/src/Entity/TaskType.php @@ -32,15 +32,15 @@ class TaskType #[ORM\Id] #[ORM\GeneratedValue] #[ORM\Column] - #[Groups(['task_type:read', 'task:read'])] + #[Groups(['task_type:read', 'task:read', 'time_entry:read'])] private ?int $id = null; #[ORM\Column(length: 255)] - #[Groups(['task_type:read', 'task_type:write', 'task:read'])] + #[Groups(['task_type:read', 'task_type:write', 'task:read', 'time_entry:read'])] private ?string $label = null; #[ORM\Column(length: 7)] - #[Groups(['task_type:read', 'task_type:write', 'task:read'])] + #[Groups(['task_type:read', 'task_type:write', 'task:read', 'time_entry:read'])] private ?string $color = '#222783'; public function getId(): ?int diff --git a/src/Entity/User.php b/src/Entity/User.php index fb40e9d..2798069 100644 --- a/src/Entity/User.php +++ b/src/Entity/User.php @@ -46,11 +46,11 @@ class User implements UserInterface, PasswordAuthenticatedUserInterface #[ORM\Id] #[ORM\GeneratedValue] #[ORM\Column] - #[Groups(['me:read', 'task:read', 'user:list'])] + #[Groups(['me:read', 'task:read', 'user:list', 'time_entry:read'])] private ?int $id = null; #[ORM\Column(length: 180, unique: true)] - #[Groups(['me:read', 'task:read', 'user:list', 'user:write'])] + #[Groups(['me:read', 'task:read', 'user:list', 'user:write', 'time_entry:read'])] private ?string $username = null; /** @var list */ diff --git a/src/State/ActiveTimeEntryProvider.php b/src/State/ActiveTimeEntryProvider.php new file mode 100644 index 0000000..87b0cf4 --- /dev/null +++ b/src/State/ActiveTimeEntryProvider.php @@ -0,0 +1,33 @@ + + */ +final readonly class ActiveTimeEntryProvider implements ProviderInterface +{ + public function __construct( + private Security $security, + private TimeEntryRepository $timeEntryRepository, + ) {} + + public function provide(Operation $operation, array $uriVariables = [], array $context = []): ?TimeEntry + { + $user = $this->security->getUser(); + + if (!$user) { + return null; + } + + return $this->timeEntryRepository->findActiveByUser($user); + } +}