diff --git a/src/app/app.routes.server.ts b/src/app/app.routes.server.ts index 2c5a11d..e407b8c 100644 --- a/src/app/app.routes.server.ts +++ b/src/app/app.routes.server.ts @@ -1,6 +1,10 @@ import { RenderMode, ServerRoute } from '@angular/ssr'; export const serverRoutes: ServerRoute[] = [ + { + path: 'checkout', + renderMode: RenderMode.Client + }, { path: '**', renderMode: RenderMode.Server diff --git a/src/app/app.routes.spec.ts b/src/app/app.routes.spec.ts index 5d89968..91711d6 100644 --- a/src/app/app.routes.spec.ts +++ b/src/app/app.routes.spec.ts @@ -182,4 +182,16 @@ describe('app routes', () => { expect(compiled.querySelector('.tenant-status')).not.toBeNull(); expect(compiled.textContent).toContain('No encontramos una tienda para este dominio'); }); + + it('redirects unauthenticated users from /checkout to /login', async () => { + const { router } = await renderAppAt('/checkout', createTenantServiceStub(), createAuthServiceStub(false)); + + expect(router.url).toBe('/login'); + }); + + it('allows authenticated users to access /checkout', async () => { + const { router } = await renderAppAt('/checkout', createTenantServiceStub(), createAuthServiceStub(true)); + + expect(router.url).toBe('/checkout'); + }); }); 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 1fcd54a..0fc586c 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 @@ -154,7 +154,7 @@ describe('StoreLayoutComponent', () => { const fixture = TestBed.createComponent(StoreLayoutComponent); fixture.detectChanges(); - fixture.componentInstance.isCartOpen.set(true); + (fixture.componentInstance as any).isCartOpen.set(true); fixture.detectChanges(); const compiled = fixture.nativeElement as HTMLElement; @@ -166,6 +166,6 @@ describe('StoreLayoutComponent', () => { fixture.detectChanges(); expect(router.navigate).toHaveBeenCalledWith(['/checkout']); - expect(fixture.componentInstance.isCartOpen()).toBe(false); + expect((fixture.componentInstance as any).isCartOpen()).toBe(false); }); }); diff --git a/src/app/core/services/checkout.service.ts b/src/app/core/services/checkout.service.ts new file mode 100644 index 0000000..9290d71 --- /dev/null +++ b/src/app/core/services/checkout.service.ts @@ -0,0 +1,23 @@ +import { inject, Injectable } from '@angular/core'; +import { HttpClient } from '@angular/common/http'; +import { firstValueFrom } from 'rxjs'; + +import { environment } from '../../../environments/environment'; +import { BankAccount } from './tenant.interface'; + +@Injectable({ + providedIn: 'root' +}) +export class CheckoutService { + private readonly http = inject(HttpClient); + + async getSelectedBankAccount(tenantCode: string): Promise { + const response = await firstValueFrom( + this.http.get<{ data: BankAccount }>(`${environment.url}tenants/${tenantCode}/bank-accounts/selected`) + ); + if (!response?.data) { + throw new Error('No se encontraron los datos de la cuenta bancaria seleccionada.'); + } + return response.data; + } +} diff --git a/src/app/core/services/tenant.interface.ts b/src/app/core/services/tenant.interface.ts index 269b2fc..afd6b7c 100644 --- a/src/app/core/services/tenant.interface.ts +++ b/src/app/core/services/tenant.interface.ts @@ -1,5 +1,14 @@ import { ApiResponse } from './api-response.interface'; +export interface BankAccount { + id: number; + tenant_code: string; + titular: string; + entidad: string; + alias: string; + cvu: string; +} + export interface Tenant { id: number; codigo: string; @@ -13,6 +22,8 @@ export interface Tenant { footer_bg_color: string; header_logo: string; footer_logo: string; + selected_bank_account_id?: number | null; + selected_bank_account?: BankAccount | null; } export type TenantBootstrapResponse = ApiResponse; diff --git a/src/app/features/store/pages/checkout-page/checkout-page.component.html b/src/app/features/store/pages/checkout-page/checkout-page.component.html index 46276d8..6a37b41 100644 --- a/src/app/features/store/pages/checkout-page/checkout-page.component.html +++ b/src/app/features/store/pages/checkout-page/checkout-page.component.html @@ -15,7 +15,7 @@ [paymentMethods]="paymentMethods" [selectedPaymentMethod]="selectedPaymentMethod()" [copiedTransferField]="copiedTransferField()" - [transferAccount]="transferAccount" + [transferAccount]="transferAccount()" (paymentMethodChange)="selectPaymentMethod($event)" (copyTransferValue)="copyTransferValue($event.field, $event.value)" (cancelStep)="onCancel()" diff --git a/src/app/features/store/pages/checkout-page/checkout-page.component.ts b/src/app/features/store/pages/checkout-page/checkout-page.component.ts index 44903cb..25998a1 100644 --- a/src/app/features/store/pages/checkout-page/checkout-page.component.ts +++ b/src/app/features/store/pages/checkout-page/checkout-page.component.ts @@ -4,6 +4,9 @@ import { Router } from '@angular/router'; import { startWith } from 'rxjs'; import { CartService } from '../../../../core/services/cart/cart.service'; +import { TenantService } from '../../../../core/services/tenant.service'; +import { CheckoutService } from '../../../../core/services/checkout.service'; +import { BankAccount } from '../../../../core/services/tenant.interface'; import { CartItem } from '../../../../core/services/cart/cart.interface'; import { CartComponent, CartItemMock } from '../../../../shared/components/cart/cart.component'; import { StepComponent } from '../../../../shared/components/stepper/step.component'; @@ -30,6 +33,8 @@ export class CheckoutPageComponent { private readonly formBuilder = inject(FormBuilder); private readonly cartService = inject(CartService); private readonly router = inject(Router); + private readonly tenantService = inject(TenantService); + private readonly checkoutService = inject(CheckoutService); @ViewChild(StepperComponent) stepper!: StepperComponent; @@ -63,17 +68,36 @@ export class CheckoutPageComponent { ]; protected readonly selectedPaymentMethod = signal('qr'); protected readonly copiedTransferField = signal(null); - protected readonly transferAccount: TransferAccount = { + protected readonly transferAccount = signal({ titular: 'Nombre y Apellido', entidad: 'TelePagos', cvu: '0000000000000000000000', alias: 'telepagos.ar' - }; + }); constructor() { this.form.statusChanges .pipe(startWith(this.form.status)) .subscribe(() => this.isStep1Valid.set(this.form.valid)); + + void this.loadSelectedBankAccount(); + } + + private async loadSelectedBankAccount(): Promise { + const tenant = this.tenantService.tenant(); + if (!tenant) return; + + try { + const data = await this.checkoutService.getSelectedBankAccount(tenant.codigo); + this.transferAccount.set({ + titular: data.titular, + entidad: data.entidad, + cvu: data.cvu, + alias: data.alias + }); + } catch (error) { + console.error('Failed to load selected bank account:', error); + } } private mapCartItemToMock(item: CartItem): CartItemMock { diff --git a/src/app/features/store/store.routes.ts b/src/app/features/store/store.routes.ts index becbeb8..a62f11a 100644 --- a/src/app/features/store/store.routes.ts +++ b/src/app/features/store/store.routes.ts @@ -2,7 +2,7 @@ import { Routes } from '@angular/router'; import { AuthLayoutComponent } from '../../core/layout/auth-layout/auth-layout.component'; import { StoreLayoutComponent } from '../../core/layout/store-layout/store-layout.component'; -import { guestOnlyGuard } from '../../core/services/auth/auth.guards'; +import { authGuard, guestOnlyGuard } from '../../core/services/auth/auth.guards'; import { LoginPageComponent } from './pages/login-page/login-page.component'; import { RegisterPageComponent } from './pages/register-page/register-page.component'; import { StoreHomePageComponent } from './pages/store-home-page/store-home-page.component'; @@ -47,6 +47,7 @@ export const routes: Routes = [ }, { path: 'checkout', + canActivate: [authGuard], loadComponent: () => import('./pages/checkout-page/checkout-page.component').then( (m) => m.CheckoutPageComponent