diff --git a/src/Controller/ItemController.php b/src/Controller/ItemController.php index 365fbc0baee599846533aec362f6f07868e7aa69..560ed7c045f454416f9e02708f700735735e07b3 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,27 +75,35 @@ 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 { - if ($this->isCsrfTokenValid('delete'.$item->getId(), $request->getPayload()->getString('_token'))) { + $wishlistId = $item->getWishlist()->getId(); + + if ($this->isCsrfTokenValid('delete' . $item->getId(), $request->request->get('_token'))) { $entityManager->remove($item); $entityManager->flush(); } - - return $this->redirectToRoute('app_item_index', [], Response::HTTP_SEE_OTHER); + + return $this->redirectToRoute('app_wishlist_show', [ + 'id' => $wishlistId + ]); } -} + +} \ No newline at end of file diff --git a/src/Controller/WishlistController.php b/src/Controller/WishlistController.php index 8348330a144ac967c5996021eb788751c91e6a82..ee45d9a3d54b1726ff2e11c9b1009a9531f6ffda 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/src/Form/ItemType.php b/src/Form/ItemType.php index a54aba6180dd3f6bf3a09aed6a85a28295d9a0ea..864d946e07d1ca0a161afe8abe78b56b079e717a 100644 --- a/src/Form/ItemType.php +++ b/src/Form/ItemType.php @@ -20,11 +20,11 @@ class ItemType extends AbstractType ->add('price', NumberType::class, ['label' => 'Price']) ->add('description', TextType::class, ['label' => 'Description']) ->add('imageFile', VichImageType::class, [ - 'label' => 'Upload a new image', // ✅ Change le label pour éviter la confusion - 'required' => false, // ✅ Permet de ne pas forcer un nouvel upload - 'allow_delete' => false, // ✅ Désactive la suppression automatique - 'download_uri' => false, // ✅ Désactive le lien de téléchargement - 'image_uri' => false, // ✅ Empêche l'affichage de l'image dans l'input + 'label' => 'Upload a new image', + 'required' => false, + 'allow_delete' => false, + 'download_uri' => false, + 'image_uri' => false, ]); } @@ -32,9 +32,9 @@ class ItemType extends AbstractType { $resolver->setDefaults([ 'data_class' => Item::class, - 'csrf_protection' => true, // ✅ Active la protection CSRF - 'csrf_field_name' => '_token', // ✅ Définit le nom du champ CSRF - 'csrf_token_id' => 'submit', // ✅ Identifie le token + 'csrf_protection' => true, + 'csrf_field_name' => '_token', + 'csrf_token_id' => 'submit', ]); } } diff --git a/templates/item/index.html.twig b/templates/item/index.html.twig index ea9218712fb00085a6958ab9bb79fc155c247748..05077d5302f07b0776901580acbedf7bfba34122 100644 --- a/templates/item/index.html.twig +++ b/templates/item/index.html.twig @@ -2,6 +2,66 @@ {% block title %}Item index{% endblock %} +{% block stylesheets %} + {{ parent() }} + <style> + body { + font-family: Arial, sans-serif; + background: linear-gradient(135deg, #00B8DE, #99CC33) fixed; + color: white; + text-align: center; + padding: 20px; + } + .container { + max-width: 90%; + margin: auto; + background: rgba(255, 255, 255, 0.2); + padding: 20px; + border-radius: 15px; + box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2); + backdrop-filter: blur(10px); + overflow-x: auto; + } + table { + width: 100%; + border-collapse: collapse; + background: rgba(255, 255, 255, 0.9); + color: black; + border-radius: 10px; + overflow: hidden; + } + th, td { + padding: 12px; + text-align: left; + border-bottom: 2px solid #99CC33; + } + th { + background: #00B8DE; + color: white; + } + tr:hover { + background-color: rgba(153, 204, 51, 0.3); + } + .btn { + background-color: white; + color: #00B8DE; + border: 2px solid #00B8DE; + padding: 10px 15px; + border-radius: 5px; + font-size: 1rem; + cursor: pointer; + transition: 0.3s; + display: inline-block; + margin-top: 15px; + } + .btn:hover { + background-color: #99CC33; + color: white; + border-color: #99CC33; + } + </style> +{% endblock %} + {% block body %} <header> <div class="user-icon"></div> @@ -13,45 +73,41 @@ <div class="container"> <section class="form-section"> <h1>Item index</h1> - <table class="table"> <thead> - <tr> - <th>Id</th> - <th>Title</th> - <th>Description</th> - <th>Url</th> - <th>Image</th> - <th>Price</th> - <th>Actions</th> - </tr> - </thead> - <tbody> - {% for item in items %} - <tr> - <td>{{ item.id }}</td> - <td>{{ item.title }}</td> - <td>{{ item.description }}</td> - <td>{{ item.url }}</td> - <td>{{ item.image }}</td> - <td>{{ item.price }}</td> - <td> - <a href="{{ path('app_item_show', {'id': item.id}) }}">Show</a> - <a href="{{ path('app_item_edit', {'id': item.id}) }}">Edit</a> - </td> - </tr> - {% else %} <tr> - <td colspan="7">No records found</td> + <th>Id</th> + <th>Title</th> + <th>Description</th> + <th>Url</th> + <th>Image</th> + <th>Price</th> + <th>Actions</th> </tr> - {% endfor %} + </thead> + <tbody> + {% for item in items %} + <tr> + <td>{{ item.id }}</td> + <td>{{ item.title }}</td> + <td>{{ item.description }}</td> + <td><a href="{{ item.url }}" target="_blank">Link</a></td> + <td><img src="{{ asset('uploads/' ~ item.image) }}" alt="{{ item.title }}" width="50"></td> + <td>{{ item.price }}</td> + <td> + <a href="{{ path('app_item_show', {'id': item.id}) }}">Show</a> + <a href="{{ path('app_item_edit', {'id': item.id}) }}">Edit</a> + </td> + </tr> + {% else %} + <tr> + <td colspan="7">No records found</td> + </tr> + {% endfor %} </tbody> </table> - <a href="{{ path('app_item_new') }}" class="btn">Create new</a> </section> </div> </main> - - {% endblock %} diff --git a/templates/item/new.html.twig b/templates/item/new.html.twig index 9773d03919bc8ece5a13a2a4c4505890726c226e..f12d2d5ff74878fdd412188d7156ccafc03d2143 100644 --- a/templates/item/new.html.twig +++ b/templates/item/new.html.twig @@ -2,10 +2,85 @@ {% block title %}New Item{% endblock %} +{% block stylesheets %} + {{ parent() }} + <style> + body { + font-family: Arial, sans-serif; + background: linear-gradient(135deg, #00B8DE, #99CC33) fixed; + color: white; + text-align: center; + padding: 20px; + } + .container { + max-width: 800px; + margin: auto; + } + header { + display: flex; + justify-content: space-between; + align-items: center; + padding: 10px 20px; + background: rgba(255, 255, 255, 0.2); + border-radius: 10px; + backdrop-filter: blur(10px); + } + .search-bar { + padding: 5px; + border: 2px solid #99CC33; + border-radius: 5px; + } + .form-section { + margin-top: 20px; + padding: 20px; + background: rgba(255, 255, 255, 0.2); + border-radius: 10px; + border: 2px solid #99CC33; + box-shadow: 0 4px 8px rgba(0,0,0,0.2); + text-align: left; + } + .input-box { + display: flex; + flex-direction: column; + } + .form-group { + margin-bottom: 15px; + } + .form-control { + width: 100%; + padding: 10px; + border: 2px solid #99CC33; + border-radius: 5px; + font-size: 1rem; + } + .btn { + background-color: white; + color: #00B8DE; + border: 2px solid #00B8DE; + padding: 10px 15px; + border-radius: 5px; + font-size: 1rem; + cursor: pointer; + transition: 0.3s; + margin-top: 15px; + } + .btn:hover { + background-color: #99CC33; + color: white; + border-color: #99CC33; + } + @media (max-width: 768px) { + .form-section { + width: 90%; + margin: auto; + } + } + </style> +{% endblock %} + {% block body %} <header> - <div class="user-icon"></div> - <h1><a href="{{ path('app_item_index') }}">Item title</a></h1> + <h1>New Item</a></h1> <input type="text" placeholder="Search…" class="search-bar"> </header> @@ -13,16 +88,19 @@ <div class="container"> <section class="form-section"> {{ form_start(form, {'attr': {'class': 'input-box'}}) }} - {{ form_row(form.title) }} - {{ form_row(form.url) }} - {{ form_row(form.price) }} - {{ form_row(form.description) }} - <!-- Upload d’image --> - <h3>Item image</h3> - {{ form_row(form.imageFile) }} + {{ form_row(form.title, {'attr': {'class': 'form-control'}}) }} + {{ form_row(form.url, {'attr': {'class': 'form-control'}}) }} + {{ form_row(form.price, {'attr': {'class': 'form-control'}}) }} + {{ form_row(form.description, {'attr': {'class': 'form-control'}}) }} + + <h3 style="margin-top: 20px;">Item Image</h3> + {{ form_row(form.imageFile, {'attr': {'class': 'form-control'}}) }} + + {{ form_rest(form) }} {# ✅ Inclut le token CSRF ici #} + + <button class="btn" type="submit">Confirm</button> - <button class="btn">Confirm</button> {{ form_end(form) }} </section> </div> diff --git a/templates/item/show.html.twig b/templates/item/show.html.twig index e7c5f66933b3779c025fe9f91a0b324b0cd5d73d..508aa78a076a84a330d015e0bd1809dd36e80dee 100644 --- a/templates/item/show.html.twig +++ b/templates/item/show.html.twig @@ -1,42 +1,112 @@ {% extends 'base.html.twig' %} -{% block title %}Item{% endblock %} +{% block title %}Item Details{% endblock %} -{% block body %} - <h1>Item</h1> - - <table class="table"> - <tbody> - <tr> - <th>Id</th> - <td>{{ item.id }}</td> - </tr> - <tr> - <th>Title</th> - <td>{{ item.title }}</td> - </tr> - <tr> - <th>Description</th> - <td>{{ item.description }}</td> - </tr> - <tr> - <th>Url</th> - <td>{{ item.url }}</td> - </tr> - <tr> - <th>Image</th> - <td>{{ item.image }}</td> - </tr> - <tr> - <th>Price</th> - <td>{{ item.price }}</td> - </tr> - </tbody> - </table> - - <a href="{{ path('app_item_index') }}">back to list</a> - - <a href="{{ path('app_item_edit', {'id': item.id}) }}">edit</a> +{% block stylesheets %} + {{ parent() }} + <style> + body { + font-family: Arial, sans-serif; + background: linear-gradient(135deg, #00B8DE, #99CC33) fixed; + color: white; + text-align: center; + padding: 20px; + } + .container { + max-width: 800px; + margin: auto; + } + .item-container { + background: rgba(255, 255, 255, 0.2); + border: 2px solid #99CC33; + border-radius: 10px; + box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2); + backdrop-filter: blur(10px); + padding: 20px; + margin-top: 20px; + } + h1 { + margin-bottom: 20px; + } + table { + width: 100%; + border-collapse: collapse; + margin-top: 20px; + } + th, td { + padding: 12px; + border: 2px solid #99CC33; + border-radius: 4px; + } + th { + background-color: rgba(153, 204, 51, 0.5); + } + a.button { + display: inline-block; + margin: 10px 5px; + padding: 10px 20px; + text-decoration: none; + border: 2px solid #00B8DE; + background-color: white; + color: #00B8DE; + border-radius: 4px; + transition: all 0.3s ease; + } + a.button:hover { + background-color: #99CC33; + color: white; + border-color: #99CC33; + } + @media (max-width: 768px) { + .item-container { + width: 90%; + } + table, th, td { + font-size: 0.9rem; + } + } + </style> +{% endblock %} - {{ include('item/_delete_form.html.twig') }} +{% block body %} +<div class="container"> + <h1>Item Details</h1> + <div class="item-container"> + <table> + <tbody> + <tr> + <th>Id</th> + <td>{{ item.id }}</td> + </tr> + <tr> + <th>Title</th> + <td>{{ item.title }}</td> + </tr> + <tr> + <th>Description</th> + <td>{{ item.description }}</td> + </tr> + <tr> + <th>Url</th> + <td>{{ item.url }}</td> + </tr> + <tr> + <th>Image</th> + <td> + <img src="{{ asset('uploads/' ~ item.image) }}" alt="{{ item.title }}" style="max-width: 100px; border-radius: 4px;"> + </td> + </tr> + <tr> + <th>Price</th> + <td>{{ item.price }} €</td> + </tr> + </tbody> + </table> + <div> + <a href="{{ path('app_item_index') }}" class="button">Back to List</a> + <a href="{{ path('app_item_edit', {'id': item.id}) }}" class="button">Edit</a> + {{ include('item/_delete_form.html.twig') }} + </div> + </div> +</div> {% endblock %} diff --git a/templates/purchase_proof/new.html.twig b/templates/purchase_proof/new.html.twig index d56226013632ab9926e4b3452034caf099ec197d..6e02dd9da1e501b097e1fdc097c090853bcbf7da 100644 --- a/templates/purchase_proof/new.html.twig +++ b/templates/purchase_proof/new.html.twig @@ -2,12 +2,102 @@ {% block title %}Add Purchase Proof{% endblock %} +{% block stylesheets %} + {{ parent() }} + <style> + body { + font-family: Arial, sans-serif; + background: linear-gradient(135deg, #00B8DE, #99CC33) fixed; + color: white; + text-align: center; + padding: 20px; + } + .container { + max-width: 800px; + margin: auto; + } + header { + display: flex; + justify-content: space-between; + align-items: center; + padding: 10px 20px; + background: rgba(255, 255, 255, 0.2); + border-radius: 10px; + backdrop-filter: blur(10px); + } + .search-bar { + padding: 5px; + border: 2px solid #99CC33; + border-radius: 5px; + } + .item-section { + display: flex; + flex-direction: column; + align-items: center; + margin-top: 20px; + } + .article-box { + width: 100%; + max-width: 600px; + padding: 15px; + background: rgba(255, 255, 255, 0.2); + border-radius: 10px; + border: 2px solid #99CC33; + box-shadow: 0 4px 8px rgba(0,0,0,0.2); + text-align: center; + } + .image-box img { + max-width: 100%; + border-radius: 10px; + } + .upload-box { + margin-top: 20px; + width: 100%; + max-width: 600px; + padding: 15px; + background: rgba(255, 255, 255, 0.2); + border-radius: 10px; + border: 2px solid #99CC33; + box-shadow: 0 4px 8px rgba(0,0,0,0.2); + } + .form-group { + margin-bottom: 15px; + text-align: left; + } + .form-control { + width: 100%; + padding: 10px; + border: 2px solid #99CC33; + border-radius: 5px; + font-size: 1rem; + } + .submit-button { + background-color: white; + color: #00B8DE; + border: 2px solid #00B8DE; + padding: 10px 15px; + border-radius: 5px; + font-size: 1rem; + cursor: pointer; + transition: 0.3s; + } + .submit-button:hover { + background-color: #99CC33; + color: white; + border-color: #99CC33; + } + @media (max-width: 768px) { + .article-box, .upload-box { + width: 90%; + } + } + </style> +{% endblock %} + {% block body %} <header> - <div class="user-icon"></div> - <h1><a href="#">Purchase proof</a></h1> + <h1>Purchase Proof</h1> <input type="text" placeholder="Search…" class="search-bar"> - <div class="menu-icon"></div> </header> <main> @@ -37,4 +127,4 @@ </section> </div> </main> -{% endblock %} \ No newline at end of file +{% endblock %} diff --git a/templates/user/profile.html.twig b/templates/user/profile.html.twig index ce689c9401f4aac428f341c6e9117f4e782e1dd8..34c65844e4af659c9624b7a616b375b79f88aa14 100644 --- a/templates/user/profile.html.twig +++ b/templates/user/profile.html.twig @@ -2,15 +2,75 @@ {% block title %}Mon Profil{% endblock %} +{% block stylesheets %} + {{ parent() }} + <style> + body { + font-family: Arial, sans-serif; + background: linear-gradient(135deg, #00B8DE, #99CC33) fixed; + color: white; + text-align: center; + padding: 20px; + } + .container { + max-width: 600px; + margin: auto; + background: rgba(255, 255, 255, 0.2); + padding: 20px; + border-radius: 15px; + box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2); + backdrop-filter: blur(10px); + } + h1 { + font-size: 2em; + margin-bottom: 10px; + } + .form-group { + margin-bottom: 15px; + text-align: left; + } + .form-control { + width: 100%; + padding: 10px; + border: 2px solid #99CC33; + border-radius: 5px; + font-size: 1rem; + } + .btn-primary { + background-color: white; + color: #00B8DE; + border: 2px solid #00B8DE; + padding: 10px 15px; + border-radius: 5px; + font-size: 1rem; + cursor: pointer; + transition: 0.3s; + } + .btn-primary:hover { + background-color: #99CC33; + color: white; + border-color: #99CC33; + } + .alert-success { + margin-top: 15px; + background: rgba(255, 255, 255, 0.2); + padding: 10px; + border-radius: 5px; + color: white; + border: 1px solid #99CC33; + } + </style> +{% endblock %} + {% block body %} <div class="container mt-5"> <h1>Mon Profil</h1> - {{ form_start(form) }} - {{ form_row(form.firstName) }} - {{ form_row(form.lastName) }} - {{ form_row(form.email) }} - {{ form_row(form.image) }} + {{ form_start(form, {'attr': {'class': 'profile-form'}}) }} + {{ form_row(form.firstName, {'attr': {'class': 'form-control'}}) }} + {{ form_row(form.lastName, {'attr': {'class': 'form-control'}}) }} + {{ form_row(form.email, {'attr': {'class': 'form-control'}}) }} + {{ form_row(form.image, {'attr': {'class': 'form-control'}}) }} <button type="submit" class="btn btn-primary">Mettre à jour</button> {{ form_end(form) }} @@ -20,4 +80,4 @@ </div> {% endfor %} </div> -{% endblock %} \ No newline at end of file +{% endblock %} diff --git a/templates/wishlist/new.html.twig b/templates/wishlist/new.html.twig index 08e16bc8c18a7234df83ea9003ad18954a76ee67..4f84df8ea8b969131ec82da8fab4cd07a70da231 100644 --- a/templates/wishlist/new.html.twig +++ b/templates/wishlist/new.html.twig @@ -6,16 +6,30 @@ {% block stylesheets %} {{ parent() }} <style> + body { + font-family: Arial, sans-serif; + background: linear-gradient(135deg, #00B8DE, #99CC33) fixed; + color: white; + text-align: center; + padding: 20px; + } + .container { + max-width: 800px; + margin: auto; + } .wishlist-form { max-width: 600px; margin: 0 auto; padding: 20px; - background-color: #f8f9fa; - border-radius: 8px; - box-shadow: 0 0 10px rgba(0,0,0,0.1); + background: rgba(255, 255, 255, 0.2); + border-radius: 10px; + border: 2px solid #99CC33; + box-shadow: 0 4px 8px rgba(0,0,0,0.2); + backdrop-filter: blur(10px); } .form-group { margin-bottom: 1.5rem; + text-align: left; } .form-label { display: block; @@ -25,7 +39,7 @@ .form-control { width: 100%; padding: 0.5rem; - border: 1px solid #ced4da; + border: 2px solid #99CC33; border-radius: 4px; font-size: 1rem; } @@ -44,16 +58,30 @@ font-size: 1rem; } .btn-primary { - background-color: #007bff; - color: white; - border: none; + background-color: white; + color: #00B8DE; + border: 2px solid #00B8DE; + transition: 0.3s; } .btn-primary:hover { - background-color: #0069d9; + background-color: #99CC33; + color: white; + border-color: #99CC33; + } + .btn-secondary { + background-color: white; + color: #99CC33; + border: 2px solid #99CC33; + transition: 0.3s; + } + .btn-secondary:hover { + background-color: #00B8DE; + color: white; + border-color: #00B8DE; } .form-help { font-size: 0.8rem; - color: #6c757d; + color: #e0e0e0; margin-top: 0.25rem; } .error-message { @@ -61,13 +89,18 @@ font-size: 0.875rem; margin-top: 0.25rem; } + @media (max-width: 768px) { + .wishlist-form { + width: 90%; + } + } </style> {% endblock %} {% block body %} <div class="container"> <h1 class="my-4">Create New Wishlist</h1> - + {{ form_start(form, {'attr': {'class': 'wishlist-form'}}) }} <div class="form-group"> {{ form_label(form.name) }} @@ -92,7 +125,7 @@ <div class="form-group mt-4"> <button type="submit" class="btn btn-primary">Create Wishlist</button> - <a href="{{ path('wishlist_index') }}" class="btn btn-secondary ml-2">Cancel</a> + <a href="{{ path('app_wishlist_index') }}" class="btn btn-secondary ml-2">Cancel</a> </div> {{ form_end(form) }} </div> diff --git a/templates/wishlist/show.html.twig b/templates/wishlist/show.html.twig index 9a0dbf67377480d49d650c582003ee801a8f29d5..e917e95f313a06e029f0076cf4200326e759ec31 100644 --- a/templates/wishlist/show.html.twig +++ b/templates/wishlist/show.html.twig @@ -1,468 +1,212 @@ -{# templates/wishlist/show.html.twig #} {% extends 'base.html.twig' %} {% block title %}{{ wishlist.name }}{% endblock %} {% 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; - } +{{ parent() }} +<style> + body { + font-family: 'Helvetica Neue', sans-serif; + margin: 0; + background: linear-gradient(135deg, #00B8DE, #99CC33); + color: #fff; + } - /* 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); } + /* Sticky Navbar */ + .navbar { + display: flex; + align-items: center; + justify-content: space-between; + padding: 10px 20px; + position: sticky; + top: 0; + background: rgba(0,0,0,0.3); + backdrop-filter: blur(10px); + z-index: 10; + } + .navbar .logo { + font-size: 1.5rem; + font-weight: bold; + } + .navbar input { + padding: 8px; + border-radius: 20px; + border: none; + min-width: 200px; + } + .navbar .actions { + display: flex; + gap: 10px; + } - /* 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; - } - - .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; + /* Header section */ + .hero { + text-align: center; + padding: 40px 20px; + } + .hero h1 { + font-size: 2.5rem; + margin-bottom: 10px; + } + .hero .add-btn { + background: #fff; + color: #00B8DE; + padding: 10px 20px; + border: none; + border-radius: 30px; + cursor: pointer; + font-weight: bold; + transition: all 0.3s; + } + .hero .add-btn:hover { + background: #99CC33; + color: #fff; + } + + /* Grid items */ + .grid-container { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); + gap: 20px; + padding: 20px; + } + + .card { + background: rgba(255, 255, 255, 0.15); + border-radius: 20px; + overflow: hidden; + display: flex; + flex-direction: column; + box-shadow: 0 4px 12px rgba(0,0,0,0.3); + transition: transform 0.2s; + } + .card:hover { + transform: scale(1.03); + } + + .card-image { + width: 100%; + aspect-ratio: 1 / 1; + overflow: hidden; + } + .card-image img { + width: 100%; + height: 100%; + object-fit: cover; + display: block; + } + + .card-body { + padding: 15px; + flex-grow: 1; + } + + .card-body h3 { + margin-top: 0; + margin-bottom: 10px; + font-size: 1.3rem; + } + + .card-body p { + margin: 0 0 10px 0; + font-size: 0.95rem; + opacity: 0.9; + } + + .card-footer { + display: flex; + justify-content: space-between; + align-items: center; + padding: 10px 15px; + background: rgba(0,0,0,0.2); + } + .card-footer .price { + font-weight: bold; + } + .card-footer .actions { + display: flex; + gap: 8px; + } + .action-btn { + background: #fff; + color: #00B8DE; + border: none; + border-radius: 8px; + padding: 5px 10px; + cursor: pointer; + transition: all 0.3s; + } + .action-btn:hover { + background: #99CC33; + color: #fff; + } + + @media (max-width: 600px) { + .navbar { flex-direction: column; + gap: 10px; } - - .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; + .hero h1 { + font-size: 2rem; } - </style> + } +</style> {% endblock %} {% block body %} - <div class="wishlist-header"> - <div> - <h1 class="wishlist-title">{{ wishlist.name }}</h1> - <div class="item-count">{{ wishlist.items|length }} items</div> - </div> - <div class="wishlist-actions"> - <button class="add-item-btn">Add item</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 class="navbar"> + <div class="logo">MyWishlist</div> + <input type="text" placeholder="Search items..."> + <div class="actions"> + <select onchange="location = this.value;"> + <option disabled selected>Sort</option> + <option value="?sort=price_asc">Price ↑</option> + <option value="?sort=price_desc">Price ↓</option> + <option value="?sort=name_asc">Name A-Z</option> + <option value="?sort=name_desc">Name Z-A</option> + </select> + <button onclick="toggleView()" class="action-btn">View</button> </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 }}" - class="item-image"> - <div class="item-details"> - <h3 class="item-title">{{ item.name }}</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> - </div> - </div> - </li> - {% else %} - <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> +</div> + +<div class="hero"> + <h1>{{ wishlist.name }}</h1> + <p>{{ wishlist.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 %} + <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> + <p>{{ item.description|default('No description') }}</p> + </div> + <div class="card-footer"> + <span class="price">${{ item.price|number_format(2) }}</span> + <div class="actions"> + {% if item.url %} + <button class="action-btn" onclick="window.open('{{ item.url }}', '_blank')">🛒</button> {% 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> + <a href="{{ path('app_item_edit', {'id': item.id}) }}" class="action-btn">✏️</a> + <form method="post" action="{{ path('app_item_delete', {'id': item.id}) }}" onsubmit="return confirm('Are you sure?');" style="display:inline;"> + <input type="hidden" name="_token" value="{{ csrf_token('delete' ~ item.id) }}"> + <button type="submit" class="action-btn">🗑️</button> + </form> </div> - </li> - {% else %} - <div class="empty-state">No items in this wishlist yet</div> - {% endfor %} - </ul> -{% endblock %} + </div> + </div> + {% else %} + <p style="text-align:center; padding:50px;">No items yet in this wishlist.</p> + {% endfor %} +</div> -{% 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 +<script> + function toggleView() { + alert('Grid view is currently default. Add alternate views if desired!'); + } +</script> +{% endblock %}