From da017457325f4fab281773fb58983aeed113ac7b Mon Sep 17 00:00:00 2001 From: ncoronel Date: Fri, 26 Jun 2026 14:02:35 -0300 Subject: [PATCH] feat: implement store layout with header and footer components, update tenant service URL --- .../store-footer/store-footer.component.html | 54 ++++ .../store-footer/store-footer.component.scss | 44 +++ .../store-footer/store-footer.component.ts | 23 ++ .../store-header/store-header.component.html | 55 ++++ .../store-header/store-header.component.scss | 46 +++ .../store-header/store-header.component.ts | 16 + .../store-layout/store-layout.component.html | 113 +------ .../store-layout/store-layout.component.scss | 282 ------------------ .../store-layout.component.spec.ts | 4 +- .../store-layout/store-layout.component.ts | 19 +- src/app/core/services/tenant.service.ts | 2 +- src/environments/environment.ts | 2 +- 12 files changed, 253 insertions(+), 407 deletions(-) create mode 100644 src/app/core/layout/store-layout/store-footer/store-footer.component.html create mode 100644 src/app/core/layout/store-layout/store-footer/store-footer.component.scss create mode 100644 src/app/core/layout/store-layout/store-footer/store-footer.component.ts create mode 100644 src/app/core/layout/store-layout/store-header/store-header.component.html create mode 100644 src/app/core/layout/store-layout/store-header/store-header.component.scss create mode 100644 src/app/core/layout/store-layout/store-header/store-header.component.ts diff --git a/src/app/core/layout/store-layout/store-footer/store-footer.component.html b/src/app/core/layout/store-layout/store-footer/store-footer.component.html new file mode 100644 index 0000000..642eb62 --- /dev/null +++ b/src/app/core/layout/store-layout/store-footer/store-footer.component.html @@ -0,0 +1,54 @@ + diff --git a/src/app/core/layout/store-layout/store-footer/store-footer.component.scss b/src/app/core/layout/store-layout/store-footer/store-footer.component.scss new file mode 100644 index 0000000..9dbd2c8 --- /dev/null +++ b/src/app/core/layout/store-layout/store-footer/store-footer.component.scss @@ -0,0 +1,44 @@ +.store-layout__footer { + background-color: #323232; +} + +.store-layout__brand-slot { + min-width: 10rem; +} + +.store-layout__brand-mark { + display: grid; + place-items: center; + width: 2.75rem; + height: 2.75rem; + border: 1px dashed rgba(255, 255, 255, 0.35); + border-radius: 999px; + font-size: 1.05rem; + font-weight: 700; + letter-spacing: 0.04em; + color: #ffffff; +} + +.store-layout__brand-text { + font-size: 1.8rem; + font-weight: 700; + letter-spacing: -0.08em; + color: #ffffff; +} + +.store-layout__footer-heading { + margin: 0; + font-size: 0.95rem; + font-weight: 500; + color: #f8f8f8; +} + +@media (max-width: 767.98px) { + .store-layout__brand-slot { + min-width: 0; + } + + .store-layout__brand-text { + font-size: 1.45rem; + } +} diff --git a/src/app/core/layout/store-layout/store-footer/store-footer.component.ts b/src/app/core/layout/store-layout/store-footer/store-footer.component.ts new file mode 100644 index 0000000..6856258 --- /dev/null +++ b/src/app/core/layout/store-layout/store-footer/store-footer.component.ts @@ -0,0 +1,23 @@ +import { Component, Input } from '@angular/core'; + +export type StoreFooterSection = { + heading: string; + links: string[]; +}; + +export type StoreSocialLink = { + label: string; + iconClass: string; +}; + +@Component({ + selector: 'app-store-footer', + imports: [], + templateUrl: './store-footer.component.html', + styleUrl: './store-footer.component.scss' +}) +export class StoreFooterComponent { + @Input({ required: true }) currentYear = new Date().getFullYear(); + @Input({ required: true }) footerSections: StoreFooterSection[] = []; + @Input({ required: true }) socialLinks: StoreSocialLink[] = []; +} 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 new file mode 100644 index 0000000..d317329 --- /dev/null +++ b/src/app/core/layout/store-layout/store-header/store-header.component.html @@ -0,0 +1,55 @@ +
+
+
+
+ + SONDER +
+ +
+ + +
+ @for (action of headerActions; track action.label) { + + } +
+
+
+
+ +
+
+ +
+
+
diff --git a/src/app/core/layout/store-layout/store-header/store-header.component.scss b/src/app/core/layout/store-layout/store-header/store-header.component.scss new file mode 100644 index 0000000..a44ea69 --- /dev/null +++ b/src/app/core/layout/store-layout/store-header/store-header.component.scss @@ -0,0 +1,46 @@ +.store-layout__brand-slot { + min-width: 10rem; +} + +.store-layout__brand-mark { + display: grid; + place-items: center; + width: 2.75rem; + height: 2.75rem; + border: 1px dashed #bbbbbb; + border-radius: 999px; + font-size: 1.05rem; + font-weight: 700; + letter-spacing: 0.04em; + color: #373737; +} + +.store-layout__brand-text { + font-size: 1.8rem; + font-weight: 700; + letter-spacing: -0.08em; +} + +.store-layout__search { + width: min(100%, 24rem); + min-width: 18rem; +} + +.store-layout__category-trigger { + font-size: 0.8rem; + letter-spacing: 0.02em; +} + +@media (max-width: 767.98px) { + .store-layout__brand-slot { + min-width: 0; + } + + .store-layout__brand-text { + font-size: 1.45rem; + } + + .store-layout__search { + min-width: 0; + } +} diff --git a/src/app/core/layout/store-layout/store-header/store-header.component.ts b/src/app/core/layout/store-layout/store-header/store-header.component.ts new file mode 100644 index 0000000..44e18a1 --- /dev/null +++ b/src/app/core/layout/store-layout/store-header/store-header.component.ts @@ -0,0 +1,16 @@ +import { Component, Input } from '@angular/core'; + +export type StoreHeaderAction = { + label: string; + iconClass: string; +}; + +@Component({ + selector: 'app-store-header', + imports: [], + templateUrl: './store-header.component.html', + styleUrl: './store-header.component.scss' +}) +export class StoreHeaderComponent { + @Input({ required: true }) headerActions: StoreHeaderAction[] = []; +} 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 c17f63a..f7e1639 100644 --- a/src/app/core/layout/store-layout/store-layout.component.html +++ b/src/app/core/layout/store-layout/store-layout.component.html @@ -1,110 +1,13 @@ -
-
-
-
-
- - SONDER -
+
+ -
- - -
- @for (action of headerActions; track action.label) { - - } -
-
-
-
- -
-
- -
-
-
- -
+
-
- -
+
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 2325374..b5f722d 100644 --- a/src/app/core/layout/store-layout/store-layout.component.scss +++ b/src/app/core/layout/store-layout/store-layout.component.scss @@ -4,285 +4,3 @@ background-color: #f6f6f6; color: #202020; } - -.store-layout { - display: flex; - flex: 1; - flex-direction: column; -} - -.store-layout__container { - width: min(100% - 3rem, 75rem); - margin: 0 auto; -} - -.store-layout__header { - background-color: #ffffff; - border-bottom: 1px solid #e9e9e9; -} - -.store-layout__top-bar { - border-bottom: 1px solid #efefef; -} - -.store-layout__top-content { - display: flex; - align-items: center; - justify-content: space-between; - gap: 1.5rem; - min-height: 5.5rem; - padding: 1rem 0; -} - -.store-layout__brand-slot { - display: inline-flex; - align-items: center; - gap: 0.75rem; - min-width: 10rem; -} - -.store-layout__brand-mark { - display: grid; - place-items: center; - width: 2.75rem; - height: 2.75rem; - border: 1px dashed #bbbbbb; - border-radius: 999px; - font-size: 1.05rem; - font-weight: 700; - letter-spacing: 0.04em; - color: #373737; -} - -.store-layout__brand-text { - font-size: 1.8rem; - font-weight: 700; - letter-spacing: -0.08em; -} - -.store-layout__tools { - display: flex; - align-items: center; - gap: 1rem; - margin-left: auto; -} - -.store-layout__search { - display: flex; - align-items: center; - width: min(100%, 24rem); - min-width: 18rem; - border: 1px solid #313131; - border-radius: 0.35rem; - background-color: #ffffff; - overflow: hidden; -} - -.store-layout__search-input { - flex: 1; - min-width: 0; - padding: 0.7rem 0.85rem; - border: 0; - font-size: 0.95rem; - background: transparent; - - &:focus { - outline: none; - } -} - -.store-layout__search-button, -.store-layout__header-action, -.store-layout__category-trigger { - border: 0; - background: transparent; -} - -.store-layout__search-button { - display: inline-flex; - align-items: center; - justify-content: center; - width: 2.75rem; - height: 2.75rem; - color: #2c2c2c; -} - -.store-layout__actions { - display: flex; - align-items: center; - gap: 0.25rem; -} - -.store-layout__header-action { - display: inline-flex; - align-items: center; - justify-content: center; - width: 2.5rem; - height: 2.5rem; - font-size: 1rem; - color: #2c2c2c; -} - -.store-layout__category-bar { - min-height: 2.35rem; -} - -.store-layout__category-trigger { - display: inline-flex; - align-items: center; - gap: 0.4rem; - min-height: 2.35rem; - padding: 0; - font-size: 0.8rem; - font-weight: 700; - letter-spacing: 0.02em; - text-transform: uppercase; - color: #202020; -} - -.store-layout__main { - flex: 1; - display: block; -} - -.store-layout__footer { - margin-top: auto; - background-color: #323232; - color: #f3f3f3; -} - -.store-layout__footer-content { - display: grid; - grid-template-columns: minmax(16rem, 1.6fr) repeat(2, minmax(10rem, 1fr)) auto; - gap: 2rem; - padding: 2.5rem 0; -} - -.store-layout__footer-brand-group, -.store-layout__footer-section, -.store-layout__social-group { - display: grid; - align-content: start; - gap: 0.9rem; -} - -.store-layout__brand-slot--footer { - min-width: 0; -} - -.store-layout__brand-mark--footer { - border-color: rgba(255, 255, 255, 0.35); - color: #ffffff; -} - -.store-layout__brand-text--footer { - color: #ffffff; -} - -.store-layout__contact-list { - display: grid; - gap: 0.45rem; -} - -.store-layout__contact-item { - display: flex; - align-items: center; - gap: 0.6rem; - font-size: 0.95rem; - color: #ececec; -} - -.store-layout__copyright { - margin: 0; - font-size: 0.85rem; - color: #d3d3d3; -} - -.store-layout__footer-heading { - margin: 0; - font-size: 0.95rem; - font-weight: 500; - color: #f8f8f8; -} - -.store-layout__footer-links { - display: grid; - gap: 0.55rem; -} - -.store-layout__footer-link { - font-size: 0.95rem; - color: #ececec; -} - -.store-layout__social-group { - justify-items: start; -} - -.store-layout__social-links { - display: flex; - align-items: center; - gap: 0.85rem; - padding-top: 0.1rem; -} - -.store-layout__social-link { - display: inline-flex; - align-items: center; - justify-content: center; - font-size: 1.1rem; - color: #ffffff; -} - -@media (max-width: 991.98px) { - .store-layout__top-content { - flex-wrap: wrap; - } - - .store-layout__tools { - flex: 1 1 100%; - justify-content: space-between; - } - - .store-layout__search { - flex: 1 1 auto; - width: auto; - } - - .store-layout__footer-content { - grid-template-columns: repeat(2, minmax(0, 1fr)); - } -} - -@media (max-width: 767.98px) { - .store-layout__container { - width: min(100% - 1.5rem, 75rem); - } - - .store-layout__brand-slot { - min-width: 0; - } - - .store-layout__brand-text { - font-size: 1.45rem; - } - - .store-layout__tools { - flex-direction: column; - align-items: stretch; - } - - .store-layout__search { - min-width: 0; - } - - .store-layout__actions { - justify-content: flex-end; - } - - .store-layout__footer-content { - grid-template-columns: minmax(0, 1fr); - gap: 1.5rem; - padding: 2rem 0; - } -} diff --git a/src/app/core/layout/store-layout/store-layout.component.spec.ts b/src/app/core/layout/store-layout/store-layout.component.spec.ts index 842364c..8e0debb 100644 --- a/src/app/core/layout/store-layout/store-layout.component.spec.ts +++ b/src/app/core/layout/store-layout/store-layout.component.spec.ts @@ -17,10 +17,10 @@ describe('StoreLayoutComponent', () => { const compiled = fixture.nativeElement as HTMLElement; - expect(compiled.querySelector('.store-layout__header')).not.toBeNull(); + expect(compiled.querySelector('app-store-header .store-layout__header')).not.toBeNull(); expect(compiled.querySelector('.store-layout__category-trigger')).not.toBeNull(); expect(compiled.querySelector('router-outlet')).not.toBeNull(); - expect(compiled.querySelector('.store-layout__footer')).not.toBeNull(); + expect(compiled.querySelector('app-store-footer .store-layout__footer')).not.toBeNull(); }); it('renders the placeholder branding and action icons', () => { 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 b71f3d4..764d17f 100644 --- a/src/app/core/layout/store-layout/store-layout.component.ts +++ b/src/app/core/layout/store-layout/store-layout.component.ts @@ -1,24 +1,11 @@ import { Component } from '@angular/core'; import { RouterOutlet } from '@angular/router'; - -type StoreHeaderAction = { - label: string; - iconClass: string; -}; - -type StoreFooterSection = { - heading: string; - links: string[]; -}; - -type StoreSocialLink = { - label: string; - iconClass: string; -}; +import { StoreFooterComponent, StoreFooterSection, StoreSocialLink } from './store-footer/store-footer.component'; +import { StoreHeaderComponent, StoreHeaderAction } from './store-header/store-header.component'; @Component({ selector: 'app-store-layout', - imports: [RouterOutlet], + imports: [RouterOutlet, StoreHeaderComponent, StoreFooterComponent], templateUrl: './store-layout.component.html', styleUrl: './store-layout.component.scss' }) diff --git a/src/app/core/services/tenant.service.ts b/src/app/core/services/tenant.service.ts index 9c2ff3c..018b3b6 100644 --- a/src/app/core/services/tenant.service.ts +++ b/src/app/core/services/tenant.service.ts @@ -23,7 +23,7 @@ export class TenantService { const domain = this.resolveDomain(); const response = await firstValueFrom( - this.http.get(`${environment.url}tennants/bootstrap/${domain}`) + this.http.get(`${environment.url}tenants/bootstrap/${domain}`) ); if (!response?.data) { diff --git a/src/environments/environment.ts b/src/environments/environment.ts index 9bb711c..8c83d8c 100644 --- a/src/environments/environment.ts +++ b/src/environments/environment.ts @@ -1,6 +1,6 @@ export const environment = { production: false, nombre:"Desarrollo - activo", - url:"url/api/", + url:"http://localhost:8000/api/", urlDescarga:"url/" };