update routing client
This commit is contained in:
parent
d7b7bc7be0
commit
218b2de59a
18 changed files with 377 additions and 148 deletions
|
|
@ -1,11 +1,62 @@
|
||||||
import { NgModule } from '@angular/core';
|
import { NgModule, inject } from '@angular/core';
|
||||||
import { RouterModule, Routes } from '@angular/router';
|
import { CanActivateFn, Router, RouterModule, Routes } from '@angular/router';
|
||||||
|
import { UserService } from './core/services/user.service';
|
||||||
|
import { map } from 'rxjs';
|
||||||
|
|
||||||
|
const authGuard: CanActivateFn = () => {
|
||||||
|
const userService: UserService = inject(UserService);
|
||||||
|
const router: Router = inject(Router);
|
||||||
|
return userService.isAuthenticated.pipe(
|
||||||
|
map((isAuth) => isAuth || router.parseUrl('/login'))
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const loginGuard: CanActivateFn = () => {
|
||||||
|
const userService: UserService = inject(UserService);
|
||||||
|
const router: Router = inject(Router);
|
||||||
|
|
||||||
|
return userService.isAuthenticated.pipe(
|
||||||
|
map((isAuth) => {
|
||||||
|
if (!isAuth) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return router.parseUrl('/dashboard');
|
||||||
|
})
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
const routes: Routes = [
|
const routes: Routes = [
|
||||||
|
{
|
||||||
|
path: 'login',
|
||||||
|
loadComponent: () =>
|
||||||
|
import('./core/auth/auth.component').then((m) => m.AuthComponent),
|
||||||
|
canActivate: [loginGuard],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'register',
|
||||||
|
loadComponent: () =>
|
||||||
|
import('./core/auth/auth.component').then((m) => m.AuthComponent),
|
||||||
|
canActivate: [loginGuard],
|
||||||
|
},
|
||||||
{
|
{
|
||||||
path: '',
|
path: '',
|
||||||
loadComponent: () =>
|
loadComponent: () =>
|
||||||
import('./features/home/home.component').then((m) => m.HomeComponent),
|
import('./core/layout/layout.component').then((m) => m.LayoutComponent),
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
path: '',
|
||||||
|
pathMatch: 'full',
|
||||||
|
redirectTo: 'dashboard',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'dashboard',
|
||||||
|
loadComponent: () =>
|
||||||
|
import('./features/dashboard/dashboard.component').then(
|
||||||
|
(m) => m.DashboardComponent
|
||||||
|
),
|
||||||
|
canActivate: [authGuard],
|
||||||
|
},
|
||||||
|
],
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1 @@
|
||||||
<app-layout-header></app-layout-header>
|
|
||||||
|
|
||||||
<router-outlet></router-outlet>
|
<router-outlet></router-outlet>
|
||||||
|
|
||||||
<app-layout-footer></app-layout-footer>
|
|
||||||
|
|
|
||||||
|
|
@ -2,16 +2,15 @@ import { NgModule } from '@angular/core';
|
||||||
import { BrowserModule } from '@angular/platform-browser';
|
import { BrowserModule } from '@angular/platform-browser';
|
||||||
|
|
||||||
import { AppRoutingModule } from './app-routing.module';
|
import { AppRoutingModule } from './app-routing.module';
|
||||||
import { AppComponent } from './app.component';
|
|
||||||
import {
|
import {
|
||||||
GoogleLoginProvider,
|
GoogleLoginProvider,
|
||||||
GoogleSigninButtonModule,
|
|
||||||
SocialAuthServiceConfig,
|
SocialAuthServiceConfig,
|
||||||
SocialLoginModule,
|
SocialLoginModule,
|
||||||
} from '@abacritt/angularx-social-login';
|
} from '@abacritt/angularx-social-login';
|
||||||
import { CoreModule } from './core/core.module';
|
|
||||||
import { FooterComponent } from './core/layout/footer.component';
|
import { FooterComponent } from './core/layout/footer.component';
|
||||||
import { HeaderComponent } from './core/layout/header.component';
|
import { HeaderComponent } from './core/layout/header.component';
|
||||||
|
import { HttpClientModule } from '@angular/common/http';
|
||||||
|
import { AppComponent } from './app.component';
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
declarations: [AppComponent],
|
declarations: [AppComponent],
|
||||||
|
|
@ -21,6 +20,7 @@ import { HeaderComponent } from './core/layout/header.component';
|
||||||
HeaderComponent,
|
HeaderComponent,
|
||||||
AppRoutingModule,
|
AppRoutingModule,
|
||||||
SocialLoginModule,
|
SocialLoginModule,
|
||||||
|
HttpClientModule,
|
||||||
],
|
],
|
||||||
providers: [
|
providers: [
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,84 @@
|
||||||
|
<main class="flex flex-col justify-around">
|
||||||
|
<div class="flex min-h-full flex-col justify-center px-6 py-12 lg:px-8">
|
||||||
|
<div class="sm:mx-auto sm:w-full sm:max-w-sm">
|
||||||
|
<img
|
||||||
|
class="mx-auto h-10 w-auto"
|
||||||
|
src="https://tailwindui.com/img/logos/mark.svg?color=indigo&shade=600"
|
||||||
|
alt="Your Company"
|
||||||
|
/>
|
||||||
|
<h2
|
||||||
|
class="mt-10 text-center text-2xl font-bold leading-9 tracking-tight text-gray-900"
|
||||||
|
>
|
||||||
|
Sign in to your account
|
||||||
|
</h2>
|
||||||
|
<div class="mt-10 sm:mx-auto sm:w-full sm:max-w-sm flex justify-center">
|
||||||
|
<asl-google-signin-button
|
||||||
|
type="standard"
|
||||||
|
shape="pill"
|
||||||
|
text="signin_with"
|
||||||
|
size="large"
|
||||||
|
logo_alignment="center"
|
||||||
|
theme="outline"
|
||||||
|
width="300"
|
||||||
|
></asl-google-signin-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="mt-10 sm:mx-auto sm:w-full sm:max-w-sm">
|
||||||
|
<form class="space-y-6" action="#" method="POST">
|
||||||
|
<div>
|
||||||
|
<label
|
||||||
|
for="email"
|
||||||
|
class="block text-sm font-medium leading-6 text-gray-900"
|
||||||
|
>Email address</label
|
||||||
|
>
|
||||||
|
<div class="mt-2">
|
||||||
|
<input
|
||||||
|
id="email"
|
||||||
|
name="email"
|
||||||
|
type="email"
|
||||||
|
autocomplete="email"
|
||||||
|
required
|
||||||
|
class="px-3 block w-full rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<div class="flex items-center justify-between">
|
||||||
|
<label
|
||||||
|
for="password"
|
||||||
|
class="block text-sm font-medium leading-6 text-gray-900"
|
||||||
|
>Password</label
|
||||||
|
>
|
||||||
|
<div class="text-sm">
|
||||||
|
<a
|
||||||
|
href="#"
|
||||||
|
class="font-semibold text-indigo-600 hover:text-indigo-500"
|
||||||
|
>Forgot password?</a
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="mt-2">
|
||||||
|
<input
|
||||||
|
id="password"
|
||||||
|
name="password"
|
||||||
|
type="password"
|
||||||
|
autocomplete="current-password"
|
||||||
|
required
|
||||||
|
class="px-3 block w-full rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<button
|
||||||
|
type="submit"
|
||||||
|
class="flex w-full justify-center rounded-md bg-indigo-600 px-3 py-1.5 text-sm font-semibold leading-6 text-white shadow-sm hover:bg-indigo-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600"
|
||||||
|
>
|
||||||
|
Sign in
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
|
@ -1,7 +1,15 @@
|
||||||
import { NgIf } from '@angular/common';
|
import { NgIf } from '@angular/common';
|
||||||
import { Component, OnInit } from '@angular/core';
|
import { Component, OnDestroy, OnInit } from '@angular/core';
|
||||||
import { Form, FormControl, FormGroup } from '@angular/forms';
|
import { FormControl, FormGroup, Validators } from '@angular/forms';
|
||||||
import { RouterLink } from '@angular/router';
|
import { ActivatedRoute, Router, RouterLink } from '@angular/router';
|
||||||
|
import { Subject, takeUntil } from 'rxjs';
|
||||||
|
import { Errors } from '../models/errors.model';
|
||||||
|
import { UserService } from '../services/user.service';
|
||||||
|
import {
|
||||||
|
GoogleSigninButtonModule,
|
||||||
|
SocialAuthService,
|
||||||
|
} from '@abacritt/angularx-social-login';
|
||||||
|
import { User } from '../models/user.model';
|
||||||
|
|
||||||
interface AuthForm {
|
interface AuthForm {
|
||||||
email: FormControl<string>;
|
email: FormControl<string>;
|
||||||
|
|
@ -12,30 +20,95 @@ interface AuthForm {
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-auth-page',
|
selector: 'app-auth-page',
|
||||||
templateUrl: './auth.component.html',
|
templateUrl: './auth.component.html',
|
||||||
imports: [RouterLink, NgIf],
|
imports: [RouterLink, NgIf, GoogleSigninButtonModule],
|
||||||
standalone: true,
|
standalone: true,
|
||||||
})
|
})
|
||||||
export class AuthComponent implements OnInit {
|
export class AuthComponent implements OnInit, OnDestroy {
|
||||||
authType = '';
|
authType = '';
|
||||||
title = '';
|
title = '';
|
||||||
|
errors: Errors = { errors: {} };
|
||||||
isSubmitting = false;
|
isSubmitting = false;
|
||||||
authForm: FormGroup<AuthForm>;
|
authForm: FormGroup<AuthForm>;
|
||||||
|
destroy$ = new Subject<void>();
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private readonly route: ActivatedRoute,
|
private readonly route: ActivatedRoute,
|
||||||
private readonly router: Router,
|
private readonly router: Router,
|
||||||
private readonly userService: UserService
|
private readonly userService: UserService,
|
||||||
|
private readonly authService: SocialAuthService
|
||||||
) {
|
) {
|
||||||
// use FormBuilder to create a form group
|
|
||||||
this.authForm = new FormGroup<AuthForm>({
|
this.authForm = new FormGroup<AuthForm>({
|
||||||
email: new FormControl('', {
|
email: new FormControl('', {
|
||||||
validators: [Validators.required],
|
validators: [Validators.required, Validators.email],
|
||||||
nonNullable: true,
|
nonNullable: true,
|
||||||
}),
|
}),
|
||||||
password: new FormControl('', {
|
password: new FormControl('', {
|
||||||
validators: [Validators.required],
|
validators: [Validators.required, Validators.minLength(8)],
|
||||||
nonNullable: true,
|
nonNullable: true,
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ngOnInit(): void {
|
||||||
|
this.authType = this.route.snapshot.url.at(-1)!.path;
|
||||||
|
this.title = this.authType === 'login' ? 'Sign in' : 'Sign up';
|
||||||
|
if (this.authType === 'resigter') {
|
||||||
|
this.authForm.addControl(
|
||||||
|
'username',
|
||||||
|
new FormControl('', {
|
||||||
|
validators: [Validators.required],
|
||||||
|
nonNullable: true,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// google login
|
||||||
|
this.authService.authState.subscribe((user) => {
|
||||||
|
this.userService.setAuth({
|
||||||
|
username: user.name,
|
||||||
|
email: user.email,
|
||||||
|
token: user.idToken,
|
||||||
|
image: user.photoUrl,
|
||||||
|
bio: '',
|
||||||
|
} satisfies User);
|
||||||
|
|
||||||
|
void this.router.navigate(['/dashboard']);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnDestroy(): void {
|
||||||
|
this.destroy$.next();
|
||||||
|
this.destroy$.complete();
|
||||||
|
}
|
||||||
|
|
||||||
|
summitForm(): void {
|
||||||
|
this.isSubmitting = true;
|
||||||
|
this.errors = { errors: {} };
|
||||||
|
|
||||||
|
let observable;
|
||||||
|
if (this.authType === 'login') {
|
||||||
|
observable = this.userService.login(
|
||||||
|
this.authForm.value as {
|
||||||
|
email: string;
|
||||||
|
password: string;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
observable = this.userService.register(
|
||||||
|
this.authForm.value as {
|
||||||
|
email: string;
|
||||||
|
password: string;
|
||||||
|
username: string;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
observable.pipe(takeUntil(this.destroy$)).subscribe({
|
||||||
|
next: () => void this.router.navigate(['/login']),
|
||||||
|
error: (err: Errors) => {
|
||||||
|
this.errors = err;
|
||||||
|
this.isSubmitting = false;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,12 @@
|
||||||
|
import { NgIf } from '@angular/common';
|
||||||
import { Component } from '@angular/core';
|
import { Component } from '@angular/core';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-layout-header',
|
selector: 'app-layout-header',
|
||||||
templateUrl: './header.component.html',
|
templateUrl: './header.component.html',
|
||||||
|
imports: [NgIf],
|
||||||
standalone: true,
|
standalone: true,
|
||||||
})
|
})
|
||||||
export class HeaderComponent {}
|
export class HeaderComponent {
|
||||||
|
constructor() {}
|
||||||
|
}
|
||||||
|
|
|
||||||
5
client/src/app/core/layout/layout.component.html
Normal file
5
client/src/app/core/layout/layout.component.html
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
<app-layout-header></app-layout-header>
|
||||||
|
|
||||||
|
<router-outlet></router-outlet>
|
||||||
|
|
||||||
|
<app-layout-footer></app-layout-footer>
|
||||||
12
client/src/app/core/layout/layout.component.ts
Normal file
12
client/src/app/core/layout/layout.component.ts
Normal file
|
|
@ -0,0 +1,12 @@
|
||||||
|
import { Component } from '@angular/core';
|
||||||
|
import { HeaderComponent } from './header.component';
|
||||||
|
import { FooterComponent } from './footer.component';
|
||||||
|
import { RouterModule } from '@angular/router';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-layout',
|
||||||
|
templateUrl: './layout.component.html',
|
||||||
|
standalone: true,
|
||||||
|
imports: [HeaderComponent, FooterComponent, RouterModule],
|
||||||
|
})
|
||||||
|
export class LayoutComponent {}
|
||||||
3
client/src/app/core/models/errors.model.ts
Normal file
3
client/src/app/core/models/errors.model.ts
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
export interface Errors {
|
||||||
|
errors: { [key: string]: string };
|
||||||
|
}
|
||||||
7
client/src/app/core/models/user.model.ts
Normal file
7
client/src/app/core/models/user.model.ts
Normal file
|
|
@ -0,0 +1,7 @@
|
||||||
|
export interface User {
|
||||||
|
email: string;
|
||||||
|
token: string;
|
||||||
|
username: string;
|
||||||
|
bio: string;
|
||||||
|
image: string;
|
||||||
|
}
|
||||||
16
client/src/app/core/services/jwt.service.ts
Normal file
16
client/src/app/core/services/jwt.service.ts
Normal file
|
|
@ -0,0 +1,16 @@
|
||||||
|
import { Injectable } from '@angular/core';
|
||||||
|
|
||||||
|
@Injectable({ providedIn: 'root' })
|
||||||
|
export class JwtService {
|
||||||
|
getToken(): string | null {
|
||||||
|
return window.localStorage['jwtToken'];
|
||||||
|
}
|
||||||
|
|
||||||
|
saveToken(token: string): void {
|
||||||
|
window.localStorage['jwtToken'] = token;
|
||||||
|
}
|
||||||
|
|
||||||
|
destroyToken(): void {
|
||||||
|
window.localStorage.removeItem('jwtToken');
|
||||||
|
}
|
||||||
|
}
|
||||||
81
client/src/app/core/services/user.service.ts
Normal file
81
client/src/app/core/services/user.service.ts
Normal file
|
|
@ -0,0 +1,81 @@
|
||||||
|
import { Injectable } from '@angular/core';
|
||||||
|
import { HttpClient } from '@angular/common/http';
|
||||||
|
import {
|
||||||
|
BehaviorSubject,
|
||||||
|
Observable,
|
||||||
|
distinctUntilChanged,
|
||||||
|
map,
|
||||||
|
tap,
|
||||||
|
} from 'rxjs';
|
||||||
|
import { User } from '../models/user.model';
|
||||||
|
import { Router } from '@angular/router';
|
||||||
|
import { JwtService } from './jwt.service';
|
||||||
|
|
||||||
|
@Injectable({ providedIn: 'root' })
|
||||||
|
export class UserService {
|
||||||
|
private currnetUserSubject = new BehaviorSubject<User | null>(null);
|
||||||
|
public currentUser = this.currnetUserSubject
|
||||||
|
.asObservable()
|
||||||
|
.pipe(distinctUntilChanged());
|
||||||
|
|
||||||
|
public isAuthenticated = this.currentUser.pipe(
|
||||||
|
map((user) => {
|
||||||
|
return user !== null;
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private readonly http: HttpClient,
|
||||||
|
private readonly router: Router,
|
||||||
|
private readonly jwtService: JwtService
|
||||||
|
) {}
|
||||||
|
|
||||||
|
login(credentials: {
|
||||||
|
email: string;
|
||||||
|
password: string;
|
||||||
|
}): Observable<{ user: User }> {
|
||||||
|
return this.http
|
||||||
|
.post<{ user: User }>('/api/users/login', credentials)
|
||||||
|
.pipe(tap(({ user }) => this.setAuth(user)));
|
||||||
|
}
|
||||||
|
|
||||||
|
register(credentials: {
|
||||||
|
username: string;
|
||||||
|
email: string;
|
||||||
|
password: string;
|
||||||
|
}): Observable<{ user: User }> {
|
||||||
|
return this.http
|
||||||
|
.post<{ user: User }>('/api/users', { user: credentials })
|
||||||
|
.pipe(tap(({ user }) => this.setAuth(user)));
|
||||||
|
}
|
||||||
|
|
||||||
|
logout(): void {
|
||||||
|
this.purgeAuth();
|
||||||
|
void this.router.navigate(['/login']);
|
||||||
|
}
|
||||||
|
|
||||||
|
getCurrentUser(): Observable<{ user: User }> {
|
||||||
|
return this.http.get<{ user: User }>('/api/user').pipe(
|
||||||
|
tap({
|
||||||
|
next: ({ user }) => this.setAuth(user),
|
||||||
|
error: () => this.purgeAuth(),
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
update(user: Partial<User>): Observable<{ user: User }> {
|
||||||
|
return this.http
|
||||||
|
.put<{ user: User }>('/api/user', { user })
|
||||||
|
.pipe(tap(({ user }) => this.currnetUserSubject.next(user)));
|
||||||
|
}
|
||||||
|
|
||||||
|
setAuth(user: User): void {
|
||||||
|
this.jwtService.saveToken(user.token);
|
||||||
|
void this.currnetUserSubject.next(user);
|
||||||
|
}
|
||||||
|
|
||||||
|
purgeAuth(): void {
|
||||||
|
this.jwtService.destroyToken();
|
||||||
|
this.currnetUserSubject.next(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,4 @@
|
||||||
|
<p>dashboard works!</p>
|
||||||
|
<button (click)="logout()" class="bg-blue-300 rounded-lg min-w-fit p-10">
|
||||||
|
Logout
|
||||||
|
</button>
|
||||||
21
client/src/app/features/dashboard/dashboard.component.ts
Normal file
21
client/src/app/features/dashboard/dashboard.component.ts
Normal file
|
|
@ -0,0 +1,21 @@
|
||||||
|
import { Component } from '@angular/core';
|
||||||
|
import { CommonModule } from '@angular/common';
|
||||||
|
import { UserService } from 'src/app/core/services/user.service';
|
||||||
|
import { SocialAuthService } from '@abacritt/angularx-social-login';
|
||||||
|
import { Router } from '@angular/router';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-dashboard',
|
||||||
|
standalone: true,
|
||||||
|
templateUrl: './dashboard.component.html',
|
||||||
|
styleUrls: ['./dashboard.component.css'],
|
||||||
|
})
|
||||||
|
export class DashboardComponent {
|
||||||
|
constructor(private _userService: UserService) {}
|
||||||
|
|
||||||
|
logout() {
|
||||||
|
console.log('logout');
|
||||||
|
this._userService.logout();
|
||||||
|
// this._authService.signOut();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,84 +0,0 @@
|
||||||
<main class="flex flex-col justify-around">
|
|
||||||
<div class="flex min-h-full flex-col justify-center px-6 py-12 lg:px-8">
|
|
||||||
<div class="sm:mx-auto sm:w-full sm:max-w-sm">
|
|
||||||
<img
|
|
||||||
class="mx-auto h-10 w-auto"
|
|
||||||
src="https://tailwindui.com/img/logos/mark.svg?color=indigo&shade=600"
|
|
||||||
alt="Your Company"
|
|
||||||
/>
|
|
||||||
<h2
|
|
||||||
class="mt-10 text-center text-2xl font-bold leading-9 tracking-tight text-gray-900"
|
|
||||||
>
|
|
||||||
Sign in to your account
|
|
||||||
</h2>
|
|
||||||
<div class="mt-10 sm:mx-auto sm:w-full sm:max-w-sm">
|
|
||||||
<asl-google-signin-button
|
|
||||||
type="standard"
|
|
||||||
shape="pill"
|
|
||||||
text="signin_with"
|
|
||||||
size="large"
|
|
||||||
logo_alignment="center"
|
|
||||||
theme="outline"
|
|
||||||
width="400"
|
|
||||||
></asl-google-signin-button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="mt-10 sm:mx-auto sm:w-full sm:max-w-sm">
|
|
||||||
<form class="space-y-6" action="#" method="POST">
|
|
||||||
<div>
|
|
||||||
<label
|
|
||||||
for="email"
|
|
||||||
class="block text-sm font-medium leading-6 text-gray-900"
|
|
||||||
>Email address</label
|
|
||||||
>
|
|
||||||
<div class="mt-2">
|
|
||||||
<input
|
|
||||||
id="email"
|
|
||||||
name="email"
|
|
||||||
type="email"
|
|
||||||
autocomplete="email"
|
|
||||||
required
|
|
||||||
class="block w-full rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<div class="flex items-center justify-between">
|
|
||||||
<label
|
|
||||||
for="password"
|
|
||||||
class="block text-sm font-medium leading-6 text-gray-900"
|
|
||||||
>Password</label
|
|
||||||
>
|
|
||||||
<div class="text-sm">
|
|
||||||
<a
|
|
||||||
href="#"
|
|
||||||
class="font-semibold text-indigo-600 hover:text-indigo-500"
|
|
||||||
>Forgot password?</a
|
|
||||||
>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="mt-2">
|
|
||||||
<input
|
|
||||||
id="password"
|
|
||||||
name="password"
|
|
||||||
type="password"
|
|
||||||
autocomplete="current-password"
|
|
||||||
required
|
|
||||||
class="block w-full rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<button
|
|
||||||
type="submit"
|
|
||||||
class="flex w-full justify-center rounded-md bg-indigo-600 px-3 py-1.5 text-sm font-semibold leading-6 text-white shadow-sm hover:bg-indigo-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600"
|
|
||||||
>
|
|
||||||
Sign in
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</main>
|
|
||||||
|
|
@ -1,21 +0,0 @@
|
||||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
|
||||||
|
|
||||||
import { HomeComponent } from './home.component';
|
|
||||||
|
|
||||||
describe('HomeComponent', () => {
|
|
||||||
let component: HomeComponent;
|
|
||||||
let fixture: ComponentFixture<HomeComponent>;
|
|
||||||
|
|
||||||
beforeEach(() => {
|
|
||||||
TestBed.configureTestingModule({
|
|
||||||
declarations: [HomeComponent]
|
|
||||||
});
|
|
||||||
fixture = TestBed.createComponent(HomeComponent);
|
|
||||||
component = fixture.componentInstance;
|
|
||||||
fixture.detectChanges();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should create', () => {
|
|
||||||
expect(component).toBeTruthy();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
@ -1,23 +0,0 @@
|
||||||
import {
|
|
||||||
GoogleSigninButtonModule,
|
|
||||||
SocialAuthService,
|
|
||||||
} from '@abacritt/angularx-social-login';
|
|
||||||
import { Component, OnInit } from '@angular/core';
|
|
||||||
import { AppModule } from 'src/app/app.module';
|
|
||||||
|
|
||||||
@Component({
|
|
||||||
selector: 'app-home',
|
|
||||||
templateUrl: './home.component.html',
|
|
||||||
styleUrls: ['./home.component.css'],
|
|
||||||
imports: [GoogleSigninButtonModule],
|
|
||||||
standalone: true,
|
|
||||||
})
|
|
||||||
export class HomeComponent implements OnInit {
|
|
||||||
constructor(private _authService: SocialAuthService) {}
|
|
||||||
|
|
||||||
ngOnInit(): void {
|
|
||||||
this._authService.authState.subscribe((user) => {
|
|
||||||
console.log(user);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue