From 226e73e1ebf9063b66fc0ff5b6ab98db64913048 Mon Sep 17 00:00:00 2001 From: Firas <firas.bouzazi@imt-atlantique.net> Date: Wed, 26 Mar 2025 01:33:18 +0100 Subject: [PATCH] major changes --- src/Controller/ItemController.php | 51 +++- src/Controller/WishlistController.php | 21 +- templates/wishlist/show.html.twig | 403 ++------------------------ 3 files changed, 83 insertions(+), 392 deletions(-) diff --git a/src/Controller/ItemController.php b/src/Controller/ItemController.php index 365fbc0b..f4322979 100644 --- a/src/Controller/ItemController.php +++ b/src/Controller/ItemController.php @@ -5,6 +5,7 @@ namespace App\Controller; use App\Entity\Item; use App\Form\ItemType; use App\Repository\ItemRepository; +use App\Repository\WishlistRepository; use Doctrine\ORM\EntityManagerInterface; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Component\HttpFoundation\Request; @@ -22,25 +23,44 @@ final class ItemController extends AbstractController ]); } - #[Route('/new', name: 'app_item_new', methods: ['GET', 'POST'])] - public function new(Request $request, EntityManagerInterface $entityManager): Response - { + + #[Route('/item/new', name: 'app_item_new')] + public function new( + Request $request, + EntityManagerInterface $entityManager, + WishlistRepository $wishlistRepository + ): Response { $item = new Item(); + + // Récupérer l’ID de la wishlist depuis l’URL + $wishlistId = $request->query->get('wishlistId'); + if ($wishlistId) { + $wishlist = $wishlistRepository->find($wishlistId); + if ($wishlist) { + $item->setWishlist($wishlist); + } + } + $form = $this->createForm(ItemType::class, $item); $form->handleRequest($request); - + if ($form->isSubmitted() && $form->isValid()) { $entityManager->persist($item); $entityManager->flush(); - - return $this->redirectToRoute('app_item_index'); + + // ✅ Redirection vers la bonne wishlist + return $this->redirectToRoute('app_wishlist_show', [ + 'id' => $item->getWishlist()->getId() + ]); } - + return $this->render('item/new.html.twig', [ - 'item' => $item, - 'form' => $form, + 'form' => $form->createView(), ]); } + + + #[Route('/{id}', name: 'app_item_show', methods: ['GET'])] public function show(Item $item): Response @@ -55,18 +75,21 @@ final class ItemController extends AbstractController { $form = $this->createForm(ItemType::class, $item); $form->handleRequest($request); - + if ($form->isSubmitted() && $form->isValid()) { $entityManager->flush(); - - return $this->redirectToRoute('app_item_index', [], Response::HTTP_SEE_OTHER); + + return $this->redirectToRoute('app_wishlist_show', [ + 'id' => $item->getWishlist()->getId(), + ], Response::HTTP_SEE_OTHER); } - + return $this->render('item/edit.html.twig', [ 'item' => $item, 'form' => $form, ]); } + #[Route('/{id}', name: 'app_item_delete', methods: ['POST'])] public function delete(Request $request, Item $item, EntityManagerInterface $entityManager): Response @@ -78,4 +101,4 @@ final class ItemController extends AbstractController return $this->redirectToRoute('app_item_index', [], Response::HTTP_SEE_OTHER); } -} +} \ No newline at end of file diff --git a/src/Controller/WishlistController.php b/src/Controller/WishlistController.php index 8348330a..ee45d9a3 100644 --- a/src/Controller/WishlistController.php +++ b/src/Controller/WishlistController.php @@ -47,14 +47,21 @@ final class WishlistController extends AbstractController ]); } - // Method to display a specific wishlist #[Route('/{id}', name: 'app_wishlist_show', methods: ['GET'])] - public function show(Wishlist $wishlist): Response - { - return $this->render('wishlist/show.html.twig', [ - 'wishlist' => $wishlist, // Pass the wishlist entity to the template - ]); - } +public function show(Wishlist $wishlist, Request $request): Response +{ + $sortBy = $request->query->get('sort', 'price_asc'); + $searchQuery = $request->query->get('search', ''); + + return $this->render('wishlist/show.html.twig', [ + 'wishlist' => $wishlist, + 'view_mode' => 'grid', + 'sort_by' => $sortBy, + 'search_query' => $searchQuery, + ]); +} + + // Method to edit an existing wishlist #[Route('/{id}/edit', name: 'app_wishlist_edit', methods: ['GET', 'POST'])] diff --git a/templates/wishlist/show.html.twig b/templates/wishlist/show.html.twig index 9a0dbf67..c3669cb0 100644 --- a/templates/wishlist/show.html.twig +++ b/templates/wishlist/show.html.twig @@ -6,208 +6,10 @@ {% block stylesheets %} {{ parent() }} <style> - /* Common Styles */ - body { - font-family: Arial, sans-serif; - max-width: 1200px; - margin: 0 auto; - padding: 20px; - color: #333; - background-color: #f9f9f9; - } - - .wishlist-header { - display: flex; - justify-content: space-between; - align-items: center; - margin-bottom: 20px; - padding-bottom: 15px; - border-bottom: 1px solid #e1e1e1; - } - - .wishlist-title { - font-size: 28px; - font-weight: bold; - margin: 0; - color: #2c3e50; - } - - .item-count { - font-size: 14px; - color: #7f8c8d; - margin-top: 5px; - } - - .wishlist-actions { - display: flex; - gap: 15px; - align-items: center; - } - - .search-sort-container { - display: flex; - align-items: center; - gap: 10px; - background: white; - padding: 5px; - border-radius: 6px; - box-shadow: 0 2px 4px rgba(0,0,0,0.05); - } - - .view-switcher { - background: none; - border: none; - font-size: 20px; - cursor: pointer; - width: 36px; - height: 36px; - display: flex; - align-items: center; - justify-content: center; - border-radius: 4px; - color: #3498db; - } - - .view-switcher:hover { - background-color: #ebf5fb; - } - - .add-item-btn { - background-color: #3498db; - color: white; - border: none; - cursor: pointer; - font-size: 14px; - padding: 8px 16px; - border-radius: 4px; - transition: background-color 0.2s; - } - - .add-item-btn:hover { - background-color: #2980b9; - } - - .search-bar { - padding: 8px 12px; - border: 1px solid #ddd; - border-radius: 4px; - width: 200px; - font-size: 14px; - } - - .sort-select { - padding: 8px 12px; - border: 1px solid #ddd; - border-radius: 4px; - font-size: 14px; - background: white; - cursor: pointer; - } - - .empty-state { - color: #95a5a6; - text-align: center; - padding: 40px 20px; - font-style: italic; - font-size: 16px; - } - - /* List View Styles */ .items-list { - list-style-type: none; - padding: 0; - margin: 20px 0 0 0; - background: white; - border-radius: 8px; - box-shadow: 0 2px 10px rgba(0,0,0,0.05); - overflow: hidden; display: {% if view_mode != 'grid' %}block{% else %}none{% endif %}; } - - .item { - display: flex; - gap: 20px; - padding: 20px; - border-bottom: 1px solid #eee; - transition: background-color 0.2s; - } - - .item:hover { - background-color: #f8f9fa; - } - - .item-image { - width: 120px; - height: 120px; - object-fit: cover; - border-radius: 8px; - background-color: #f5f5f5; - flex-shrink: 0; - box-shadow: 0 2px 4px rgba(0,0,0,0.1); - } - - .item-details { - flex-grow: 1; - display: flex; - flex-direction: column; - } - - .item-title { - font-size: 18px; - font-weight: bold; - margin: 0 0 8px 0; - color: #2c3e50; - } - - .item-description { - color: #7f8c8d; - margin-bottom: 12px; - line-height: 1.5; - font-size: 14px; - } - - .item-price { - color: #27ae60; - font-weight: bold; - font-size: 16px; - margin-bottom: 15px; - } - - .item-actions { - margin-top: auto; - display: flex; - justify-content: flex-end; - gap: 10px; - } - - .action-btn { - background: none; - border: none; - font-size: 18px; - cursor: pointer; - width: 36px; - height: 36px; - display: flex; - align-items: center; - justify-content: center; - border-radius: 50%; - transition: background-color 0.2s; - } - - .action-btn:hover { - background-color: rgba(0,0,0,0.05); - } - - .buy-btn { color: #27ae60; } - .buy-btn:hover { background-color: rgba(39, 174, 96, 0.1); } - - .edit-btn { color: #f39c12; } - .edit-btn:hover { background-color: rgba(243, 156, 18, 0.1); } - - .delete-btn { color: #e74c3c; } - .delete-btn:hover { background-color: rgba(231, 76, 60, 0.1); } - /* Grid View Styles */ .items-grid { display: {% if view_mode == 'grid' %}grid{% else %}none{% endif %}; grid-template-columns: repeat(auto-fill, minmax(250px, 1fr)); @@ -216,28 +18,24 @@ margin: 20px 0 0 0; list-style-type: none; } - - .grid-item { - background: white; + + .item-image { + width: 120px; + height: 120px; + object-fit: cover; border-radius: 8px; + background-color: #f5f5f5; + flex-shrink: 0; + box-shadow: 0 2px 4px rgba(0,0,0,0.1); overflow: hidden; - box-shadow: 0 2px 10px rgba(0,0,0,0.05); - transition: transform 0.2s, box-shadow 0.2s; - display: flex; - flex-direction: column; } - - .grid-item:hover { - transform: translateY(-5px); - box-shadow: 0 5px 15px rgba(0,0,0,0.1); - } - + .grid-image-container { position: relative; padding-top: 100%; /* 1:1 Aspect Ratio */ overflow: hidden; } - + .grid-image { position: absolute; top: 0; @@ -246,36 +44,8 @@ height: 100%; object-fit: cover; } - - .grid-details { - padding: 15px; - flex-grow: 1; - display: flex; - flex-direction: column; - } - - .grid-title { - font-size: 16px; - font-weight: bold; - margin: 0 0 8px 0; - color: #2c3e50; - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; - } - - .grid-price { - color: #27ae60; - font-weight: bold; - font-size: 15px; - margin-bottom: 12px; - } - - .grid-actions { - display: flex; - justify-content: space-between; - margin-top: auto; - } + + /* Autres styles conservés... */ </style> {% endblock %} @@ -286,7 +56,7 @@ <div class="item-count">{{ wishlist.items|length }} items</div> </div> <div class="wishlist-actions"> - <button class="add-item-btn">Add item</button> + <a href="{{ path('app_item_new', { 'wishlistId': wishlist.id }) }}" class="add-item-btn">Add item</a> <div class="search-sort-container"> <input type="text" class="search-bar" placeholder="Search items..." value="{{ search_query ?? '' }}"> <select class="sort-select" id="sortSelect"> @@ -295,34 +65,32 @@ <option value="name_asc" {% if sort_by == 'name_asc' %}selected{% endif %}>Name: A-Z</option> <option value="name_desc" {% if sort_by == 'name_desc' %}selected{% endif %}>Name: Z-A</option> </select> - <button class="view-switcher" title="Switch view" - onclick="toggleViewMode()"> + <button class="view-switcher" title="Switch view" onclick="toggleViewMode()"> {{ view_mode == 'grid' ? '≡' : '⏹' }} </button> </div> </div> </div> - + {# List View #} <ul class="items-list" id="listView"> {% for item in wishlist.items %} <li class="item" data-item-id="{{ item.id }}" data-price="{{ item.price ?? 0 }}"> - <img src="{{ item.imageUrl ?? 'https://via.placeholder.com/120?text=No+Image' }}" - alt="{{ item.name }}" + <img src="{{ item.image ? asset('uploads/images/' ~ item.image) : 'https://via.placeholder.com/120?text=No+Image' }}" + alt="{{ item.title }}" class="item-image"> <div class="item-details"> - <h3 class="item-title">{{ item.name }}</h3> + <h3 class="item-title">{{ item.title }}</h3> <p class="item-description">{{ item.description ?? 'No description available' }}</p> {% if item.price %} <div class="item-price">${{ item.price|number_format(2) }}</div> {% endif %} <div class="item-actions"> - <button class="action-btn buy-btn" title="Buy" - onclick="window.open('{{ item.purchaseUrl }}', '_blank')">🛒</button> - <button class="action-btn edit-btn" title="Edit" - onclick="window.location.href='{{ path('wishlist_item_edit', {'id': item.id}) }}'">✏️</button> - <button class="action-btn delete-btn" title="Delete" - data-delete-url="{{ path('wishlist_item_delete', {'id': item.id}) }}">🗑️</button> + {% if item.url %} + <button class="action-btn buy-btn" title="Buy" onclick="window.open('{{ item.url }}', '_blank')">🛒</button> + {% endif %} + <button class="action-btn edit-btn" title="Edit" onclick="window.location.href='{{ path('app_item_edit', {'id': item.id}) }}'">✏️</button> + <button class="action-btn delete-btn" title="Delete" data-delete-url="{{ path('app_item_delete', {'id': item.id}) }}">🗑️</button> </div> </div> </li> @@ -330,28 +98,27 @@ <div class="empty-state">No items in this wishlist yet</div> {% endfor %} </ul> - + {# Grid View #} <ul class="items-grid" id="gridView"> {% for item in wishlist.items %} <li class="grid-item" data-item-id="{{ item.id }}" data-price="{{ item.price ?? 0 }}"> <div class="grid-image-container"> - <img src="{{ item.imageUrl ?? 'https://via.placeholder.com/300?text=No+Image' }}" - alt="{{ item.name }}" + <img src="{{ item.image ? asset('uploads/images/' ~ item.image) : 'https://via.placeholder.com/300?text=No+Image' }}" + alt="{{ item.title }}" class="grid-image"> </div> <div class="grid-details"> - <h3 class="grid-title">{{ item.name }}</h3> + <h3 class="grid-title">{{ item.title }}</h3> {% if item.price %} <div class="grid-price">${{ item.price|number_format(2) }}</div> {% endif %} <div class="grid-actions"> - <button class="action-btn buy-btn" title="Buy" - onclick="window.open('{{ item.purchaseUrl }}', '_blank')">🛒</button> - <button class="action-btn edit-btn" title="Edit" - onclick="window.location.href='{{ path('wishlist_item_edit', {'id': item.id}) }}'">✏️</button> - <button class="action-btn delete-btn" title="Delete" - data-delete-url="{{ path('wishlist_item_delete', {'id': item.id}) }}">🗑️</button> + {% if item.url %} + <button class="action-btn buy-btn" title="Buy" onclick="window.open('{{ item.url }}', '_blank')">🛒</button> + {% endif %} + <button class="action-btn edit-btn" title="Edit" onclick="window.location.href='{{ path('app_item_edit', {'id': item.id}) }}'">✏️</button> + <button class="action-btn delete-btn" title="Delete" data-delete-url="{{ path('app_item_delete', {'id': item.id}) }}">🗑️</button> </div> </div> </li> @@ -359,110 +126,4 @@ <div class="empty-state">No items in this wishlist yet</div> {% endfor %} </ul> -{% endblock %} - -{% block javascripts %} - {{ parent() }} - <script> - document.addEventListener('DOMContentLoaded', function() { - // View mode from URL or default to list - const urlParams = new URLSearchParams(window.location.search); - const currentView = urlParams.get('view') || 'list'; - - // Set initial view - toggleViewMode(currentView, false); - - // Delete button action (works for both views) - document.querySelectorAll('.delete-btn').forEach(btn => { - btn.addEventListener('click', function(e) { - e.stopPropagation(); - const itemElement = this.closest('.item, .grid-item'); - const itemName = itemElement.querySelector('.item-title, .grid-title').textContent; - - if (confirm(`Are you sure you want to delete "${itemName}"?`)) { - const deleteUrl = this.getAttribute('data-delete-url'); - fetch(deleteUrl, { - method: 'DELETE', - headers: { - 'X-Requested-With': 'XMLHttpRequest' - } - }) - .then(response => { - if (response.ok) { - itemElement.remove(); - // Update item count - const itemCount = document.querySelectorAll('.item, .grid-item').length; - document.querySelector('.item-count').textContent = - itemCount === 0 ? 'No items' : `${itemCount} item${itemCount !== 1 ? 's' : ''}`; - - if (itemCount === 0) { - document.querySelector('#listView').innerHTML = '<div class="empty-state">No items in this wishlist yet</div>'; - document.querySelector('#gridView').innerHTML = '<div class="empty-state">No items in this wishlist yet</div>'; - } - } - }); - } - }); - }); - - // Search functionality - document.querySelector('.search-bar').addEventListener('input', function(e) { - const searchTerm = e.target.value.toLowerCase(); - const items = document.querySelectorAll('.item, .grid-item'); - - items.forEach(item => { - const text = item.textContent.toLowerCase(); - item.style.display = text.includes(searchTerm) ? - (item.classList.contains('grid-item') ? 'flex' : 'flex') : 'none'; - }); - }); - - // Sort functionality - document.querySelector('#sortSelect').addEventListener('change', function() { - const sortValue = this.value; - const url = new URL(window.location.href); - url.searchParams.set('sort', sortValue); - window.location.href = url.toString(); - }); - - // Click on grid item (excluding buttons) - document.querySelectorAll('.grid-item').forEach(item => { - item.addEventListener('click', function(e) { - if (!e.target.closest('button')) { - const itemId = this.getAttribute('data-item-id'); - window.location.href = `/wishlist/item/${itemId}`; - } - }); - }); - }); - - function toggleViewMode(mode = null, updateUrl = true) { - const listView = document.getElementById('listView'); - const gridView = document.getElementById('gridView'); - const viewSwitcher = document.querySelector('.view-switcher'); - - // Determine new mode - const newMode = mode || (listView.style.display !== 'none' ? 'grid' : 'list'); - - // Toggle displays - if (newMode === 'grid') { - listView.style.display = 'none'; - gridView.style.display = 'grid'; - viewSwitcher.textContent = '≡'; - viewSwitcher.title = 'Switch to list view'; - } else { - listView.style.display = 'block'; - gridView.style.display = 'none'; - viewSwitcher.textContent = '⏹'; - viewSwitcher.title = 'Switch to grid view'; - } - - // Update URL if requested - if (updateUrl) { - const url = new URL(window.location.href); - url.searchParams.set('view', newMode); - window.history.pushState({}, '', url.toString()); - } - } - </script> {% endblock %} \ No newline at end of file -- GitLab