diff --git a/web/src/app/app/login/login.html b/web/src/app/app/login/login.html index efe01e8..8ff12bc 100644 --- a/web/src/app/app/login/login.html +++ b/web/src/app/app/login/login.html @@ -24,6 +24,12 @@

Sign in

+ + + + Email diff --git a/web/src/app/app/login/login.scss b/web/src/app/app/login/login.scss index b228e07..bdc47cf 100644 --- a/web/src/app/app/login/login.scss +++ b/web/src/app/app/login/login.scss @@ -12,3 +12,10 @@ font-size: 0.875rem; text-align: right; } + +.auth-separator { + margin: 0; + text-align: center; + color: var(--mat-sys-on-surface-variant); + font-size: 0.875rem; +} diff --git a/web/src/app/app/login/login.spec.ts b/web/src/app/app/login/login.spec.ts index c674ba0..06a3779 100644 --- a/web/src/app/app/login/login.spec.ts +++ b/web/src/app/app/login/login.spec.ts @@ -13,6 +13,7 @@ class MockAuthService { waitUntilInitialized = async (): Promise => {}; signInWithPassword = async (): Promise => null; + signInWithGoogle = vi.fn().mockResolvedValue(null); signOut = async (): Promise => null; } @@ -58,4 +59,26 @@ describe('LoginPage', () => { expect(router.navigateByUrl).toHaveBeenCalledWith('/clubs', { replaceUrl: true }); }); + + it('starts Google sign-in with the latest redirectTo value', async () => { + const fixture = TestBed.createComponent(LoginPage); + const page = fixture.componentInstance as any; + fixture.detectChanges(); + redirectTo = '/clubs/123'; + + await page.signInWithGoogle(); + + expect(auth.signInWithGoogle).toHaveBeenCalledWith('/clubs/123'); + }); + + it('shows a Google sign-in error', async () => { + const fixture = TestBed.createComponent(LoginPage); + const page = fixture.componentInstance as any; + auth.signInWithGoogle.mockResolvedValue('OAuth provider unavailable'); + fixture.detectChanges(); + + await page.signInWithGoogle(); + + expect(page.authMessage()).toBe('OAuth provider unavailable'); + }); }); diff --git a/web/src/app/app/login/login.ts b/web/src/app/app/login/login.ts index f38eab8..cbd516f 100644 --- a/web/src/app/app/login/login.ts +++ b/web/src/app/app/login/login.ts @@ -55,4 +55,15 @@ export class LoginPage { this.isSubmitting.set(false); } + + protected async signInWithGoogle(): Promise { + this.authMessage.set(null); + this.isSubmitting.set(true); + + const errorMessage = await this.auth.signInWithGoogle(this.getRedirectTo()); + if (errorMessage) { + this.authMessage.set(errorMessage); + this.isSubmitting.set(false); + } + } } diff --git a/web/src/app/auth.service.ts b/web/src/app/auth.service.ts index c790bf2..cfb9b16 100644 --- a/web/src/app/auth.service.ts +++ b/web/src/app/auth.service.ts @@ -59,6 +59,20 @@ export class AuthService { return error?.message ?? null; } + async signInWithGoogle(redirectPath = '/'): Promise { + if (!this.authClient) { + return 'Supabase is not configured.'; + } + + const { error } = await this.authClient.signInWithOAuth({ + provider: 'google', + options: { + redirectTo: this.buildRedirectUrl(redirectPath), + }, + }); + return error?.message ?? null; + } + async signUp(email: string, password: string): Promise { if (!this.authClient) { return 'Supabase is not configured.';