- - -
- - - Rocket Ship - - - - - - - - - - {{ title }} app is running! - - - Rocket Ship Smoke - - - -
- - -

Resources

-

Here are some links to help you get started:

- -
- - - Learn Angular - - - - - CLI Documentation - - - - - - Angular Material - - - - - - Angular Blog - - - - - - Angular DevTools - - - +
+ + +
+
- - -

Next Steps

-

What do you want to do next with your app?

- - - -
- - - - - - - - - - - -
- - -
-
ng generate component xyz
-
ng add @angular/material
-
ng add @angular/pwa
-
ng add _____
-
ng test
-
ng build
-
- - - - - - - - - Gray Clouds Background - - -
- - - - - - - - - - diff --git a/src/app/app.component.ts b/src/app/app.component.ts index e1f3d79..242adcc 100644 --- a/src/app/app.component.ts +++ b/src/app/app.component.ts @@ -1,4 +1,8 @@ import { Component } from '@angular/core'; +import { Subscription } from 'rxjs'; +import { StorageService } from './_services/storage.service'; +import { AuthService } from './_services/auth.service'; +import { EventBusService } from './_shared/event-bus.service'; @Component({ selector: 'app-root', @@ -6,5 +10,49 @@ import { Component } from '@angular/core'; styleUrls: ['./app.component.css'] }) export class AppComponent { - title = 'angular-16-jwt-auth'; + private roles: string[] = []; + isLoggedIn = false; + showAdminBoard = false; + showModeratorBoard = false; + username?: string; + + eventBusSub?: Subscription; + + constructor( + private storageService: StorageService, + private authService: AuthService, + private eventBusService: EventBusService + ) {} + + ngOnInit(): void { + this.isLoggedIn = this.storageService.isLoggedIn(); + + if (this.isLoggedIn) { + const user = this.storageService.getUser(); + this.roles = user.roles; + + this.showAdminBoard = this.roles.includes('ROLE_ADMIN'); + this.showModeratorBoard = this.roles.includes('ROLE_MODERATOR'); + + this.username = user.username; + } + + this.eventBusSub = this.eventBusService.on('logout', () => { + this.logout(); + }); + } + + logout(): void { + this.authService.logout().subscribe({ + next: res => { + console.log(res); + this.storageService.clean(); + + window.location.reload(); + }, + error: err => { + console.log(err); + } + }); + } } diff --git a/src/app/app.module.ts b/src/app/app.module.ts index b1c6c96..782bfe6 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -1,18 +1,38 @@ import { NgModule } from '@angular/core'; import { BrowserModule } from '@angular/platform-browser'; +import { FormsModule } from '@angular/forms'; +import { HttpClientModule } from '@angular/common/http'; import { AppRoutingModule } from './app-routing.module'; import { AppComponent } from './app.component'; +import { LoginComponent } from './login/login.component'; +import { RegisterComponent } from './register/register.component'; +import { HomeComponent } from './home/home.component'; +import { ProfileComponent } from './profile/profile.component'; +import { BoardAdminComponent } from './board-admin/board-admin.component'; +import { BoardModeratorComponent } from './board-moderator/board-moderator.component'; +import { BoardUserComponent } from './board-user/board-user.component'; + +import { httpInterceptorProviders } from './_helpers/http.interceptor'; @NgModule({ declarations: [ - AppComponent + AppComponent, + LoginComponent, + RegisterComponent, + HomeComponent, + ProfileComponent, + BoardAdminComponent, + BoardModeratorComponent, + BoardUserComponent ], imports: [ BrowserModule, - AppRoutingModule + AppRoutingModule, + FormsModule, + HttpClientModule ], - providers: [], + providers: [httpInterceptorProviders], bootstrap: [AppComponent] }) export class AppModule { } diff --git a/src/app/board-admin/board-admin.component.css b/src/app/board-admin/board-admin.component.css new file mode 100644 index 0000000..e69de29 diff --git a/src/app/board-admin/board-admin.component.html b/src/app/board-admin/board-admin.component.html new file mode 100644 index 0000000..cbadc7a --- /dev/null +++ b/src/app/board-admin/board-admin.component.html @@ -0,0 +1,5 @@ +
+
+

{{ content }}

