diff --git a/src/Controller/HomeController.php b/src/Controller/HomeController.php index 29c1c1c3483af43cd31cadfcc1be12ae721bf36e..b046743b1d974ea203baec3d3e5f6a2c5b82b855 100644 --- a/src/Controller/HomeController.php +++ b/src/Controller/HomeController.php @@ -11,16 +11,30 @@ class HomeController extends AbstractController #[Route('/', name: 'homepage')] public function index(): Response { + $user = $this->getUser(); // Récupère l'utilisateur connecté + + $links = [ + ]; + + // Ajoutez le lien "Admin Dashboard" uniquement si l'utilisateur est admin + if ($user && $user->isAdmin()) { + $links['Admin Dashboard'] = $this->generateUrl('admin_dashboard'); + } + + if (!$user) { + $links['Register'] = $this->generateUrl('register'); + $links['Login'] = $this->generateUrl('login'); + } + + if ($user) { + $links['My Wishlists'] = $this->generateUrl('app_wishlist_index'); + $links['Profile'] = $this->generateUrl('user_profile'); + $links['Logout'] = $this->generateUrl('logout'); + + } + return $this->render('home/index.html.twig', [ - 'links' => [ - 'Register' => $this->generateUrl('register'), - 'Login' => $this->generateUrl('login'), - 'My Wishlists' => $this->generateUrl('app_wishlist_index'), - 'Admin Dashboard' => $this->generateUrl('admin_dashboard'), - 'Profile' => $this->generateUrl(route: 'user_profile'), - 'Logout' => $this->generateUrl(route: 'logout'), - - ], + 'links' => $links, ]); } } \ No newline at end of file diff --git a/templates/wishlist/show.html.twig b/templates/wishlist/show.html.twig index bead562e103c7b9b96a311fbbdaf2803e02a27e4..9a0dbf67377480d49d650c582003ee801a8f29d5 100644 --- a/templates/wishlist/show.html.twig +++ b/templates/wishlist/show.html.twig @@ -6,12 +6,14 @@ {% block stylesheets %} {{ parent() }} <style> + /* Common Styles */ body { font-family: Arial, sans-serif; - max-width: 800px; + max-width: 1200px; margin: 0 auto; padding: 20px; color: #333; + background-color: #f9f9f9; } .wishlist-header { @@ -19,14 +21,21 @@ justify-content: space-between; align-items: center; margin-bottom: 20px; - border-bottom: 1px solid #eee; padding-bottom: 15px; + border-bottom: 1px solid #e1e1e1; } .wishlist-title { - font-size: 24px; + font-size: 28px; font-weight: bold; margin: 0; + color: #2c3e50; + } + + .item-count { + font-size: 14px; + color: #7f8c8d; + margin-top: 5px; } .wishlist-actions { @@ -35,10 +44,14 @@ align-items: center; } - .search-view-container { + .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 { @@ -46,25 +59,32 @@ border: none; font-size: 20px; cursor: pointer; - width: 32px; - height: 32px; + width: 36px; + height: 36px; display: flex; align-items: center; justify-content: center; border-radius: 4px; + color: #3498db; } .view-switcher:hover { - background-color: #f5f5f5; + background-color: #ebf5fb; } .add-item-btn { - background: none; + background-color: #3498db; + color: white; border: none; - color: #007bff; cursor: pointer; font-size: 14px; - padding: 5px 10px; + padding: 8px 16px; + border-radius: 4px; + transition: background-color 0.2s; + } + + .add-item-btn:hover { + background-color: #2980b9; } .search-bar { @@ -72,19 +92,48 @@ 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: 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 0; + padding: 20px; border-bottom: 1px solid #eee; + transition: background-color 0.2s; + } + + .item:hover { + background-color: #f8f9fa; } .item-image { @@ -94,34 +143,41 @@ 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: #666; + color: #7f8c8d; margin-bottom: 12px; - line-height: 1.4; + line-height: 1.5; + font-size: 14px; } .item-price { - color: #28a745; + color: #27ae60; font-weight: bold; + font-size: 16px; margin-bottom: 15px; } .item-actions { + margin-top: auto; display: flex; justify-content: flex-end; - gap: 12px; + gap: 10px; } .action-btn { @@ -129,33 +185,96 @@ border: none; font-size: 18px; cursor: pointer; - width: 32px; - height: 32px; + width: 36px; + height: 36px; display: flex; align-items: center; justify-content: center; - border-radius: 4px; + border-radius: 50%; + transition: background-color 0.2s; } .action-btn:hover { background-color: rgba(0,0,0,0.05); } - .buy-btn { color: #28a745; } - .edit-btn { color: #ffc107; } - .delete-btn { color: #dc3545; } + .buy-btn { color: #27ae60; } + .buy-btn:hover { background-color: rgba(39, 174, 96, 0.1); } - .empty-state { - color: #6c757d; - text-align: center; - padding: 40px 0; - font-style: italic; + .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)); + gap: 20px; + padding: 0; + margin: 20px 0 0 0; + list-style-type: none; } - .item-count { - font-size: 14px; - color: #6c757d; - margin-top: 5px; + .grid-item { + background: white; + border-radius: 8px; + 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; + left: 0; + width: 100%; + 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; } </style> {% endblock %} @@ -168,16 +287,26 @@ </div> <div class="wishlist-actions"> <button class="add-item-btn">Add item</button> - <div class="search-view-container"> - <input type="text" class="search-bar" placeholder="Search..."> - <button class="view-switcher" title="Change view">≡</button> + <div class="search-sort-container"> + <input type="text" class="search-bar" placeholder="Search items..." value="{{ search_query ?? '' }}"> + <select class="sort-select" id="sortSelect"> + <option value="price_asc" {% if sort_by == 'price_asc' %}selected{% endif %}>Price: Low to High</option> + <option value="price_desc" {% if sort_by == 'price_desc' %}selected{% endif %}>Price: High to Low</option> + <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()"> + {{ view_mode == 'grid' ? '≡' : '⏹' }} + </button> </div> </div> </div> - <ul class="items-list"> + {# List View #} + <ul class="items-list" id="listView"> {% for item in wishlist.items %} - <li class="item" data-item-id="{{ item.id }}"> + <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 }}" class="item-image"> @@ -201,16 +330,56 @@ <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 }}" + class="grid-image"> + </div> + <div class="grid-details"> + <h3 class="grid-title">{{ item.name }}</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> + </div> + </div> + </li> + {% else %} + <div class="empty-state">No items in this wishlist yet</div> + {% endfor %} + </ul> {% endblock %} {% block javascripts %} {{ parent() }} <script> document.addEventListener('DOMContentLoaded', function() { - // Delete button action + // 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() { - if (confirm('Are you sure you want to delete this item?')) { + 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', @@ -220,15 +389,80 @@ }) .then(response => { if (response.ok) { - this.closest('.item').remove(); + itemElement.remove(); // Update item count - const itemCount = document.querySelectorAll('.item').length; - document.querySelector('.item-count').textContent = itemCount + ' items'; + 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