feat: replace notifications with import status in profile dropdown
Remove the notification system entirely and show import progress directly in the user dropdown menu. Block new imports while one is already running. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -7,6 +7,7 @@ namespace App\Controller;
|
||||
use App\Entity\Import;
|
||||
use App\Entity\User;
|
||||
use App\Message\ProcessImportMessage;
|
||||
use App\Repository\ImportRepository;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use League\Flysystem\FilesystemOperator;
|
||||
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||
@@ -19,14 +20,45 @@ use Symfony\Component\Security\Http\Attribute\IsGranted;
|
||||
|
||||
class ImportController extends AbstractController
|
||||
{
|
||||
#[Route('/api/imports/latest', methods: ['GET'])]
|
||||
#[IsGranted('ROLE_USER')]
|
||||
public function latest(ImportRepository $importRepository): JsonResponse
|
||||
{
|
||||
/** @var User $user */
|
||||
$user = $this->getUser();
|
||||
|
||||
$import = $importRepository->findLatestForUser($user);
|
||||
|
||||
if (!$import) {
|
||||
return $this->json(null);
|
||||
}
|
||||
|
||||
return $this->json([
|
||||
'id' => $import->getId(),
|
||||
'status' => $import->getStatus(),
|
||||
'totalFilms' => $import->getTotalFilms(),
|
||||
'failedFilms' => $import->getFailedFilms(),
|
||||
'processedBatches' => $import->getProcessedBatches(),
|
||||
'totalBatches' => $import->getTotalBatches(),
|
||||
]);
|
||||
}
|
||||
|
||||
#[Route('/api/imports', methods: ['POST'])]
|
||||
#[IsGranted('ROLE_USER')]
|
||||
public function create(
|
||||
Request $request,
|
||||
FilesystemOperator $defaultStorage,
|
||||
EntityManagerInterface $em,
|
||||
ImportRepository $importRepository,
|
||||
MessageBusInterface $bus,
|
||||
): JsonResponse {
|
||||
/** @var User $user */
|
||||
$user = $this->getUser();
|
||||
|
||||
if ($importRepository->hasActiveImport($user)) {
|
||||
return $this->json(['error' => 'Un import est déjà en cours.'], Response::HTTP_CONFLICT);
|
||||
}
|
||||
|
||||
$file = $request->files->get('file');
|
||||
|
||||
if (!$file) {
|
||||
@@ -41,9 +73,6 @@ class ImportController extends AbstractController
|
||||
return $this->json(['error' => 'File too large (max 5 MB).'], Response::HTTP_UNPROCESSABLE_ENTITY);
|
||||
}
|
||||
|
||||
/** @var User $user */
|
||||
$user = $this->getUser();
|
||||
|
||||
$import = new Import();
|
||||
$import->setUser($user);
|
||||
$import->setFilePath('pending');
|
||||
|
||||
@@ -1,49 +0,0 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Controller;
|
||||
|
||||
use App\Entity\User;
|
||||
use App\Repository\NotificationRepository;
|
||||
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||
use Symfony\Component\HttpFoundation\JsonResponse;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\Routing\Attribute\Route;
|
||||
use Symfony\Component\Security\Http\Attribute\IsGranted;
|
||||
|
||||
class NotificationController extends AbstractController
|
||||
{
|
||||
#[Route('/api/notifications', methods: ['GET'])]
|
||||
#[IsGranted('ROLE_USER')]
|
||||
public function index(NotificationRepository $notificationRepository): JsonResponse
|
||||
{
|
||||
/** @var User $user */
|
||||
$user = $this->getUser();
|
||||
|
||||
$notifications = $notificationRepository->findRecentForUser($user);
|
||||
$unreadCount = $notificationRepository->countUnreadForUser($user);
|
||||
|
||||
return $this->json([
|
||||
'unreadCount' => $unreadCount,
|
||||
'notifications' => array_map(fn ($n) => [
|
||||
'id' => $n->getId(),
|
||||
'message' => $n->getMessage(),
|
||||
'read' => $n->isRead(),
|
||||
'createdAt' => $n->getCreatedAt()->format('c'),
|
||||
], $notifications),
|
||||
]);
|
||||
}
|
||||
|
||||
#[Route('/api/notifications/read', methods: ['POST'])]
|
||||
#[IsGranted('ROLE_USER')]
|
||||
public function markRead(NotificationRepository $notificationRepository): Response
|
||||
{
|
||||
/** @var User $user */
|
||||
$user = $this->getUser();
|
||||
|
||||
$notificationRepository->markAllReadForUser($user);
|
||||
|
||||
return new Response('', Response::HTTP_NO_CONTENT);
|
||||
}
|
||||
}
|
||||
@@ -1,78 +0,0 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Entity;
|
||||
|
||||
use App\Repository\NotificationRepository;
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
|
||||
#[ORM\Entity(repositoryClass: NotificationRepository::class)]
|
||||
class Notification
|
||||
{
|
||||
#[ORM\Id]
|
||||
#[ORM\GeneratedValue]
|
||||
#[ORM\Column]
|
||||
private ?int $id = null;
|
||||
|
||||
#[ORM\ManyToOne(targetEntity: User::class)]
|
||||
#[ORM\JoinColumn(nullable: false)]
|
||||
private ?User $user = null;
|
||||
|
||||
#[ORM\Column(length: 255)]
|
||||
private ?string $message = null;
|
||||
|
||||
#[ORM\Column(name: 'is_read')]
|
||||
private bool $read = false;
|
||||
|
||||
#[ORM\Column]
|
||||
private \DateTimeImmutable $createdAt;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->createdAt = new \DateTimeImmutable();
|
||||
}
|
||||
|
||||
public function getId(): ?int
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
public function getUser(): ?User
|
||||
{
|
||||
return $this->user;
|
||||
}
|
||||
|
||||
public function setUser(?User $user): static
|
||||
{
|
||||
$this->user = $user;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getMessage(): ?string
|
||||
{
|
||||
return $this->message;
|
||||
}
|
||||
|
||||
public function setMessage(string $message): static
|
||||
{
|
||||
$this->message = $message;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function isRead(): bool
|
||||
{
|
||||
return $this->read;
|
||||
}
|
||||
|
||||
public function setRead(bool $read): static
|
||||
{
|
||||
$this->read = $read;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getCreatedAt(): \DateTimeImmutable
|
||||
{
|
||||
return $this->createdAt;
|
||||
}
|
||||
}
|
||||
@@ -33,4 +33,27 @@ class ImportRepository extends ServiceEntityRepository
|
||||
['id' => $import->getId()]
|
||||
);
|
||||
}
|
||||
|
||||
public function findLatestForUser(\App\Entity\User $user): ?Import
|
||||
{
|
||||
return $this->createQueryBuilder('i')
|
||||
->andWhere('i.user = :user')
|
||||
->setParameter('user', $user)
|
||||
->orderBy('i.createdAt', 'DESC')
|
||||
->setMaxResults(1)
|
||||
->getQuery()
|
||||
->getOneOrNullResult();
|
||||
}
|
||||
|
||||
public function hasActiveImport(\App\Entity\User $user): bool
|
||||
{
|
||||
return (int) $this->createQueryBuilder('i')
|
||||
->select('COUNT(i.id)')
|
||||
->andWhere('i.user = :user')
|
||||
->andWhere('i.status IN (:statuses)')
|
||||
->setParameter('user', $user)
|
||||
->setParameter('statuses', [Import::STATUS_PENDING, Import::STATUS_PROCESSING])
|
||||
->getQuery()
|
||||
->getSingleScalarResult() > 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,54 +0,0 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Repository;
|
||||
|
||||
use App\Entity\Notification;
|
||||
use App\Entity\User;
|
||||
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
|
||||
use Doctrine\Persistence\ManagerRegistry;
|
||||
|
||||
/**
|
||||
* @extends ServiceEntityRepository<Notification>
|
||||
*/
|
||||
class NotificationRepository extends ServiceEntityRepository
|
||||
{
|
||||
public function __construct(ManagerRegistry $registry)
|
||||
{
|
||||
parent::__construct($registry, Notification::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Notification[]
|
||||
*/
|
||||
public function findRecentForUser(User $user, int $limit = 20): array
|
||||
{
|
||||
return $this->createQueryBuilder('n')
|
||||
->andWhere('n.user = :user')
|
||||
->setParameter('user', $user)
|
||||
->orderBy('n.createdAt', 'DESC')
|
||||
->setMaxResults($limit)
|
||||
->getQuery()
|
||||
->getResult();
|
||||
}
|
||||
|
||||
public function countUnreadForUser(User $user): int
|
||||
{
|
||||
return $this->count(['user' => $user, 'read' => false]);
|
||||
}
|
||||
|
||||
public function markAllReadForUser(User $user): void
|
||||
{
|
||||
$this->createQueryBuilder('n')
|
||||
->update()
|
||||
->set('n.read', ':true')
|
||||
->where('n.user = :user')
|
||||
->andWhere('n.read = :false')
|
||||
->setParameter('true', true)
|
||||
->setParameter('false', false)
|
||||
->setParameter('user', $user)
|
||||
->getQuery()
|
||||
->execute();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user