diff --git a/src/app/features/store/components/menu-page-layout/menu-page-layout.component.html b/src/app/features/store/components/menu-page-layout/menu-page-layout.component.html new file mode 100644 index 0000000..7ba9834 --- /dev/null +++ b/src/app/features/store/components/menu-page-layout/menu-page-layout.component.html @@ -0,0 +1,22 @@ + diff --git a/src/app/features/store/components/menu-page-layout/menu-page-layout.component.scss b/src/app/features/store/components/menu-page-layout/menu-page-layout.component.scss new file mode 100644 index 0000000..551be91 --- /dev/null +++ b/src/app/features/store/components/menu-page-layout/menu-page-layout.component.scss @@ -0,0 +1,84 @@ +:host { + display: block; + width: 100%; +} + +.menu-page-layout { + display: grid; + grid-template-columns: minmax(190px, 250px) minmax(0, 1fr); + gap: clamp(3rem, 8vw, 6.25rem); + width: 100%; + + &__sidebar, + &__content { + min-width: 0; + } + + &__sidebar { + display: flex; + flex-direction: column; + } + + &__title { + margin: 0 0 0.75rem; + color: #1a1a1a; + font-size: 1rem; + font-weight: 700; + line-height: 1.25; + text-transform: uppercase; + } + + &__divider { + height: 1px; + margin-bottom: 1.25rem; + background: #dedede; + } + + &__nav { + display: flex; + flex-direction: column; + gap: 1rem; + } + + &__link { + color: #888888; + font-size: 0.875rem; + font-weight: 500; + line-height: 1.35; + text-decoration: none; + transition: color 150ms ease; + + &:hover, + &:focus-visible { + color: #5a5a5a; + } + + &:focus-visible { + border-radius: 2px; + outline: 2px solid var(--color-primary); + outline-offset: 3px; + } + + &--active { + color: var(--color-primary); + font-weight: 600; + } + } +} + +@media (max-width: 767.98px) { + .menu-page-layout { + grid-template-columns: 1fr; + gap: 2rem; + + &__title { + text-align: center; + } + + &__nav { + flex-flow: row wrap; + justify-content: center; + gap: 0.75rem 1.25rem; + } + } +} diff --git a/src/app/features/store/components/menu-page-layout/menu-page-layout.component.spec.ts b/src/app/features/store/components/menu-page-layout/menu-page-layout.component.spec.ts new file mode 100644 index 0000000..051c8bb --- /dev/null +++ b/src/app/features/store/components/menu-page-layout/menu-page-layout.component.spec.ts @@ -0,0 +1,136 @@ +import { signal } from '@angular/core'; +import { TestBed } from '@angular/core/testing'; +import { ActivatedRoute, provideRouter } from '@angular/router'; +import { describe, expect, it } from 'vitest'; + +import { Tenant } from '../../../../core/services/tenant.interface'; +import { TenantService } from '../../../../core/services/tenant.service'; +import { MenuPageLayoutComponent } from './menu-page-layout.component'; + +function createTenant(): Tenant { + return { + id: 1, + codigo: 'test', + nombre: 'Test', + dominio: 'localhost', + primary_color: '#000000', + secondary_color: '#000000', + danger_color: '#000000', + success_color: '#000000', + header_bg_color: '#ffffff', + footer_bg_color: '#ffffff', + header_logo: '', + footer_logo: '', + categories: [], + menues: [ + { + id: 1, + code: 'account', + label: 'Mi cuenta', + parent_menu_code: null, + content_type: 'dynamic', + route: '/mi-cuenta', + submenues: [ + { + id: 2, + code: 'account.profile', + label: 'Datos personales', + parent_menu_code: 'account', + content_type: 'dynamic', + route: '/mi-cuenta/datos-personales', + submenues: [], + }, + { + id: 3, + code: 'account.purchases', + label: 'Mis compras', + parent_menu_code: 'account', + content_type: 'dynamic', + route: '/mi-cuenta/compras', + submenues: [], + }, + ], + }, + { + id: 4, + code: 'help', + label: 'Centro de ayuda', + parent_menu_code: null, + content_type: 'dynamic', + route: '/ayuda', + submenues: [ + { + id: 5, + code: 'help.faq', + label: 'Consultas habituales', + parent_menu_code: 'help', + content_type: 'dynamic', + route: '/ayuda/preguntas-frecuentes', + submenues: [], + }, + ], + }, + ], + }; +} + +async function renderLayout(menuCode: string, navigationLabel: string): Promise { + const tenant = signal(createTenant()); + + await TestBed.configureTestingModule({ + imports: [MenuPageLayoutComponent], + providers: [ + provideRouter([]), + { + provide: ActivatedRoute, + useValue: { + snapshot: { + data: { menuCode, navigationLabel }, + }, + }, + }, + { + provide: TenantService, + useValue: { tenant: tenant.asReadonly() }, + }, + ], + }).compileComponents(); + + const fixture = TestBed.createComponent(MenuPageLayoutComponent); + fixture.detectChanges(); + + return fixture.nativeElement as HTMLElement; +} + +describe('MenuPageLayoutComponent', () => { + it('renders the configured account menu and its navigation label', async () => { + const element = await renderLayout('account', 'Secciones de mi cuenta'); + const links = Array.from( + element.querySelectorAll('.menu-page-layout__link'), + ); + + expect(element.querySelector('.menu-page-layout__title')?.textContent?.trim()).toBe( + 'Mi cuenta', + ); + expect(element.querySelector('nav')?.getAttribute('aria-label')).toBe('Secciones de mi cuenta'); + expect(links.map((link) => link.textContent?.trim())).toEqual([ + 'Datos personales', + 'Mis compras', + ]); + expect(links.map((link) => link.getAttribute('href'))).toEqual([ + '/mi-cuenta/datos-personales', + '/mi-cuenta/compras', + ]); + }); + + it('uses the same component for the help menu', async () => { + const element = await renderLayout('help', 'Secciones de ayuda'); + + expect(element.querySelector('.menu-page-layout__title')?.textContent?.trim()).toBe( + 'Centro de ayuda', + ); + expect(element.querySelector('.menu-page-layout__link')?.textContent?.trim()).toBe( + 'Consultas habituales', + ); + }); +}); diff --git a/src/app/features/store/components/menu-page-layout/menu-page-layout.component.ts b/src/app/features/store/components/menu-page-layout/menu-page-layout.component.ts new file mode 100644 index 0000000..0640f32 --- /dev/null +++ b/src/app/features/store/components/menu-page-layout/menu-page-layout.component.ts @@ -0,0 +1,26 @@ +import { Component, computed, inject } from '@angular/core'; +import { ActivatedRoute, RouterLink, RouterLinkActive, RouterOutlet } from '@angular/router'; + +import { findMenu } from '../../../../core/services/menu.utils'; +import { TenantService } from '../../../../core/services/tenant.service'; + +@Component({ + selector: 'app-menu-page-layout', + imports: [RouterLink, RouterLinkActive, RouterOutlet], + templateUrl: './menu-page-layout.component.html', + styleUrl: './menu-page-layout.component.scss', +}) +export class MenuPageLayoutComponent { + private readonly route = inject(ActivatedRoute); + private readonly tenantService = inject(TenantService); + + protected readonly navigationLabel = + (this.route.snapshot.data['navigationLabel'] as string | undefined) ?? 'Secciones'; + + private readonly menuCode = this.route.snapshot.data['menuCode'] as string | undefined; + + protected readonly menu = computed(() => + findMenu(this.tenantService.tenant()?.menues ?? [], this.menuCode ?? ''), + ); + protected readonly submenues = computed(() => this.menu()?.submenues ?? []); +} diff --git a/src/app/features/store/pages/account-page/components/account-sidebar/account-sidebar.html b/src/app/features/store/pages/account-page/components/account-sidebar/account-sidebar.html deleted file mode 100644 index a2e6e39..0000000 --- a/src/app/features/store/pages/account-page/components/account-sidebar/account-sidebar.html +++ /dev/null @@ -1,11 +0,0 @@ - diff --git a/src/app/features/store/pages/account-page/components/account-sidebar/account-sidebar.scss b/src/app/features/store/pages/account-page/components/account-sidebar/account-sidebar.scss deleted file mode 100644 index 7fa466d..0000000 --- a/src/app/features/store/pages/account-page/components/account-sidebar/account-sidebar.scss +++ /dev/null @@ -1,35 +0,0 @@ -.sidebar-container { - display: flex; - flex-direction: column; -} -.sidebar-title { - font-size: 16px; - font-weight: bold; - color: #1a1a1a; - margin-bottom: 12px; - text-transform: uppercase; -} -.divider { - height: 1px; - background-color: #e0e0e0; - margin-bottom: 20px; -} -.sidebar-nav { - display: flex; - flex-direction: column; - gap: 16px; -} -.nav-link { - text-decoration: none; - color: #888; - font-size: 14px; - font-weight: 500; - transition: color 0.2s; -} -.nav-link:hover { - color: #5a5a5a; -} -.nav-link.active { - color: var(--color-primary); /* Blue color matching the image */ - font-weight: 600; -} diff --git a/src/app/features/store/pages/account-page/components/account-sidebar/account-sidebar.spec.ts b/src/app/features/store/pages/account-page/components/account-sidebar/account-sidebar.spec.ts deleted file mode 100644 index 72b27d2..0000000 --- a/src/app/features/store/pages/account-page/components/account-sidebar/account-sidebar.spec.ts +++ /dev/null @@ -1,85 +0,0 @@ -import { signal } from '@angular/core'; -import { TestBed } from '@angular/core/testing'; -import { provideRouter } from '@angular/router'; -import { describe, expect, it } from 'vitest'; - -import { Tenant } from '../../../../../../core/services/tenant.interface'; -import { TenantService } from '../../../../../../core/services/tenant.service'; -import { AccountSidebar } from './account-sidebar'; - -describe('AccountSidebar', () => { - it('renders only the account submenus configured for the tenant', async () => { - const tenant = signal({ - id: 1, - codigo: 'test', - nombre: 'Test', - dominio: 'localhost', - primary_color: '#000000', - secondary_color: '#000000', - danger_color: '#000000', - success_color: '#000000', - header_bg_color: '#ffffff', - footer_bg_color: '#ffffff', - header_logo: '', - footer_logo: '', - categories: [], - menues: [ - { - id: 1, - code: 'account', - label: 'Mi cuenta', - parent_menu_code: null, - content_type: 'dynamic', - route: '/mi-cuenta', - submenues: [ - { - id: 2, - code: 'account.profile', - label: 'Datos personales', - parent_menu_code: 'account', - content_type: 'dynamic', - route: '/mi-cuenta/datos-personales', - submenues: [], - }, - { - id: 3, - code: 'account.purchases', - label: 'Mis compras', - parent_menu_code: 'account', - content_type: 'dynamic', - route: '/mi-cuenta/compras', - submenues: [], - }, - ], - }, - ], - }); - - await TestBed.configureTestingModule({ - imports: [AccountSidebar], - providers: [ - provideRouter([]), - { - provide: TenantService, - useValue: { tenant: tenant.asReadonly() }, - }, - ], - }).compileComponents(); - - const fixture = TestBed.createComponent(AccountSidebar); - fixture.detectChanges(); - - const element = fixture.nativeElement as HTMLElement; - const links = Array.from(element.querySelectorAll('.nav-link')); - - expect(element.querySelector('.sidebar-title')?.textContent?.trim()).toBe('Mi cuenta'); - expect(links.map((link) => link.textContent?.trim())).toEqual([ - 'Datos personales', - 'Mis compras', - ]); - expect(links.map((link) => link.getAttribute('href'))).toEqual([ - '/mi-cuenta/datos-personales', - '/mi-cuenta/compras', - ]); - }); -}); diff --git a/src/app/features/store/pages/account-page/components/account-sidebar/account-sidebar.ts b/src/app/features/store/pages/account-page/components/account-sidebar/account-sidebar.ts deleted file mode 100644 index 2fd1025..0000000 --- a/src/app/features/store/pages/account-page/components/account-sidebar/account-sidebar.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { Component, computed, inject } from '@angular/core'; -import { RouterLink, RouterLinkActive } from '@angular/router'; - -import { findMenu } from '../../../../../../core/services/menu.utils'; -import { TenantService } from '../../../../../../core/services/tenant.service'; - -@Component({ - selector: 'app-account-sidebar', - standalone: true, - imports: [RouterLink, RouterLinkActive], - templateUrl: './account-sidebar.html', - styleUrl: './account-sidebar.scss', -}) -export class AccountSidebar { - private readonly tenantService = inject(TenantService); - - protected readonly menu = computed(() => - findMenu(this.tenantService.tenant()?.menues ?? [], 'account'), - ); - protected readonly submenues = computed(() => this.menu()?.submenues ?? []); -} diff --git a/src/app/features/store/pages/account-page/sidebar-layout/sidebar-layout.html b/src/app/features/store/pages/account-page/sidebar-layout/sidebar-layout.html deleted file mode 100644 index d1c2cec..0000000 --- a/src/app/features/store/pages/account-page/sidebar-layout/sidebar-layout.html +++ /dev/null @@ -1,6 +0,0 @@ - diff --git a/src/app/features/store/pages/account-page/sidebar-layout/sidebar-layout.scss b/src/app/features/store/pages/account-page/sidebar-layout/sidebar-layout.scss deleted file mode 100644 index e12d4c8..0000000 --- a/src/app/features/store/pages/account-page/sidebar-layout/sidebar-layout.scss +++ /dev/null @@ -1,17 +0,0 @@ -.sidebar-layout { - display: flex; - margin: 0 auto; - gap: 100px; - min-height: calc(100vh - 100px); -} -.account-sidebar { - width: 250px; - flex-shrink: 0; -} -.account-content { - flex: 1; -} - -label { - font-size: 13px !important; -} diff --git a/src/app/features/store/pages/account-page/sidebar-layout/sidebar-layout.ts b/src/app/features/store/pages/account-page/sidebar-layout/sidebar-layout.ts deleted file mode 100644 index 7ba43ba..0000000 --- a/src/app/features/store/pages/account-page/sidebar-layout/sidebar-layout.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { Component } from '@angular/core'; -import { RouterOutlet } from '@angular/router'; -import { AccountSidebar } from '../components/account-sidebar/account-sidebar'; - -@Component({ - selector: 'app-sidebar-layout', - standalone: true, - imports: [RouterOutlet, AccountSidebar], - templateUrl: './sidebar-layout.html', - styleUrl: './sidebar-layout.scss', -}) -export class SidebarLayout {} diff --git a/src/app/features/store/pages/help-page/components/help-sidebar/help-sidebar.html b/src/app/features/store/pages/help-page/components/help-sidebar/help-sidebar.html deleted file mode 100644 index 487b033..0000000 --- a/src/app/features/store/pages/help-page/components/help-sidebar/help-sidebar.html +++ /dev/null @@ -1,16 +0,0 @@ - diff --git a/src/app/features/store/pages/help-page/components/help-sidebar/help-sidebar.scss b/src/app/features/store/pages/help-page/components/help-sidebar/help-sidebar.scss deleted file mode 100644 index 8e60bb6..0000000 --- a/src/app/features/store/pages/help-page/components/help-sidebar/help-sidebar.scss +++ /dev/null @@ -1,62 +0,0 @@ -.help-sidebar { - display: flex; - flex-direction: column; -} - -.help-sidebar__title { - margin: 0 0 0.75rem; - color: #1a1a1a; - font-size: 1rem; - font-weight: 700; - line-height: 1.25; - text-transform: uppercase; -} - -.help-sidebar__divider { - height: 1px; - margin-bottom: 1.25rem; - background: #dedede; -} - -.help-sidebar__nav { - display: flex; - flex-direction: column; - gap: 1rem; -} - -.help-sidebar__link { - color: #888888; - font-size: 0.875rem; - font-weight: 500; - line-height: 1.35; - text-decoration: none; - transition: color 150ms ease; - - &:hover, - &:focus-visible { - color: #5a5a5a; - } - - &:focus-visible { - border-radius: 2px; - outline: 2px solid var(--color-primary); - outline-offset: 3px; - } -} - -.help-sidebar__link--active { - color: var(--color-primary); - font-weight: 600; -} - -@media (max-width: 767.98px) { - .help-sidebar__title { - text-align: center; - } - - .help-sidebar__nav { - flex-flow: row wrap; - justify-content: center; - gap: 0.75rem 1.25rem; - } -} diff --git a/src/app/features/store/pages/help-page/components/help-sidebar/help-sidebar.spec.ts b/src/app/features/store/pages/help-page/components/help-sidebar/help-sidebar.spec.ts deleted file mode 100644 index 264460a..0000000 --- a/src/app/features/store/pages/help-page/components/help-sidebar/help-sidebar.spec.ts +++ /dev/null @@ -1,72 +0,0 @@ -import { signal } from '@angular/core'; -import { TestBed } from '@angular/core/testing'; -import { provideRouter } from '@angular/router'; -import { describe, expect, it } from 'vitest'; - -import { Tenant } from '../../../../../../core/services/tenant.interface'; -import { TenantService } from '../../../../../../core/services/tenant.service'; -import { HelpSidebar } from './help-sidebar'; - -describe('HelpSidebar', () => { - it('renders the parent and submenu labels supplied by the tenant', async () => { - const tenant = signal({ - id: 1, - codigo: 'test', - nombre: 'Test', - dominio: 'localhost', - primary_color: '#000000', - secondary_color: '#000000', - danger_color: '#000000', - success_color: '#000000', - header_bg_color: '#ffffff', - footer_bg_color: '#ffffff', - header_logo: '', - footer_logo: '', - categories: [], - menues: [ - { - id: 1, - code: 'help', - label: 'Centro de ayuda', - parent_menu_code: null, - content_type: 'dynamic', - route: '/ayuda', - submenues: [ - { - id: 2, - code: 'help.faq', - label: 'Consultas habituales', - parent_menu_code: 'help', - content_type: 'dynamic', - route: '/ayuda/preguntas-frecuentes', - submenues: [], - }, - ], - }, - ], - }); - - await TestBed.configureTestingModule({ - imports: [HelpSidebar], - providers: [ - provideRouter([]), - { - provide: TenantService, - useValue: { tenant: tenant.asReadonly() }, - }, - ], - }).compileComponents(); - - const fixture = TestBed.createComponent(HelpSidebar); - fixture.detectChanges(); - - const element = fixture.nativeElement as HTMLElement; - - expect(element.querySelector('.help-sidebar__title')?.textContent?.trim()).toBe( - 'Centro de ayuda', - ); - expect(element.querySelector('.help-sidebar__link')?.textContent?.trim()).toBe( - 'Consultas habituales', - ); - }); -}); diff --git a/src/app/features/store/pages/help-page/components/help-sidebar/help-sidebar.ts b/src/app/features/store/pages/help-page/components/help-sidebar/help-sidebar.ts deleted file mode 100644 index 2839b8c..0000000 --- a/src/app/features/store/pages/help-page/components/help-sidebar/help-sidebar.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { Component, computed, inject } from '@angular/core'; -import { RouterLink, RouterLinkActive } from '@angular/router'; - -import { findMenu } from '../../../../../../core/services/menu.utils'; -import { TenantService } from '../../../../../../core/services/tenant.service'; - -@Component({ - selector: 'app-help-sidebar', - imports: [RouterLink, RouterLinkActive], - templateUrl: './help-sidebar.html', - styleUrl: './help-sidebar.scss', -}) -export class HelpSidebar { - private readonly tenantService = inject(TenantService); - - protected readonly menu = computed(() => - findMenu(this.tenantService.tenant()?.menues ?? [], 'help'), - ); - protected readonly submenues = computed(() => this.menu()?.submenues ?? []); -} diff --git a/src/app/features/store/pages/help-page/sidebar-layout/help-sidebar-layout.html b/src/app/features/store/pages/help-page/sidebar-layout/help-sidebar-layout.html deleted file mode 100644 index 2658476..0000000 --- a/src/app/features/store/pages/help-page/sidebar-layout/help-sidebar-layout.html +++ /dev/null @@ -1,7 +0,0 @@ -
- - -
- -
-
diff --git a/src/app/features/store/pages/help-page/sidebar-layout/help-sidebar-layout.scss b/src/app/features/store/pages/help-page/sidebar-layout/help-sidebar-layout.scss deleted file mode 100644 index d1ca2d4..0000000 --- a/src/app/features/store/pages/help-page/sidebar-layout/help-sidebar-layout.scss +++ /dev/null @@ -1,25 +0,0 @@ -:host { - display: block; -} - -.help-layout { - display: grid; - grid-template-columns: minmax(190px, 250px) minmax(0, 1fr); - gap: clamp(3rem, 8vw, 6.25rem); - width: 100%; -} - -.help-layout__sidebar { - min-width: 0; -} - -.help-layout__content { - min-width: 0; -} - -@media (max-width: 767.98px) { - .help-layout { - grid-template-columns: 1fr; - gap: 2rem; - } -} diff --git a/src/app/features/store/pages/help-page/sidebar-layout/help-sidebar-layout.ts b/src/app/features/store/pages/help-page/sidebar-layout/help-sidebar-layout.ts deleted file mode 100644 index e030876..0000000 --- a/src/app/features/store/pages/help-page/sidebar-layout/help-sidebar-layout.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { Component } from '@angular/core'; -import { RouterOutlet } from '@angular/router'; - -import { HelpSidebar } from '../components/help-sidebar/help-sidebar'; - -@Component({ - selector: 'app-help-sidebar-layout', - imports: [RouterOutlet, HelpSidebar], - templateUrl: './help-sidebar-layout.html', - styleUrl: './help-sidebar-layout.scss', -}) -export class HelpSidebarLayout {} diff --git a/src/app/features/store/store.routes.ts b/src/app/features/store/store.routes.ts index ae173cb..a78e899 100644 --- a/src/app/features/store/store.routes.ts +++ b/src/app/features/store/store.routes.ts @@ -116,9 +116,13 @@ export const routes: Routes = [ { path: 'ayuda', canActivate: [hasMenuGuard('help')], + data: { + menuCode: 'help', + navigationLabel: 'Secciones de ayuda', + }, loadComponent: () => - import('./pages/help-page/sidebar-layout/help-sidebar-layout').then( - (m) => m.HelpSidebarLayout, + import('./components/menu-page-layout/menu-page-layout.component').then( + (m) => m.MenuPageLayoutComponent, ), children: [ { @@ -152,8 +156,14 @@ export const routes: Routes = [ { path: 'mi-cuenta', canActivate: [authGuard, hasMenuGuard('account')], + data: { + menuCode: 'account', + navigationLabel: 'Secciones de mi cuenta', + }, loadComponent: () => - import('./pages/account-page/sidebar-layout/sidebar-layout').then((m) => m.SidebarLayout), + import('./components/menu-page-layout/menu-page-layout.component').then( + (m) => m.MenuPageLayoutComponent, + ), children: [ { path: 'datos-personales',