diff --git a/assets/css/wishlistAlert.css b/assets/css/wishlistAlert.css new file mode 100644 index 0000000..61f4d0e --- /dev/null +++ b/assets/css/wishlistAlert.css @@ -0,0 +1,37 @@ +.WishlistAlert { + border-radius: 8px; + padding: 48px 40px; + width: 100%; + max-width: 100%; + position: fixed; + bottom: 0; + left: 0; + z-index: 99; + background-color: var(--theme-lightest); + + @screen sm { + top: 30px; + right: 30px; + left: auto; + height: fit-content; + flex-direction: row; + flex-wrap: wrap; + align-items: center; + box-shadow: 0px 0px 8px 0px #5454544d; + max-width: 518px; + padding: 24px 20px; + width: calc(100vw - 60px); + } + + &-iconClose { + position: absolute; + top: 10px; + right: 10px; + width: 35px; + cursor: pointer; + } + + &-title { + @apply mb-0 paragraph-2; + } +} diff --git a/assets/css/wishlistModal.css b/assets/css/wishlistModal.css new file mode 100644 index 0000000..2110758 --- /dev/null +++ b/assets/css/wishlistModal.css @@ -0,0 +1,131 @@ +.WishListButton { + display: inline-flex; +} + +.WishListModal { + z-index: 30; + + &-body { + display: flex; + flex-direction: column; + gap: 1.5rem; + max-height: 70vh; + overflow-y: auto; + } + + &-section { + display: flex; + flex-direction: column; + gap: 0.75rem; + } + + &-title { + @apply block mb-6 text-center text-black; + } + + &-titleSection { + @apply paragraph-2; + font-weight: 700; + color: var(--black); + } + + &-form { + display: flex; + flex-direction: column; + gap: 0.5rem; + } + + &-formRow { + display: flex; + gap: rem-convert(8px); + align-items: stretch; + + .FieldInput-blockInput { + flex: 1 1 auto; + } + } + + &-grid { + display: grid; + gap: 0.5rem; + + @screen md { + grid-template-columns: repeat(2, minmax(0, 1fr)); + } + } + + &-card { + display: flex; + align-items: center; + gap: 0.5rem; + padding: 1.25rem; + border-radius: 12px; + background: var(--theme-lightest); + text-align: left; + cursor: pointer; + transition: border-color 0.15s ease; + + &:hover:not(.WishListModal--selected) { + background: var(--theme-lighter); + } + } + &-cardIcon { + width: rem-convert(18px); + height: rem-convert(18px); + flex: 0 0 auto; + } + + &-list { + display: flex; + flex-direction: column; + gap: rem-convert(8px); + margin: 0; + padding: 0; + list-style: none; + } + + &-item { + display: flex; + align-items: center; + justify-content: space-between; + gap: rem-convert(12px); + padding: rem-convert(10px) rem-convert(12px); + border: 1px solid var(--grey); + border-radius: rem-convert(12px); + } + + &-remove { + display: inline-flex; + align-items: center; + gap: rem-convert(6px); + flex: 0 0 auto; + margin-left: auto; + padding: 0.5rem 0.75rem; + + svg { + width: 1rem; + height: 1rem; + } + } +} + +@media (min-width: 1024px) { + .WishListModal.MobileDrawer { + top: 50%; + bottom: auto; + left: 50%; + right: auto; + width: min(560px, 92vw); + max-height: 85vh; + overflow-y: auto; + border-radius: rem-convert(20px); + } + + .WishListModal.MobileDrawer.is-open { + transform: translate(-50%, -50%); + } + + .WishListModal.MobileDrawer::before { + content: none; + } +} diff --git a/assets/icons/heart-fill.svg b/assets/icons/heart-fill.svg index 98e42b8..9884e7c 100644 --- a/assets/icons/heart-fill.svg +++ b/assets/icons/heart-fill.svg @@ -1,5 +1,4 @@ - - - + + + \ No newline at end of file diff --git a/assets/icons/heart.svg b/assets/icons/heart.svg index 4793afd..c4d8fbf 100644 --- a/assets/icons/heart.svg +++ b/assets/icons/heart.svg @@ -1,3 +1,4 @@ - - - + + + \ No newline at end of file diff --git a/base.html.twig b/base.html.twig index ab81b82..bfdd575 100644 --- a/base.html.twig +++ b/base.html.twig @@ -2,34 +2,33 @@ - - - - - {% block title %} - {{ SEOnePageTitle() }} - {% endblock %} - - - {% block robots %}{% endblock %} - - - - - - {% block breadcrumb_json %} - {{ SEOneBreadcrumbJsonLd(breadcrumb)|raw }} - {% endblock %} - - - - - - {% block hreflang %} - {{SEOneHreflang()|raw}} - {% endblock %} - - + + + + + {% block title %} + {{ SEOnePageTitle() }} + {% endblock %} + + {% block robots %}{% endblock %} + + + + + + {% block breadcrumb_json %} + {{ SEOneBreadcrumbJsonLd(breadcrumb)|raw }} + {% endblock %} + + + + + + {% block hreflang %} + {{SEOneHreflang()|raw}} + {% endblock %} + + {% block stylesheets %} {{ encore_entry_link_tags('app') }} @@ -43,19 +42,18 @@ {% endblock %} - - {% block header %} - {{ include('@components/Layout/Header/Header.html.twig') }} - {% endblock %} + + {% block header %} + {{ include('@components/Layout/Header/Header.html.twig') }} + {% endblock %} -
- {% block body %}{% endblock %} -
+
{% block body %}{% endblock %} +
- {% block footer %} - {{ include('@components/Layout/Footer/Footer.html.twig') }} - {% endblock %} + {% block footer %} + {{ include('@components/Layout/Footer/Footer.html.twig') }} + {% endblock %} - {% block modals %}{% endblock %} - - + {% block modals %}{% endblock %} + + diff --git a/components/Layout/PseSelector/PseSelector.html.twig b/components/Layout/PseSelector/PseSelector.html.twig index 6032866..66e6d5e 100644 --- a/components/Layout/PseSelector/PseSelector.html.twig +++ b/components/Layout/PseSelector/PseSelector.html.twig @@ -4,7 +4,7 @@ 'data-action': 'live#action:prevent', 'data-live-action-param': 'save', }}, - ) }} + ) }} {% set outOfStock = this.isStockManaged() and currentPse.quantity == 0 %}
@@ -45,10 +45,10 @@ {% set available = true %}
  • {% endfor %} diff --git a/index.html.twig b/index.html.twig index c58e087..4fa62a6 100644 --- a/index.html.twig +++ b/index.html.twig @@ -4,8 +4,6 @@ {% block body %} -{# {{ component('HeaderHighlights') }}#} - {{ component('Flexy:CrossSelling', {categoryId: 1, title: 'Last seen products'|trans}) }}
    diff --git a/src/UiComponents/ProductCard/ProductCard.css b/src/UiComponents/ProductCard/ProductCard.css index e2370e4..6d506e7 100644 --- a/src/UiComponents/ProductCard/ProductCard.css +++ b/src/UiComponents/ProductCard/ProductCard.css @@ -6,54 +6,18 @@ background: var(--white); @apply rounded-lg; z-index: 0; - .ButtonWishList { - transition: 0.2s; - display: none; - - @screen sm { - display: block; - } - svg { - max-width: 35px; - } - &:focus-visible { - outline: none; - } - - .in-wishlist-icon { - display: none; - - &:hover { - svg { - color: var(--theme-medium); - } - } - } - - .not-in-wishlist-icon { - display: inline-block; - - &:hover { - svg { - color: var(--theme); - } - } - } - - &.in-wishlist { - .in-wishlist-icon { - display: inline-block; - } + &:focus, + &:focus-visible { + outline: none; - .not-in-wishlist-icon { - display: none; - } + .ProductCard-img { + outline: 2px solid theme('colors.gray.400'); } } &-header { - margin-bottom: 0.5rem; position: absolute; + margin-bottom: 0.5rem; display: flex; justify-content: space-between; width: 100%; @@ -229,4 +193,20 @@ outline: 2px solid theme('colors.gray.400'); } } + + &:has(.WishListModal) { + z-index: initial; + } +} + +.WishListButton { + width: 2.5rem; + height: 2.5rem; + padding: 0.5rem; + + > svg { + width: 100%; + height: 100%; + object-fit: contain; + } } diff --git a/src/UiComponents/ProductCard/ProductCard.html.twig b/src/UiComponents/ProductCard/ProductCard.html.twig index 3aa48ef..e076805 100644 --- a/src/UiComponents/ProductCard/ProductCard.html.twig +++ b/src/UiComponents/ProductCard/ProductCard.html.twig @@ -1,8 +1,6 @@ {% props imgWidth = 300, imgHeight = 300, - wishButton = false, - inWishlist = false, reviewCount = 0 %} @@ -25,17 +23,6 @@ {{ include('@components/Molecules/Tag/Tag.html.twig', {customText: this.promoRate|format_number(style: 'percent'), variant: 'error'}) }} {% endif %}
    - - {% if wishButton %} - - {% endif %}
    diff --git a/src/UiComponents/ProductCard/ProductCard.php b/src/UiComponents/ProductCard/ProductCard.php index 5e274c0..110f1b4 100644 --- a/src/UiComponents/ProductCard/ProductCard.php +++ b/src/UiComponents/ProductCard/ProductCard.php @@ -20,7 +20,6 @@ use Symfony\UX\TwigComponent\Attribute\PreMount; use Thelia\Api\Service\DataAccess\DataAccessService; use Thelia\Domain\Taxation\TaxEngine\TaxEngine; -use Thelia\Model\ProductPriceQuery; use Thelia\Model\ProductQuery; #[AsTwigComponent(name: 'Flexy:ProductCard', template: '@UiComponents/ProductCard/ProductCard.html.twig')] @@ -28,6 +27,7 @@ class ProductCard { public ?int $productId = null; private ?ProductDTO $product = null; + private ?int $defaultPseId = null; private ?int $imageId = null; private ?float $price = null; private ?float $taxedPrice = null; @@ -39,8 +39,7 @@ class ProductCard public function __construct( private readonly DataAccessService $dataAccessService, private TaxEngine $taxEngine, - ) - { + ) { } #[PreMount] @@ -55,7 +54,7 @@ public function mount(ProductDTO|array|null $product = null): void { if ($product instanceof ProductDTO) { $this->product = $product; - } else if (is_array($product)) { + } elseif (is_array($product)) { $this->product = ProductDTO::fromArray($product); } @@ -74,6 +73,8 @@ public function mount(ProductDTO|array|null $product = null): void $defaultPse = $this->findDefaultPse($this->product->productSaleElements); + $this->defaultPseId = $defaultPse?->id; + $this->setImage($defaultPse); if ($defaultPse instanceof ProductSaleElementDTO) { @@ -155,6 +156,17 @@ public function getProduct(): ProductDTO return $this->product; } + public function getDefaultPseId(): ?int + { + if (null !== $this->defaultPseId) { + return $this->defaultPseId; + } + + $firstPse = $this->product?->productSaleElements[0] ?? null; + + return $firstPse?->id; + } + public function getImageId(): ?int { return $this->imageId; @@ -183,7 +195,7 @@ private function setImage(?ProductSaleElementDTO $defaultPse): void $pseImages = $this->dataAccessService->resources( '/api/front/product_sale_elements_product_image', [ - 'productImageId' => array_map(static fn($img) => $img['id'], $productImages), + 'productImageId' => array_map(static fn ($img) => $img['id'], $productImages), 'productSaleElements.product.id' => $this->product->id, 'visible' => true, ] diff --git a/src/UiComponents/ProductCard/ProductCard.stories.js b/src/UiComponents/ProductCard/ProductCard.stories.js index 388d2d5..fa63713 100644 --- a/src/UiComponents/ProductCard/ProductCard.stories.js +++ b/src/UiComponents/ProductCard/ProductCard.stories.js @@ -6,22 +6,20 @@ export default { }; export const base = { - render : (args) => + render: (args) => `
    ${ProductCard(args)}
    `, - args : { - - img : { url: '/images/placeholder2.webp', alt: '' }, - url : '#', - title : 'Nom du produit', + args: { + img: { url: '/images/placeholder2.webp', alt: '' }, + url: '#', + title: 'Nom du produit', secondaryTitle: 'Titre secondaire', - quantity : 1, - attributesAv : { Taille: 'S' }, - price : '1000,00€', - promoPrice : '900,00€', - rate : 4, - reviewCount : 12, - wishButton : false, - colors : [ + quantity: 1, + attributesAv: { Taille: 'S' }, + price: '1000,00€', + promoPrice: '900,00€', + rate: 4, + reviewCount: 12, + colors: [ '#667761', '#84DCC6', '#C17767', @@ -31,7 +29,7 @@ export const base = { '#2E3438' ] }, - argTypes : {}, + argTypes: {}, parameters: { backgrounds: { default: 'theme-lighter' } }