From f2cc42e0cc76e1cfecbaf132f8eed41a6d384063 Mon Sep 17 00:00:00 2001 From: ncoronel Date: Thu, 2 Jul 2026 09:59:24 -0300 Subject: [PATCH] feat: implement cart overlay and dropdown with styling for improved user experience --- .../store-header/store-header.component.html | 1 + .../store-header/store-header.component.ts | 3 +- .../store-layout/store-layout.component.html | 24 ++++++++- .../store-layout/store-layout.component.scss | 50 +++++++++++++++++ .../store-layout/store-layout.component.ts | 53 ++++++++++++++++++- .../cart-item/cart-item.component.scss | 12 ++++- .../components/cart/cart.component.html | 4 ++ .../components/cart/cart.component.scss | 6 ++- 8 files changed, 146 insertions(+), 7 deletions(-) diff --git a/src/app/core/layout/store-layout/store-header/store-header.component.html b/src/app/core/layout/store-layout/store-header/store-header.component.html index 7c365dc..77a590c 100644 --- a/src/app/core/layout/store-layout/store-header/store-header.component.html +++ b/src/app/core/layout/store-layout/store-header/store-header.component.html @@ -38,6 +38,7 @@ [quantity]="cartQuantity()" ariaLabel="Carrito de compras" title="Carrito" + (click)="cartClick.emit()" /> (null); readonly cartQuantity = input(0); + readonly cartClick = output(); } diff --git a/src/app/core/layout/store-layout/store-layout.component.html b/src/app/core/layout/store-layout/store-layout.component.html index 0db4524..62fc3a3 100644 --- a/src/app/core/layout/store-layout/store-layout.component.html +++ b/src/app/core/layout/store-layout/store-layout.component.html @@ -1,5 +1,27 @@
- + + + @if (isCartOpen()) { +
+
+ + Seguir comprando + Comprar + +
+ }
diff --git a/src/app/core/layout/store-layout/store-layout.component.scss b/src/app/core/layout/store-layout/store-layout.component.scss index b939f0e..958c90d 100644 --- a/src/app/core/layout/store-layout/store-layout.component.scss +++ b/src/app/core/layout/store-layout/store-layout.component.scss @@ -10,3 +10,53 @@ #f6f6f6; color: #202020; } + +.store-layout { + position: relative; +} + +.store-layout__cart-overlay { + position: fixed; + top: 0; + left: 0; + width: 100vw; + height: 100vh; + z-index: 1040; + background: transparent; +} + +.store-layout__cart-dropdown { + position: absolute; + top: 76px; + right: calc((100% - 1320px) / 2 + 1.5rem); + + height: 520px; + max-height: calc(100vh - 120px); + z-index: 1050; + background-color: #ffffff; + border-radius: 8px; + border: 1px solid rgba(0, 0, 0, 0.08); + box-shadow: 0 10px 25px rgba(0, 0, 0, 0.08); + overflow: hidden; + animation: store-layout-slide-down 0.2s ease-out; + + @media (max-width: 1400px) { + right: 1.5rem; + } + @media (max-width: 576px) { + right: 1rem; + left: 1rem; + width: auto; + } +} + +@keyframes store-layout-slide-down { + from { + opacity: 0; + transform: translateY(-8px); + } + to { + opacity: 1; + transform: translateY(0); + } +} diff --git a/src/app/core/layout/store-layout/store-layout.component.ts b/src/app/core/layout/store-layout/store-layout.component.ts index bce675b..0e4b339 100644 --- a/src/app/core/layout/store-layout/store-layout.component.ts +++ b/src/app/core/layout/store-layout/store-layout.component.ts @@ -1,13 +1,16 @@ -import { Component, computed, inject, OnInit } from '@angular/core'; +import { Component, computed, inject, OnInit, signal } from '@angular/core'; import { RouterOutlet } from '@angular/router'; import { TenantService } from '../../services/tenant.service'; import { CartService } from '../../services/cart/cart.service'; import { StoreFooterComponent, StoreFooterSection, StoreSocialLink } from './store-footer/store-footer.component'; import { StoreHeaderComponent } from './store-header/store-header.component'; +import { CartComponent, CartItemMock } from '../../../shared/components/cart/cart.component'; +import { ButtonComponent } from '../../../shared/components/button/button.component'; +import { CartItem } from '../../services/cart/cart.interface'; @Component({ selector: 'app-store-layout', - imports: [RouterOutlet, StoreHeaderComponent, StoreFooterComponent], + imports: [RouterOutlet, StoreHeaderComponent, StoreFooterComponent, CartComponent, ButtonComponent], templateUrl: './store-layout.component.html', styleUrl: './store-layout.component.scss' }) @@ -15,6 +18,52 @@ export class StoreLayoutComponent implements OnInit { private readonly tenantService = inject(TenantService); private readonly cartService = inject(CartService); + protected readonly isCartOpen = signal(false); + + protected readonly cartSubtotal = computed(() => { + const cart = this.cartService.cart(); + return cart ? parseFloat(cart.subtotal) : 0; + }); + + protected readonly cartDiscount = computed(() => 0); + + protected readonly cartTotal = computed(() => this.cartSubtotal() - this.cartDiscount()); + + protected readonly mappedCartItems = computed(() => { + const cart = this.cartService.cart(); + if (!cart || !cart.items) return []; + return cart.items.map((item) => this.mapCartItemToMock(item)); + }); + + private mapCartItemToMock(item: CartItem): CartItemMock { + const fullName = item.product?.nombre ?? ''; + let product = fullName; + let attributes: { label: string; value: string }[] = []; + + const match = fullName.match(/^(.*?)\s*\((.*?)\)$/); + if (match) { + product = match[1]; + const attributesString = match[2]; + attributes = attributesString.split(',').map((attr) => { + const parts = attr.split(':'); + if (parts.length === 2) { + return { label: parts[0].trim(), value: parts[1].trim() }; + } + return { label: '', value: attr.trim() }; + }); + } + + return { + imageUrl: item.product?.imagen ?? null, + product, + originalPrice: null, + discountedPrice: parseFloat(item.precio_unitario), + discountPercentage: null, + attributes, + quantity: item.cantidad + }; + } + protected readonly currentYear = new Date().getFullYear(); protected readonly tenant = this.tenantService.tenant; diff --git a/src/app/shared/components/cart-item/cart-item.component.scss b/src/app/shared/components/cart-item/cart-item.component.scss index 43ad442..6e9bdd9 100644 --- a/src/app/shared/components/cart-item/cart-item.component.scss +++ b/src/app/shared/components/cart-item/cart-item.component.scss @@ -22,8 +22,16 @@ .cart-item-media { width: var(--item-media-width); height: var(--item-media-height); - border-radius: 6px 0 0 6px; - background-color: #d9d9d9; + border-radius: 6px; + background-color: #ffffff; + border: 1px solid #eaeaea; + overflow: hidden; + + img { + width: 100%; + height: 100%; + object-fit: contain; + } } .cart-item-discount-badge { diff --git a/src/app/shared/components/cart/cart.component.html b/src/app/shared/components/cart/cart.component.html index 41a0220..1bfa5cc 100644 --- a/src/app/shared/components/cart/cart.component.html +++ b/src/app/shared/components/cart/cart.component.html @@ -38,6 +38,10 @@ TOTAL: {{ formattedTotal() }}
+ +
+ +
diff --git a/src/app/shared/components/cart/cart.component.scss b/src/app/shared/components/cart/cart.component.scss index e693721..2bb4027 100644 --- a/src/app/shared/components/cart/cart.component.scss +++ b/src/app/shared/components/cart/cart.component.scss @@ -1,7 +1,6 @@ :host { display: block; width: 100%; - max-width: 370px; height: 100%; } @@ -59,3 +58,8 @@ font-size: 15px; font-weight: 700; } + +.cart-actions { + display: flex; + gap: 12px; +}