feat(store-layout): add dynamic background images to header and footer components
This commit is contained in:
@@ -1,4 +1,7 @@
|
||||
<footer class="store-layout__footer mt-auto text-light">
|
||||
<footer
|
||||
class="store-layout__footer mt-auto text-light"
|
||||
[style.background-image]="backgroundImageUrl ? 'url(' + backgroundImageUrl + ')' : null"
|
||||
>
|
||||
<div class="container-xl px-3 px-md-4 py-4 py-md-5 store-layout__footer-content">
|
||||
<div class="row g-4 store-layout__footer-grid">
|
||||
<div class="col-12 store-layout__brand-column">
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
.store-layout__footer {
|
||||
background-color: var(--tenant-footer-bg);
|
||||
background-repeat: no-repeat;
|
||||
background-position: center;
|
||||
background-size: cover;
|
||||
}
|
||||
|
||||
.store-layout__mobile-divider {
|
||||
|
||||
@@ -25,6 +25,7 @@ export class StoreFooterComponent {
|
||||
@Input({ required: true }) footerSections: StoreFooterSection[] = [];
|
||||
@Input({ required: true }) socialMedia: SocialMedia[] = [];
|
||||
@Input() logoUrl: string | null = null;
|
||||
@Input() backgroundImageUrl: string | null = null;
|
||||
@Input() storeName: string | null = null;
|
||||
readonly logoutClick = output<void>();
|
||||
}
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
<header class="store-layout__header">
|
||||
<header
|
||||
class="store-layout__header"
|
||||
[style.background-image]="backgroundImageUrl() ? 'url(' + backgroundImageUrl() + ')' : null"
|
||||
>
|
||||
<div class="store-layout__header-main">
|
||||
<div class="container-xl px-3 px-md-4 py-3 py-lg-4">
|
||||
<div class="row align-items-center gy-3 gy-md-0">
|
||||
@@ -74,12 +77,14 @@
|
||||
</app-button>
|
||||
}
|
||||
|
||||
<app-cart-icon
|
||||
[quantity]="cartQuantity()"
|
||||
ariaLabel="Carrito de compras"
|
||||
title="Carrito"
|
||||
(click)="cartClick.emit()"
|
||||
/>
|
||||
@if (displayCart()) {
|
||||
<app-cart-icon
|
||||
[quantity]="cartQuantity()"
|
||||
ariaLabel="Carrito de compras"
|
||||
title="Carrito"
|
||||
(click)="cartClick.emit()"
|
||||
/>
|
||||
}
|
||||
|
||||
<div class="store-layout__user-menu d-none d-md-inline-flex position-relative">
|
||||
<app-icon-button
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
.store-layout__header {
|
||||
color: #ffffff;
|
||||
background-color: var(--tenant-header-bg);
|
||||
background-repeat: no-repeat;
|
||||
background-position: center;
|
||||
background-size: cover;
|
||||
}
|
||||
|
||||
.store-layout__header-main {
|
||||
|
||||
@@ -29,6 +29,7 @@ export class StoreHeaderComponent {
|
||||
private readonly router = inject(Router);
|
||||
|
||||
readonly logoUrl = input<string | null>(null);
|
||||
readonly backgroundImageUrl = input<string | null>(null);
|
||||
readonly cartQuantity = input<number>(0);
|
||||
readonly user = input<AuthUser | null>(null);
|
||||
readonly isAuthenticated = input<boolean>(false);
|
||||
@@ -37,6 +38,7 @@ export class StoreHeaderComponent {
|
||||
readonly categories = input<readonly Category[]>([]);
|
||||
readonly displayCategories = input(true);
|
||||
readonly displaySeachBar = input(true);
|
||||
readonly displayCart = input(true);
|
||||
readonly cartClick = output<void>();
|
||||
readonly ticketsClick = output<void>();
|
||||
readonly loginClick = output<void>();
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
ngSkipHydration
|
||||
[cartQuantity]="cartQuantity()"
|
||||
[logoUrl]="tenant()?.header_logo ?? null"
|
||||
[backgroundImageUrl]="tenant()?.header_bg_image ?? null"
|
||||
[user]="user()"
|
||||
[isAuthenticated]="isAuthenticated()"
|
||||
[accountNavigationMenus]="accountNavigationMenus()"
|
||||
@@ -10,6 +11,7 @@
|
||||
[categories]="tenant()?.categories ?? []"
|
||||
[displayCategories]="tenant()?.display_categories ?? true"
|
||||
[displaySeachBar]="tenant()?.display_seach_bar ?? true"
|
||||
[displayCart]="displayCart()"
|
||||
(cartClick)="isCartOpen.set(!isCartOpen())"
|
||||
(ticketsClick)="onTicketsClick()"
|
||||
(loginClick)="onLoginClick()"
|
||||
@@ -18,7 +20,7 @@
|
||||
(categorySelect)="onCategorySelect($event)"
|
||||
/>
|
||||
|
||||
@if (isCartOpen()) {
|
||||
@if (displayCart() && isCartOpen()) {
|
||||
<div class="store-layout__cart-overlay" (click)="isCartOpen.set(false)"></div>
|
||||
<div class="store-layout__cart-dropdown card border-0 shadow-lg">
|
||||
<app-cart
|
||||
@@ -58,6 +60,7 @@
|
||||
[footerSections]="footerSections()"
|
||||
[socialMedia]="tenant()?.social_media ?? []"
|
||||
[logoUrl]="tenant()?.footer_logo ?? null"
|
||||
[backgroundImageUrl]="tenant()?.footer_bg_image ?? null"
|
||||
[storeName]="tenant()?.nombre ?? null"
|
||||
(logoutClick)="onLogoutClick()"
|
||||
/>
|
||||
|
||||
@@ -30,6 +30,8 @@ const tenant: Tenant = {
|
||||
footer_bg_color: '#313131',
|
||||
header_logo: 'https://example.com/header.png',
|
||||
footer_logo: 'https://example.com/footer.png',
|
||||
header_bg_image: 'https://example.com/header-background.png',
|
||||
footer_bg_image: 'https://example.com/footer-background.png',
|
||||
categories: [],
|
||||
menues: [
|
||||
{
|
||||
@@ -210,6 +212,7 @@ describe('StoreLayoutComponent', () => {
|
||||
categories: [{ id: 1, nombre: 'Remeras', subcategories: [] }],
|
||||
display_categories: false,
|
||||
display_seach_bar: false,
|
||||
display_cart: false,
|
||||
});
|
||||
|
||||
const fixture = TestBed.createComponent(StoreLayoutComponent);
|
||||
@@ -219,6 +222,13 @@ describe('StoreLayoutComponent', () => {
|
||||
|
||||
expect(compiled.querySelector('#store-search-input')).toBeNull();
|
||||
expect(compiled.querySelector('.store-layout__category-trigger')).toBeNull();
|
||||
expect(compiled.querySelector('app-cart-icon')).toBeNull();
|
||||
|
||||
(fixture.componentInstance as any).isCartOpen.set(true);
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(compiled.querySelector('app-cart')).toBeNull();
|
||||
expect(compiled.querySelector('.store-layout__cart-overlay')).toBeNull();
|
||||
|
||||
const header = fixture.debugElement.query(By.directive(StoreHeaderComponent));
|
||||
(header.componentInstance as any).toggleMobileMenu();
|
||||
@@ -245,6 +255,12 @@ describe('StoreLayoutComponent', () => {
|
||||
`app-store-footer img.store-layout__brand-logo[src="${tenant.footer_logo}"]`,
|
||||
),
|
||||
).not.toBeNull();
|
||||
expect(
|
||||
(compiled.querySelector('.store-layout__header') as HTMLElement).style.backgroundImage,
|
||||
).toContain(tenant.header_bg_image!);
|
||||
expect(
|
||||
(compiled.querySelector('.store-layout__footer') as HTMLElement).style.backgroundImage,
|
||||
).toContain(tenant.footer_bg_image!);
|
||||
expect(compiled.querySelector('.fa-cart-shopping')).not.toBeNull();
|
||||
expect(compiled.querySelector('.fa-circle-user')).not.toBeNull();
|
||||
expect(compiled.querySelector('.fa-instagram')).not.toBeNull();
|
||||
|
||||
@@ -35,6 +35,7 @@ export class StoreLayoutComponent implements OnInit {
|
||||
|
||||
protected readonly isCartOpen = signal(false);
|
||||
protected readonly isCreatingPurchase = signal(false);
|
||||
protected readonly displayCart = computed(() => this.tenant()?.display_cart ?? true);
|
||||
|
||||
protected readonly cartSubtotal = computed(() => {
|
||||
const cart = this.cartService.cart();
|
||||
|
||||
@@ -106,6 +106,8 @@ export interface Tenant {
|
||||
footer_bg_color: string;
|
||||
header_logo: string;
|
||||
footer_logo: string;
|
||||
header_bg_image?: string | null;
|
||||
footer_bg_image?: string | null;
|
||||
website_type_code?: string | null;
|
||||
website_type?: WebsiteType | null;
|
||||
event_date_text?: string | null;
|
||||
@@ -118,6 +120,7 @@ export interface Tenant {
|
||||
search_items_per_page?: number;
|
||||
display_categories?: boolean;
|
||||
display_seach_bar?: boolean;
|
||||
display_cart?: boolean;
|
||||
social_media?: SocialMedia[];
|
||||
menues?: Menu[];
|
||||
categories: Category[];
|
||||
|
||||
@@ -53,6 +53,14 @@
|
||||
}
|
||||
}
|
||||
|
||||
::ng-deep .hero-title h1 {
|
||||
margin: 0;
|
||||
color: inherit;
|
||||
font-size: inherit;
|
||||
font-weight: 700;
|
||||
line-height: inherit;
|
||||
}
|
||||
|
||||
::ng-deep .hero-description,
|
||||
.hero-description {
|
||||
color: #666666;
|
||||
|
||||
Reference in New Issue
Block a user