diff --git a/.gitignore b/.gitignore index 69f2383d58307c2cf792b5b6d4961826a651ef5a..65da71214f3f225fd6c76a4c82bd0af6fdee6ab9 100644 --- a/.gitignore +++ b/.gitignore @@ -9,4 +9,4 @@ vendor/ .idea/ docker/server migrations/ -public/uploads \ No newline at end of file +public/uploads diff --git a/public/css/style.css b/public/css/style.css index 31d7b1329076bcc7230699698fbeceee9b48180f..a31b23f2ad3ceab1f91774fdcee8db1f5121215a 100644 --- a/public/css/style.css +++ b/public/css/style.css @@ -322,6 +322,12 @@ body { box-shadow: 0 0.125rem 0.25rem rgba(0, 0, 0, 0.075); } + +.card-body { + padding: 16px; + text-align: center; +} + /* Tables */ .table thead th { background-color: var(--imt-primary); diff --git a/src/Controller/AdminController.php b/src/Controller/AdminController.php index a4477ee421b398d76140ebe76dd3bf4f200e3aa6..d6856fb91f600c3cda3379622b8f3909eafab87c 100644 --- a/src/Controller/AdminController.php +++ b/src/Controller/AdminController.php @@ -8,7 +8,9 @@ use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\Routing\Attribute\Route; use Doctrine\ORM\EntityManagerInterface; +use Symfony\Component\HttpFoundation\RedirectResponse; +use App\Entity\User; use App\Entity\Item; use App\Entity\Wishlist; use Twig\TokenParser\UseTokenParser; @@ -55,31 +57,36 @@ final class AdminController extends AbstractController ]); } + + #[Route('/admin/user/{id}/lock', name: 'admin_user_lock', methods: ['POST'])] - public function lockUser(User $user, EntityManagerInterface $entityManager): Response + public function lockUser(User $user, EntityManagerInterface $entityManager): RedirectResponse { $user->setIsLocked(true); $entityManager->flush(); - return $this->redirectToRoute('admin_users'); + $this->addFlash('success', "User has been locked."); + return $this->redirectToRoute('admin_dashboard'); } #[Route('/admin/user/{id}/unlock', name: 'admin_user_unlock', methods: ['POST'])] - public function unlockUser(User $user, EntityManagerInterface $entityManager): Response + public function unlockUser(User $user, EntityManagerInterface $entityManager): RedirectResponse { $user->setIsLocked(false); $entityManager->flush(); - return $this->redirectToRoute('admin_users'); + $this->addFlash('success', "User has been unlocked."); + return $this->redirectToRoute('admin_dashboard'); } #[Route('/admin/user/{id}/delete', name: 'admin_user_delete', methods: ['POST'])] - public function deleteUser(User $user, EntityManagerInterface $entityManager): Response + public function deleteUser(User $user, EntityManagerInterface $entityManager): RedirectResponse { - $entityManager->remove($user); + $entityManager->remove(object: $user); $entityManager->flush(); - return $this->redirectToRoute('admin_users'); + $this->addFlash('success', "User has been deleted."); + return $this->redirectToRoute('admin_dashboard'); } } diff --git a/src/Controller/PurchaseProofController.php b/src/Controller/PurchaseProofController.php index 935dc5f36a8de9061647f7ab895b6f327a7ea270..40a13dd1cf680551b62e98fc22ba7b01acebd4ea 100644 --- a/src/Controller/PurchaseProofController.php +++ b/src/Controller/PurchaseProofController.php @@ -23,8 +23,8 @@ class PurchaseProofController extends AbstractController } $purchaseProof = new PurchaseProof(); - $purchaseProof->setItem($item); - + $purchaseProof->setItem(item: $item); + $form = $this->createForm(PurchaseProofType::class, $purchaseProof); $form->handleRequest($request); diff --git a/src/Controller/WishlistController.php b/src/Controller/WishlistController.php index ee45d9a3d54b1726ff2e11c9b1009a9531f6ffda..b1a6d1a0181cce4c6cb03f67cf6ab9a19bb948e2 100644 --- a/src/Controller/WishlistController.php +++ b/src/Controller/WishlistController.php @@ -49,17 +49,36 @@ final class WishlistController extends AbstractController #[Route('/{id}', name: 'app_wishlist_show', methods: ['GET'])] public function show(Wishlist $wishlist, Request $request): Response -{ + { $sortBy = $request->query->get('sort', 'price_asc'); $searchQuery = $request->query->get('search', ''); + // Convert PersistentCollection to an array + $items = $wishlist->getItems()->toArray(); + + // Sort items based on the sortBy parameter + if ($sortBy === 'price_asc') { + usort($items, fn($a, $b) => $a->getPrice() <=> $b->getPrice()); + } elseif ($sortBy === 'price_desc') { + usort($items, fn($a, $b) => $b->getPrice() <=> $a->getPrice()); + } + elseif ($sortBy === 'name_asc') { + // Sort items alphabetically in ascending order + usort($items, fn($a, $b) => strcmp($a->getTitle(), $b->getTitle())); + } elseif ($sortBy === 'name_desc') { + // Sort items alphabetically in descending order + usort($items, fn($a, $b) => strcmp($b->getTitle(), $a->getTitle())); + } + + return $this->render('wishlist/show.html.twig', [ 'wishlist' => $wishlist, + 'items' => $items, // Pass sorted items to the template 'view_mode' => 'grid', 'sort_by' => $sortBy, 'search_query' => $searchQuery, ]); -} + } diff --git a/src/Entity/Item.php b/src/Entity/Item.php index 3ebfe22ed9782e4b5c457b8848f4f647de6ce39b..a56313aa17cdc2ec10b6185655ec6a6e7363aba8 100644 --- a/src/Entity/Item.php +++ b/src/Entity/Item.php @@ -39,10 +39,10 @@ class Item #[ORM\Column] private ?float $price = null; - #[ORM\OneToOne(mappedBy: 'item', targetEntity: PurchaseProof::class, cascade: ['persist', 'remove'])] + #[ORM\OneToOne(mappedBy: 'item', targetEntity: PurchaseProof::class, cascade: ['remove'])] private ?PurchaseProof $purchaseProof = null; - #[ORM\ManyToOne(inversedBy: 'items')] + #[ORM\ManyToOne(inversedBy: 'items', cascade: ['remove'])] #[ORM\JoinColumn(nullable: false)] private ?Wishlist $wishlist = null; diff --git a/src/Entity/PurchaseProof.php b/src/Entity/PurchaseProof.php index 3667d91336b614fb18316721b0eb26c1dd02b732..f8638c435a62c4be7c1c9b1e39bd01aff66e5404 100644 --- a/src/Entity/PurchaseProof.php +++ b/src/Entity/PurchaseProof.php @@ -23,8 +23,8 @@ class PurchaseProof #[ORM\JoinColumn(nullable: false)] private ?Item $item = null; - #[ORM\ManyToOne(targetEntity: User::class, inversedBy: 'purchaseProofs')] - #[ORM\JoinColumn(nullable: false)] + #[ORM\ManyToOne(targetEntity: User::class, inversedBy: 'purchaseProofs',cascade: ['remove'])] + #[ORM\JoinColumn(nullable: false, onDelete: 'CASCADE')] private ?User $buyer = null; public function getId(): ?int diff --git a/src/Entity/Wishlist.php b/src/Entity/Wishlist.php index 60d02f6b46676bc0c8339747922d1879a7317770..871bfe5b7f6ed6a30ea6cef28f0be629af3562f6 100644 --- a/src/Entity/Wishlist.php +++ b/src/Entity/Wishlist.php @@ -29,11 +29,13 @@ class Wishlist /** * @var Collection<int, Item> */ - #[ORM\OneToMany(targetEntity: Item::class, mappedBy: 'wishlist', orphanRemoval: true)] + #[ORM\OneToMany(targetEntity: Item::class, mappedBy: 'wishlist', orphanRemoval: true, cascade: ['remove'])] + #[ORM\JoinColumn(nullable: false, onDelete: 'CASCADE')] + private Collection $items; - #[ORM\ManyToOne(targetEntity: User::class, inversedBy: 'wishlists')] - #[ORM\JoinColumn(nullable: false)] + #[ORM\ManyToOne(targetEntity: User::class, inversedBy: 'wishlists', cascade: ['remove'] )] + #[ORM\JoinColumn(nullable: false, onDelete: 'CASCADE')] private ?User $owner = null; public function getOwner(): ?User @@ -150,14 +152,15 @@ class Wishlist } - public function wishlistTotalPrice() { - $itemsArray = (($this->items->toArray())) ; - - $total = 0 ; + public function wishlistTotalPrice(): float + { + $itemsArray = $this->items->toArray(); // Pas besoin des doubles parenthèses + $total = 0; + for ($i = 0; $i < count($itemsArray); $i++) { - $total += $itemsArray[i]->getPrice() ; + $total += $itemsArray[$i]->getPrice(); // Utilisation correcte des crochets } - - return $total ; + + return $total; } } diff --git a/src/Repository/WishlistRepository.php b/src/Repository/WishlistRepository.php index 637aa1d319898d6ff827e43b4fcd4680e156abf8..3b71436d150bf4db8b2afb6158f9ea431eb8519a 100644 --- a/src/Repository/WishlistRepository.php +++ b/src/Repository/WishlistRepository.php @@ -58,8 +58,8 @@ class WishlistRepository extends ServiceEntityRepository usort($rankings, callback: function($a, $b) {return $a['total'] - $b['total'];}); } else { for ($i = 0; $i < sizeof($rankings) ; $i++ ) { - if ($rankings[i]['total'] < $total ) { - $rankings[i] = ['wishlist' => $wishlist, 'total' => $total] ; + if ($rankings[$i]['total'] < $total ) { + $rankings[$i] = ['wishlist' => $wishlist, 'total' => $total] ; } } @@ -68,7 +68,7 @@ class WishlistRepository extends ServiceEntityRepository } $result = array() ; for ($i = 0 ; $i < sizeof($rankings) ; $i++ ) { - $result[] = $rankings[i]['wishlist'] ; + $result[] = $rankings[$i]['wishlist'] ; } return $result; } diff --git a/templates/admin/dashboard.html.twig b/templates/admin/dashboard.html.twig index 8c663bf6246bcb3e7b9dbdbafa838a3394d31cf9..ad56b4c4c43d598a4ff2fdc68185a156669b4435 100644 --- a/templates/admin/dashboard.html.twig +++ b/templates/admin/dashboard.html.twig @@ -39,7 +39,7 @@ {% for wishlist in topWishlists %} <li class="list-group-item d-flex justify-content-between align-items-center"> {{ wishlist.name }} - <span class="badge bg-success rounded-pill">{{ wishlist.totalValue }} €</span> + <span class="badge bg-success rounded-pill">{{ wishlist.wishlistTotalPrice() }} €</span> </li> {% endfor %} </ul> @@ -72,7 +72,8 @@ {% for user in users %} <tr> <td>{{ user.id }}</td> - <td>{{ user.username }}</td> + <td>{{ user.firstName }}</td> + <td>{{ user.lastName }}</td> <td>{{ user.email }}</td> <td> {% if user.isLocked %} diff --git a/templates/admin/users.html.twig b/templates/admin/users.html.twig new file mode 100644 index 0000000000000000000000000000000000000000..f663e661e79720f37ada66c09686c02ba51cc525 --- /dev/null +++ b/templates/admin/users.html.twig @@ -0,0 +1,66 @@ +{% extends 'base.html.twig' %} + +{% block title %}User Management{% endblock %} + +{% block body %} + <header> + <div class="admin-icon"></div> + <h1><a href="#">User Management</a></h1> + <input type="text" placeholder="Search users…" class="search-bar"> + </header> + + <main> + <div class="container"> + <section class="form-section"> + <h1>User List</h1> + + <table class="table"> + <thead> + <tr> + <th>Id</th> + <th>First Name</th> + <th>Last Name</th> + <th>Email</th> + <th>Is Locked</th> + <th>Is Admin</th> + <th>Actions</th> + </tr> + </thead> + <tbody> + {% for user in users %} + <tr> + <td>{{ user.id }}</td> + <td>{{ user.firstName }}</td> + <td>{{ user.lastName }}</td> + <td>{{ user.email }}</td> + <td> + {% if user.isLocked %} + <span class="text-danger">Yes</span> + {% else %} + <span class="text-success">No</span> + {% endif %} + </td> + <td> + {% if user.isAdmin %} + <span class="text-primary">Yes</span> + {% else %} + <span class="text-secondary">No</span> + {% endif %} + </td> + <td> + <a href="{{ path('admin_user_lock', {'id': user.id}) }}" class="btn btn-danger btn-sm">Lock</a> + <a href="{{ path('admin_user_unlock', {'id': user.id}) }}" class="btn btn-success btn-sm">Unlock</a> + <a href="{{ path('admin_user_delete', {'id': user.id}) }}" class="btn btn-warning btn-sm">Delete</a> + </td> + </tr> + {% else %} + <tr> + <td colspan="7">No users found</td> + </tr> + {% endfor %} + </tbody> + </table> + </section> + </div> + </main> +{% endblock %} \ No newline at end of file diff --git a/templates/wishlist/show.html.twig b/templates/wishlist/show.html.twig index e917e95f313a06e029f0076cf4200326e759ec31..de9582b211e2f46020c91b2e3397808eddc7fc4f 100644 --- a/templates/wishlist/show.html.twig +++ b/templates/wishlist/show.html.twig @@ -171,18 +171,18 @@ <div class="hero"> <h1>{{ wishlist.name }}</h1> - <p>{{ wishlist.items|length }} items total</p> + <p>{{ items|length }} items total</p> <a href="{{ path('app_item_new', { 'wishlistId': wishlist.id }) }}" class="add-btn">+ Add New Item</a> </div> <div class="grid-container"> - {% for item in wishlist.items %} + {% for item in items %} <div class="card"> <div class="card-image"> <img src="{{ item.image ? asset('uploads/images/' ~ item.image) : 'https://via.placeholder.com/300?text=No+Image' }}" alt="{{ item.title }}"> </div> <div class="card-body"> - <h3>{{ item.title }}</h3> + <h1>{{ item.title }}</h1> <p>{{ item.description|default('No description') }}</p> </div> <div class="card-footer">