+
+
diff --git a/src/app/board-admin/board-admin.component.spec.ts b/src/app/board-admin/board-admin.component.spec.ts new file mode 100644 index 0000000..b91a08d --- /dev/null +++ b/src/app/board-admin/board-admin.component.spec.ts @@ -0,0 +1,21 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { BoardAdminComponent } from './board-admin.component'; + +describe('BoardAdminComponent', () => { + let component: BoardAdminComponent; + let fixture: ComponentFixture; + + beforeEach(() => { + TestBed.configureTestingModule({ + declarations: [BoardAdminComponent] + }); + fixture = TestBed.createComponent(BoardAdminComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/board-admin/board-admin.component.ts b/src/app/board-admin/board-admin.component.ts new file mode 100644 index 0000000..5b70d2d --- /dev/null +++ b/src/app/board-admin/board-admin.component.ts @@ -0,0 +1,33 @@ +import { Component, OnInit } from '@angular/core'; +import { UserService } from '../_services/user.service'; + +@Component({ + selector: 'app-board-admin', + templateUrl: './board-admin.component.html', + styleUrls: ['./board-admin.component.css'] +}) +export class BoardAdminComponent implements OnInit { + content?: string; + + constructor(private userService: UserService) { } + + ngOnInit(): void { + this.userService.getAdminBoard().subscribe({ + next: data => { + this.content = data; + }, + error: err => { + if (err.error) { + try { + const res = JSON.parse(err.error); + this.content = res.message; + } catch { + this.content = `Error with status: ${err.status} - ${err.statusText}`; + } + } else { + this.content = `Error with status: ${err.status}`; + } + } + }); + } +} diff --git a/src/app/board-moderator/board-moderator.component.css b/src/app/board-moderator/board-moderator.component.css new file mode 100644 index 0000000..e69de29 diff --git a/src/app/board-moderator/board-moderator.component.html b/src/app/board-moderator/board-moderator.component.html new file mode 100644 index 0000000..cbadc7a --- /dev/null +++ b/src/app/board-moderator/board-moderator.component.html @@ -0,0 +1,5 @@ +
+
+

{{ content }}

+
+
diff --git a/src/app/board-moderator/board-moderator.component.spec.ts b/src/app/board-moderator/board-moderator.component.spec.ts new file mode 100644 index 0000000..2343025 --- /dev/null +++ b/src/app/board-moderator/board-moderator.component.spec.ts @@ -0,0 +1,21 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { BoardModeratorComponent } from './board-moderator.component'; + +describe('BoardModeratorComponent', () => { + let component: BoardModeratorComponent; + let fixture: ComponentFixture; + + beforeEach(() => { + TestBed.configureTestingModule({ + declarations: [BoardModeratorComponent] + }); + fixture = TestBed.createComponent(BoardModeratorComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/board-moderator/board-moderator.component.ts b/src/app/board-moderator/board-moderator.component.ts new file mode 100644 index 0000000..c04c397 --- /dev/null +++ b/src/app/board-moderator/board-moderator.component.ts @@ -0,0 +1,33 @@ +import { Component, OnInit } from '@angular/core'; +import { UserService } from '../_services/user.service'; + +@Component({ + selector: 'app-board-moderator', + templateUrl: './board-moderator.component.html', + styleUrls: ['./board-moderator.component.css'] +}) +export class BoardModeratorComponent implements OnInit { + content?: string; + + constructor(private userService: UserService) { } + + ngOnInit(): void { + this.userService.getModeratorBoard().subscribe({ + next: data => { + this.content = data; + }, + error: err => { + if (err.error) { + try { + const res = JSON.parse(err.error); + this.content = res.message; + } catch { + this.content = `Error with status: ${err.status} - ${err.statusText}`; + } + } else { + this.content = `Error with status: ${err.status}`; + } + } + }); + } +} diff --git a/src/app/board-user/board-user.component.css b/src/app/board-user/board-user.component.css new file mode 100644 index 0000000..e69de29 diff --git a/src/app/board-user/board-user.component.html b/src/app/board-user/board-user.component.html new file mode 100644 index 0000000..cbadc7a --- /dev/null +++ b/src/app/board-user/board-user.component.html @@ -0,0 +1,5 @@ +
+
+

{{ content }}

+
+
diff --git a/src/app/board-user/board-user.component.spec.ts b/src/app/board-user/board-user.component.spec.ts new file mode 100644 index 0000000..33a199c --- /dev/null +++ b/src/app/board-user/board-user.component.spec.ts @@ -0,0 +1,21 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { BoardUserComponent } from './board-user.component'; + +describe('BoardUserComponent', () => { + let component: BoardUserComponent; + let fixture: ComponentFixture; + + beforeEach(() => { + TestBed.configureTestingModule({ + declarations: [BoardUserComponent] + }); + fixture = TestBed.createComponent(BoardUserComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/board-user/board-user.component.ts b/src/app/board-user/board-user.component.ts new file mode 100644 index 0000000..6e17378 --- /dev/null +++ b/src/app/board-user/board-user.component.ts @@ -0,0 +1,33 @@ +import { Component, OnInit } from '@angular/core'; +import { UserService } from '../_services/user.service'; + +@Component({ + selector: 'app-board-user', + templateUrl: './board-user.component.html', + styleUrls: ['./board-user.component.css'] +}) +export class BoardUserComponent implements OnInit { + content?: string; + + constructor(private userService: UserService) { } + + ngOnInit(): void { + this.userService.getUserBoard().subscribe({ + next: data => { + this.content = data; + }, + error: err => { + if (err.error) { + try { + const res = JSON.parse(err.error); + this.content = res.message; + } catch { + this.content = `Error with status: ${err.status} - ${err.statusText}`; + } + } else { + this.content = `Error with status: ${err.status}`; + } + } + }); + } +} diff --git a/src/app/home/home.component.css b/src/app/home/home.component.css new file mode 100644 index 0000000..e69de29 diff --git a/src/app/home/home.component.html b/src/app/home/home.component.html new file mode 100644 index 0000000..cbadc7a --- /dev/null +++ b/src/app/home/home.component.html @@ -0,0 +1,5 @@ +
+
+

{{ content }}

+
+
diff --git a/src/app/home/home.component.spec.ts b/src/app/home/home.component.spec.ts new file mode 100644 index 0000000..ba1b4a3 --- /dev/null +++ b/src/app/home/home.component.spec.ts @@ -0,0 +1,21 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { HomeComponent } from './home.component'; + +describe('HomeComponent', () => { + let component: HomeComponent; + let fixture: ComponentFixture; + + beforeEach(() => { + TestBed.configureTestingModule({ + declarations: [HomeComponent] + }); + fixture = TestBed.createComponent(HomeComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/home/home.component.ts b/src/app/home/home.component.ts new file mode 100644 index 0000000..02f10a3 --- /dev/null +++ b/src/app/home/home.component.ts @@ -0,0 +1,33 @@ +import { Component, OnInit } from '@angular/core'; +import { UserService } from '../_services/user.service'; + +@Component({ + selector: 'app-home', + templateUrl: './home.component.html', + styleUrls: ['./home.component.css'] +}) +export class HomeComponent implements OnInit { + content?: string; + + constructor(private userService: UserService) { } + + ngOnInit(): void { + this.userService.getPublicContent().subscribe({ + next: data => { + this.content = data; + }, + error: err => { + if (err.error) { + try { + const res = JSON.parse(err.error); + this.content = res.message; + } catch { + this.content = `Error with status: ${err.status} - ${err.statusText}`; + } + } else { + this.content = `Error with status: ${err.status}`; + } + } + }); + } +} diff --git a/src/app/login/login.component.css b/src/app/login/login.component.css new file mode 100644 index 0000000..924cf00 --- /dev/null +++ b/src/app/login/login.component.css @@ -0,0 +1,32 @@ +label { + display: block; + margin-top: 10px; +} + +.card-container.card { + max-width: 400px !important; + padding: 40px 40px; +} + +.card { + background-color: #f7f7f7; + padding: 20px 25px 30px; + margin: 0 auto 25px; + margin-top: 50px; + -moz-border-radius: 2px; + -webkit-border-radius: 2px; + border-radius: 2px; + -moz-box-shadow: 0px 2px 2px rgba(0, 0, 0, 0.3); + -webkit-box-shadow: 0px 2px 2px rgba(0, 0, 0, 0.3); + box-shadow: 0px 2px 2px rgba(0, 0, 0, 0.3); +} + +.profile-img-card { + width: 96px; + height: 96px; + margin: 0 auto 10px; + display: block; + -moz-border-radius: 50%; + -webkit-border-radius: 50%; + border-radius: 50%; +} \ No newline at end of file diff --git a/src/app/login/login.component.html b/src/app/login/login.component.html new file mode 100644 index 0000000..f79d7ad --- /dev/null +++ b/src/app/login/login.component.html @@ -0,0 +1,65 @@ +
+
+ +
+
+ + +
+ Username is required! +
+
+
+ + +
+
Password is required
+
+ Password must be at least 6 characters +
+
+
+
+ +
+
+ +
+
+ +
+ Logged in as {{ roles }}. +
+
+
\ No newline at end of file diff --git a/src/app/login/login.component.spec.ts b/src/app/login/login.component.spec.ts new file mode 100644 index 0000000..360f9f2 --- /dev/null +++ b/src/app/login/login.component.spec.ts @@ -0,0 +1,21 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { LoginComponent } from './login.component'; + +describe('LoginComponent', () => { + let component: LoginComponent; + let fixture: ComponentFixture; + + beforeEach(() => { + TestBed.configureTestingModule({ + declarations: [LoginComponent] + }); + fixture = TestBed.createComponent(LoginComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/login/login.component.ts b/src/app/login/login.component.ts new file mode 100644 index 0000000..97b4050 --- /dev/null +++ b/src/app/login/login.component.ts @@ -0,0 +1,51 @@ +import { Component, OnInit } from '@angular/core'; +import { AuthService } from '../_services/auth.service'; +import { StorageService } from '../_services/storage.service'; + +@Component({ + selector: 'app-login', + templateUrl: './login.component.html', + styleUrls: ['./login.component.css'] +}) +export class LoginComponent implements OnInit { + form: any = { + username: null, + password: null + }; + isLoggedIn = false; + isLoginFailed = false; + errorMessage = ''; + roles: string[] = []; + + constructor(private authService: AuthService, private storageService: StorageService) { } + + ngOnInit(): void { + if (this.storageService.isLoggedIn()) { + this.isLoggedIn = true; + this.roles = this.storageService.getUser().roles; + } + } + + onSubmit(): void { + const { username, password } = this.form; + + this.authService.login(username, password).subscribe({ + next: data => { + this.storageService.saveUser(data); + + this.isLoginFailed = false; + this.isLoggedIn = true; + this.roles = this.storageService.getUser().roles; + this.reloadPage(); + }, + error: err => { + this.errorMessage = err.error.message; + this.isLoginFailed = true; + } + }); + } + + reloadPage(): void { + window.location.reload(); + } +} diff --git a/src/app/profile/profile.component.css b/src/app/profile/profile.component.css new file mode 100644 index 0000000..e69de29 diff --git a/src/app/profile/profile.component.html b/src/app/profile/profile.component.html new file mode 100644 index 0000000..bf82165 --- /dev/null +++ b/src/app/profile/profile.component.html @@ -0,0 +1,21 @@ +
+
+

+ {{ currentUser.username }} Profile +

+
+

+ Email: + {{ currentUser.email }} +

+ Roles: +
    +
  • + {{ role }} +
  • +
+
+ + + Please login. + \ No newline at end of file diff --git a/src/app/profile/profile.component.spec.ts b/src/app/profile/profile.component.spec.ts new file mode 100644 index 0000000..042e899 --- /dev/null +++ b/src/app/profile/profile.component.spec.ts @@ -0,0 +1,21 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { ProfileComponent } from './profile.component'; + +describe('ProfileComponent', () => { + let component: ProfileComponent; + let fixture: ComponentFixture; + + beforeEach(() => { + TestBed.configureTestingModule({ + declarations: [ProfileComponent] + }); + fixture = TestBed.createComponent(ProfileComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/profile/profile.component.ts b/src/app/profile/profile.component.ts new file mode 100644 index 0000000..b574b93 --- /dev/null +++ b/src/app/profile/profile.component.ts @@ -0,0 +1,17 @@ +import { Component, OnInit } from '@angular/core'; +import { StorageService } from '../_services/storage.service'; + +@Component({ + selector: 'app-profile', + templateUrl: './profile.component.html', + styleUrls: ['./profile.component.css'] +}) +export class ProfileComponent implements OnInit { + currentUser: any; + + constructor(private storageService: StorageService) { } + + ngOnInit(): void { + this.currentUser = this.storageService.getUser(); + } +} diff --git a/src/app/register/register.component.css b/src/app/register/register.component.css new file mode 100644 index 0000000..924cf00 --- /dev/null +++ b/src/app/register/register.component.css @@ -0,0 +1,32 @@ +label { + display: block; + margin-top: 10px; +} + +.card-container.card { + max-width: 400px !important; + padding: 40px 40px; +} + +.card { + background-color: #f7f7f7; + padding: 20px 25px 30px; + margin: 0 auto 25px; + margin-top: 50px; + -moz-border-radius: 2px; + -webkit-border-radius: 2px; + border-radius: 2px; + -moz-box-shadow: 0px 2px 2px rgba(0, 0, 0, 0.3); + -webkit-box-shadow: 0px 2px 2px rgba(0, 0, 0, 0.3); + box-shadow: 0px 2px 2px rgba(0, 0, 0, 0.3); +} + +.profile-img-card { + width: 96px; + height: 96px; + margin: 0 auto 10px; + display: block; + -moz-border-radius: 50%; + -webkit-border-radius: 50%; + border-radius: 50%; +} \ No newline at end of file diff --git a/src/app/register/register.component.html b/src/app/register/register.component.html new file mode 100644 index 0000000..05bac8a --- /dev/null +++ b/src/app/register/register.component.html @@ -0,0 +1,89 @@ +
+
+ +
+
+ + +
+
Username is required
+
+ Username must be at least 3 characters +
+
+ Username must be at most 20 characters +
+
+
+
+ + +
+
Email is required
+
+ Email must be a valid email address +
+
+
+
+ + +
+
Password is required
+
+ Password must be at least 6 characters +
+
+
+
+ +
+ +
+ Signup failed!
{{ errorMessage }} +
+
+ +
+ Your registration is successful! +
+
+
\ No newline at end of file diff --git a/src/app/register/register.component.spec.ts b/src/app/register/register.component.spec.ts new file mode 100644 index 0000000..9c4c73c --- /dev/null +++ b/src/app/register/register.component.spec.ts @@ -0,0 +1,21 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { RegisterComponent } from './register.component'; + +describe('RegisterComponent', () => { + let component: RegisterComponent; + let fixture: ComponentFixture; + + beforeEach(() => { + TestBed.configureTestingModule({ + declarations: [RegisterComponent] + }); + fixture = TestBed.createComponent(RegisterComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/register/register.component.ts b/src/app/register/register.component.ts new file mode 100644 index 0000000..0b2d193 --- /dev/null +++ b/src/app/register/register.component.ts @@ -0,0 +1,36 @@ +import { Component } from '@angular/core'; +import { AuthService } from '../_services/auth.service'; + +@Component({ + selector: 'app-register', + templateUrl: './register.component.html', + styleUrls: ['./register.component.css'] +}) +export class RegisterComponent { + form: any = { + username: null, + email: null, + password: null + }; + isSuccessful = false; + isSignUpFailed = false; + errorMessage = ''; + + constructor(private authService: AuthService) { } + + onSubmit(): void { + const { username, email, password } = this.form; + + this.authService.register(username, email, password).subscribe({ + next: data => { + console.log(data); + this.isSuccessful = true; + this.isSignUpFailed = false; + }, + error: err => { + this.errorMessage = err.error.message; + this.isSignUpFailed = true; + } + }); + } +} diff --git a/src/styles.css b/src/styles.css index 90d4ee0..a97f91e 100644 --- a/src/styles.css +++ b/src/styles.css @@ -1 +1,2 @@ /* You can add global styles to this file, and also import other style files */ +@import "~bootstrap/dist/css/bootstrap.css"; \ No newline at end of file From 5346d380356744c253032a64231bb0e96e953ffd Mon Sep 17 00:00:00 2001 From: GaryDev <112358055+garydev10@users.noreply.github.com> Date: Sun, 14 Jul 2024 16:59:29 +0100 Subject: [PATCH 2/2] sync file with upstream repo --- src/app/register/register.component.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/register/register.component.ts b/src/app/register/register.component.ts index 2cd4997..0b2d193 100644 --- a/src/app/register/register.component.ts +++ b/src/app/register/register.component.ts @@ -1,4 +1,4 @@ -import { Component, OnInit } from '@angular/core'; +import { Component } from '@angular/core'; import { AuthService } from '../_services/auth.service'; @Component({