add angular material project
This commit is contained in:
@@ -0,0 +1,16 @@
|
||||
import { TestBed } from '@angular/core/testing';
|
||||
|
||||
import { EventBusService } from './event-bus.service';
|
||||
|
||||
describe('EventBusService', () => {
|
||||
let service: EventBusService;
|
||||
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({});
|
||||
service = TestBed.inject(EventBusService);
|
||||
});
|
||||
|
||||
it('should be created', () => {
|
||||
expect(service).toBeTruthy();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,21 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { Subject, Subscription } from 'rxjs';
|
||||
import { filter, map } from 'rxjs/operators';
|
||||
import { EventData } from './event.class';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root',
|
||||
})
|
||||
export class EventBusService {
|
||||
private subject$ = new Subject<EventData>();
|
||||
|
||||
emit(event: EventData) {
|
||||
this.subject$.next(event);
|
||||
}
|
||||
|
||||
on(eventName: string, action: any): Subscription {
|
||||
return this.subject$.pipe(
|
||||
filter((e: EventData) => e.name === eventName),
|
||||
map((e: EventData) => e["value"])).subscribe(action);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
export class EventData {
|
||||
name: string;
|
||||
value: any;
|
||||
|
||||
constructor(name: string, value: any) {
|
||||
this.name = name;
|
||||
this.value = value;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
import { RouterModule, Routes } from '@angular/router';
|
||||
import { TutorialsListComponent } from './components/tutorials-list/tutorials-list.component';
|
||||
import { TutorialDetailsComponent } from './components/tutorial-details/tutorial-details.component';
|
||||
import { AddTutorialComponent } from './components/add-tutorial/add-tutorial.component';
|
||||
import { BoardAdminComponent } from './components/board-admin/board-admin.component';
|
||||
import { BoardModeratorComponent } from './components/board-moderator/board-moderator.component';
|
||||
import { BoardUserComponent } from './components/board-user/board-user.component';
|
||||
import { ProfileComponent } from './components/profile/profile.component';
|
||||
import { RegisterComponent } from './components/register/register.component';
|
||||
import { LoginComponent } from './components/login/login.component';
|
||||
import { HomeComponent } from './components/home/home.component';
|
||||
|
||||
const routes: Routes = [
|
||||
{ path: 'home', component: HomeComponent },
|
||||
{ path: 'login', component: LoginComponent },
|
||||
{ path: 'register', component: RegisterComponent },
|
||||
{ path: 'profile', component: ProfileComponent },
|
||||
{ path: 'user', component: BoardUserComponent },
|
||||
{ path: 'mod', component: BoardModeratorComponent },
|
||||
{ path: 'admin', component: BoardAdminComponent },
|
||||
{ path: '', redirectTo: 'home', pathMatch: 'full' },
|
||||
{ path: 'tutorials', component: TutorialsListComponent },
|
||||
{ path: 'tutorials/:id', component: TutorialDetailsComponent },
|
||||
{ path: 'add', component: AddTutorialComponent }
|
||||
];
|
||||
|
||||
@NgModule({
|
||||
imports: [RouterModule.forRoot(routes, {useHash: true})],
|
||||
exports: [RouterModule]
|
||||
})
|
||||
export class AppRoutingModule { }
|
||||
@@ -0,0 +1,79 @@
|
||||
<div>
|
||||
<nav class="navbar navbar-expand navbar-dark bg-dark">
|
||||
<a href="#" class="navbar-brand">Spring-AngularTS</a>
|
||||
|
||||
<ul class="navbar-nav mr-auto" routerLinkActive="active">
|
||||
|
||||
<li class="nav-item">
|
||||
<a routerLink="tutorials" class="nav-link">Tutorials</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a routerLink="add" class="nav-link">Add</a>
|
||||
</li>
|
||||
|
||||
<li class="nav-item" *ngIf="showAdminBoard">
|
||||
<a href="/admin" class="nav-link" routerLink="admin">Admin Board</a>
|
||||
</li>
|
||||
<li class="nav-item" *ngIf="showModeratorBoard">
|
||||
<a href="/mod" class="nav-link" routerLink="mod">Moderator Board</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a href="/user" class="nav-link" *ngIf="isLoggedIn" routerLink="user">User</a>
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
|
||||
<ul class="navbar-nav ml-auto" *ngIf="!isLoggedIn">
|
||||
<li class="nav-item">
|
||||
<a href="/register" class="nav-link" routerLink="register">Sign Up</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a href="/login" class="nav-link" routerLink="login">Login</a>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<ul class="navbar-nav ml-auto" *ngIf="isLoggedIn">
|
||||
<li class="nav-item">
|
||||
<a href="/profile" class="nav-link" routerLink="profile">{{ username }}</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a href class="nav-link" (click)="logout()">LogOut</a>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
</nav>
|
||||
<mat-toolbar>
|
||||
|
||||
<button mat-raised-button routerLink="/" href="#">
|
||||
My App
|
||||
</button>
|
||||
<span>My App
|
||||
</span>
|
||||
<span class="example-spacer"></span>
|
||||
<button mat-raised-button *ngIf="!isLoggedIn">
|
||||
<mat-icon>app_registration</mat-icon>
|
||||
Register
|
||||
</button>
|
||||
|
||||
<button mat-raised-button (click)="openDialog('100ms', '5ms')" *ngIf="!isLoggedIn">
|
||||
<mat-icon>login</mat-icon>
|
||||
Login
|
||||
</button>
|
||||
|
||||
<button mat-raised-button *ngIf="isLoggedIn">
|
||||
<mat-icon>account_circle</mat-icon>
|
||||
{{ username }}
|
||||
</button>
|
||||
|
||||
<button mat-raised-button (click)="logout()"*ngIf="isLoggedIn">
|
||||
<mat-icon>logout</mat-icon>
|
||||
Logout
|
||||
</button>
|
||||
|
||||
</mat-toolbar>
|
||||
|
||||
|
||||
<div class="container mt-3">
|
||||
<router-outlet></router-outlet>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,6 @@
|
||||
//#header_panel{
|
||||
// background-color: #181d1f;
|
||||
//}
|
||||
.example-spacer {
|
||||
flex: 1 1 auto;
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
import { TestBed } from '@angular/core/testing';
|
||||
import { RouterTestingModule } from '@angular/router/testing';
|
||||
import { AppComponent } from './app.component';
|
||||
|
||||
describe('AppComponent', () => {
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
imports: [
|
||||
RouterTestingModule
|
||||
],
|
||||
declarations: [
|
||||
AppComponent
|
||||
],
|
||||
}).compileComponents();
|
||||
});
|
||||
|
||||
it('should create the app', () => {
|
||||
const fixture = TestBed.createComponent(AppComponent);
|
||||
const app = fixture.componentInstance;
|
||||
expect(app).toBeTruthy();
|
||||
});
|
||||
|
||||
// it(`should have as title 'spring-angular-ui'`, () => {
|
||||
// const fixture = TestBed.createComponent(AppComponent);
|
||||
// const app = fixture.componentInstance;
|
||||
// expect(app.title).toEqual('spring-angular-ui');
|
||||
// });
|
||||
|
||||
it('should render title', () => {
|
||||
const fixture = TestBed.createComponent(AppComponent);
|
||||
fixture.detectChanges();
|
||||
const compiled = fixture.nativeElement;
|
||||
expect(compiled.querySelector('.content span').textContent).toContain('spring-angular-ui app is running!');
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,113 @@
|
||||
import {Component, inject, OnInit} from '@angular/core';
|
||||
import { TokenStorageService } from './services/token-storage.service';
|
||||
import {MatDialog} from "@angular/material/dialog";
|
||||
import {DialogComponent} from "./components/dialog/dialog.component";
|
||||
import {AuthService} from "./services/auth.service";
|
||||
import {Subscription} from "rxjs";
|
||||
import {EventBusService} from "./_shared/event-bus.service";
|
||||
|
||||
@Component({
|
||||
selector: 'app-root',
|
||||
templateUrl: './app.component.html',
|
||||
styleUrls: ['./app.component.scss'],
|
||||
standalone: false
|
||||
})
|
||||
export class AppComponent implements OnInit {
|
||||
readonly dialog = inject(MatDialog);
|
||||
//private authService = new AuthService(provideHttpClient())
|
||||
public dialogData : DialogData = {password: "", username: ""};
|
||||
|
||||
private roles: string[] = [];
|
||||
isLoggedIn = false;
|
||||
showAdminBoard = false;
|
||||
showModeratorBoard = false;
|
||||
username?: string;
|
||||
|
||||
eventBusSub?: Subscription;
|
||||
private errorMessage: any;
|
||||
private isLoginFailed: boolean = false;
|
||||
|
||||
constructor(
|
||||
private storageService: TokenStorageService,
|
||||
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 = true;//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);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
openDialog(enterAnimationDuration: string, exitAnimationDuration: string) {
|
||||
|
||||
let dialogRef = this.dialog.open(DialogComponent, {
|
||||
width: '350px',
|
||||
enterAnimationDuration,
|
||||
exitAnimationDuration,
|
||||
data: {username: this.dialogData.username, password: this.dialogData.password}
|
||||
});
|
||||
|
||||
dialogRef.afterClosed().subscribe(result => {
|
||||
console.log('The dialog was closed');
|
||||
if(result== null){
|
||||
return;
|
||||
}
|
||||
this.dialogData = result;
|
||||
|
||||
this.authService.login(this.dialogData.username, this.dialogData.password).subscribe(
|
||||
data => {
|
||||
this.storageService.saveToken(data.accessToken);
|
||||
this.storageService.saveUser(data);
|
||||
|
||||
this.isLoginFailed = false;
|
||||
this.isLoggedIn = true;
|
||||
let user = this.storageService.getUser();
|
||||
this.roles = user.roles;
|
||||
//this.username = user.username;
|
||||
this.reloadPage();
|
||||
},
|
||||
err => {
|
||||
this.errorMessage = err.error.message;
|
||||
this.isLoginFailed = true;
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
}
|
||||
reloadPage(): void {
|
||||
window.location.reload();
|
||||
}
|
||||
|
||||
}
|
||||
export interface DialogData {
|
||||
username: string;
|
||||
password: string;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
import { BrowserModule } from '@angular/platform-browser';
|
||||
|
||||
import { AppRoutingModule } from './app-routing.module';
|
||||
import {FormsModule, ReactiveFormsModule} from '@angular/forms';
|
||||
|
||||
|
||||
import { AppComponent } from './app.component';
|
||||
import { TutorialsListComponent } from './components/tutorials-list/tutorials-list.component';
|
||||
import { TutorialDetailsComponent } from './components/tutorial-details/tutorial-details.component';
|
||||
import { AddTutorialComponent } from './components/add-tutorial/add-tutorial.component';
|
||||
import { LoginComponent } from './components/login/login.component';
|
||||
import { RegisterComponent } from './components/register/register.component';
|
||||
import { HomeComponent } from './components/home/home.component';
|
||||
import { ProfileComponent } from './components/profile/profile.component';
|
||||
import { BoardAdminComponent } from './components/board-admin/board-admin.component';
|
||||
import { BoardModeratorComponent } from './components/board-moderator/board-moderator.component';
|
||||
import { BoardUserComponent } from './components/board-user/board-user.component';
|
||||
|
||||
import { authInterceptorProviders } from './helpers/auth.interceptor';
|
||||
import {provideHttpClient, withInterceptorsFromDi} from "@angular/common/http";
|
||||
import {provideAnimationsAsync} from "@angular/platform-browser/animations/async";
|
||||
import {MatToolbar} from "@angular/material/toolbar";
|
||||
import {MatAnchor, MatButton} from "@angular/material/button";
|
||||
import {MatIcon} from "@angular/material/icon";
|
||||
import {MatChipGrid, MatChipInput, MatChipListbox, MatChipOption, MatChipRow} from "@angular/material/chips";
|
||||
import {MatAutocomplete, MatAutocompleteTrigger, MatOption} from "@angular/material/autocomplete";
|
||||
import {MatFormField} from "@angular/material/form-field";
|
||||
|
||||
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
AppComponent,
|
||||
TutorialsListComponent,
|
||||
TutorialDetailsComponent,
|
||||
AddTutorialComponent,
|
||||
|
||||
RegisterComponent,
|
||||
HomeComponent,
|
||||
ProfileComponent,
|
||||
BoardAdminComponent,
|
||||
BoardModeratorComponent,
|
||||
BoardUserComponent
|
||||
|
||||
],
|
||||
bootstrap: [AppComponent],
|
||||
imports: [
|
||||
BrowserModule,
|
||||
AppRoutingModule,
|
||||
FormsModule,
|
||||
LoginComponent,
|
||||
MatToolbar,
|
||||
MatButton,
|
||||
MatIcon,
|
||||
MatAnchor,
|
||||
MatChipGrid,
|
||||
MatChipRow,
|
||||
MatChipInput,
|
||||
MatAutocompleteTrigger,
|
||||
MatAutocomplete,
|
||||
MatOption,
|
||||
MatFormField,
|
||||
MatChipListbox,
|
||||
MatChipOption,
|
||||
ReactiveFormsModule
|
||||
],
|
||||
providers: [authInterceptorProviders, provideHttpClient(withInterceptorsFromDi()), provideAnimationsAsync()] })
|
||||
|
||||
|
||||
export class AppModule { }
|
||||
@@ -0,0 +1,35 @@
|
||||
<div>
|
||||
<div class="submit-form">
|
||||
<div *ngIf="!submitted">
|
||||
<div class="form-group">
|
||||
<label for="title">Title</label>
|
||||
<input
|
||||
type="text"
|
||||
class="form-control"
|
||||
id="title"
|
||||
required
|
||||
[(ngModel)]="tutorial.title"
|
||||
name="title"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="description">Description</label>
|
||||
<input
|
||||
class="form-control"
|
||||
id="description"
|
||||
required
|
||||
[(ngModel)]="tutorial.description"
|
||||
name="description"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<button (click)="saveTutorial()" class="btn btn-success">Submit</button>
|
||||
</div>
|
||||
|
||||
<div *ngIf="submitted">
|
||||
<h4>Tutorial was submitted successfully!</h4>
|
||||
<button class="btn btn-success" (click)="newTutorial()">Add</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,4 @@
|
||||
.submit-form {
|
||||
max-width: 400px;
|
||||
margin: auto;
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { AddTutorialComponent } from './add-tutorial.component';
|
||||
|
||||
describe('AddTutorialComponent', () => {
|
||||
let component: AddTutorialComponent;
|
||||
let fixture: ComponentFixture<AddTutorialComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
declarations: [ AddTutorialComponent ]
|
||||
})
|
||||
.compileComponents();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(AddTutorialComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,51 @@
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
import {Tutorial} from '../../models/tutorial.model';
|
||||
import {TutorialService} from '../../services/tutorial.service';
|
||||
|
||||
|
||||
@Component({
|
||||
selector: 'app-add-tutorial',
|
||||
templateUrl: './add-tutorial.component.html',
|
||||
styleUrls: ['./add-tutorial.component.scss'],
|
||||
standalone: false
|
||||
})
|
||||
export class AddTutorialComponent implements OnInit {
|
||||
|
||||
tutorial: Tutorial = {
|
||||
title: '',
|
||||
description: '',
|
||||
published: false
|
||||
};
|
||||
submitted = false;
|
||||
|
||||
constructor(private tutorialService: TutorialService) { }
|
||||
|
||||
ngOnInit(): void {
|
||||
}
|
||||
|
||||
saveTutorial(): void {
|
||||
const data = {
|
||||
title: this.tutorial.title,
|
||||
description: this.tutorial.description
|
||||
};
|
||||
|
||||
this.tutorialService.create(data)
|
||||
.subscribe(
|
||||
response => {
|
||||
console.log(response);
|
||||
this.submitted = true;
|
||||
},
|
||||
error => {
|
||||
console.log(error);
|
||||
});
|
||||
}
|
||||
|
||||
newTutorial(): void {
|
||||
this.submitted = false;
|
||||
this.tutorial = {
|
||||
title: '',
|
||||
description: '',
|
||||
published: false
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
<div class="col-md-6">
|
||||
<h4>Users List</h4>
|
||||
<ul class="list-group">
|
||||
<li
|
||||
class="list-group-item"
|
||||
*ngFor="let row of rowData; let i = index"
|
||||
|
||||
>
|
||||
{{ row.username }}
|
||||
|
||||
{{ row.email}}
|
||||
{{ row.password}}
|
||||
|
||||
<form>
|
||||
<mat-form-field class="example-chip-list">
|
||||
<mat-chip-grid #chipGrid aria-label="Role selection">
|
||||
@for (role of row.roles; track $index) {
|
||||
<mat-chip-row (removed)="remove(role)">
|
||||
{{role.name}}
|
||||
<button matChipRemove [attr.aria-label]="'remove ' + role.name">
|
||||
<mat-icon>cancel</mat-icon>
|
||||
</button>
|
||||
</mat-chip-row>
|
||||
}
|
||||
</mat-chip-grid>
|
||||
<input
|
||||
name="currentFruit"
|
||||
placeholder="Add role..."
|
||||
#fruitInput
|
||||
[(ngModel)]="currentRole"
|
||||
[matChipInputFor]="chipGrid"
|
||||
[matAutocomplete]="auto"
|
||||
[matChipInputSeparatorKeyCodes]="separatorKeysCodes"
|
||||
[formControl]="myControl"
|
||||
(matChipInputTokenEnd)="add($event)"
|
||||
(input)="change($event,filteredRoles(row.roles))"
|
||||
/>
|
||||
<mat-autocomplete [formControl]="ac" autoActiveFirstOption #auto="matAutocomplete" (optionSelected)="selected(row.roles,$event); ">
|
||||
@for (role of filteredRoles(row.roles); track role) {
|
||||
<mat-option [value]="role">{{role.name}}</mat-option>
|
||||
}
|
||||
</mat-autocomplete>
|
||||
|
||||
</mat-form-field>
|
||||
</form>
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
|
||||
</div>
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
.example-chip-list {
|
||||
width: 100%;
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { BoardAdminComponent } from './board-admin.component';
|
||||
|
||||
describe('BoardAdminComponent', () => {
|
||||
let component: BoardAdminComponent;
|
||||
let fixture: ComponentFixture<BoardAdminComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
declarations: [ BoardAdminComponent ]
|
||||
})
|
||||
.compileComponents();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(BoardAdminComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,124 @@
|
||||
import {Component, computed, model, OnInit} from '@angular/core';
|
||||
|
||||
import {User} from "../../models/user.model";
|
||||
import {MatChipInputEvent} from "@angular/material/chips";
|
||||
import {MatAutocompleteSelectedEvent} from "@angular/material/autocomplete";
|
||||
import {COMMA, ENTER} from "@angular/cdk/keycodes";
|
||||
import {AsyncPipe} from "@angular/common";
|
||||
import {FormControl} from "@angular/forms";
|
||||
import {Observable, startWith} from "rxjs";
|
||||
import {map} from "rxjs/operators";
|
||||
import {UserService} from '../../services/user.service';
|
||||
import {RolesService} from '../../services/roles.service';
|
||||
import {Role} from '../../models/role';
|
||||
|
||||
|
||||
|
||||
@Component({
|
||||
selector: 'app-board-admin',
|
||||
templateUrl: './board-admin.component.html',
|
||||
styleUrls: ['./board-admin.component.scss'],
|
||||
standalone: false,
|
||||
|
||||
})
|
||||
export class BoardAdminComponent implements OnInit {
|
||||
private gridApi: any;
|
||||
|
||||
readonly separatorKeysCodes: number[] = [ENTER, COMMA];
|
||||
rowData?: User[];
|
||||
allRoles: any;
|
||||
|
||||
readonly currentRole = model('');
|
||||
|
||||
protected myControl = new FormControl('');
|
||||
protected ac = new FormControl('');
|
||||
|
||||
|
||||
constructor(private userService: UserService, private roleService: RolesService ) {
|
||||
this.getAllRoles();
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
|
||||
//this.myControl.valueChanges.pipe(
|
||||
// startWith(''),
|
||||
// map(value => this._filter(value || '')),
|
||||
//);
|
||||
|
||||
this.userService.getAdminBoard().subscribe(
|
||||
(data : any) => {
|
||||
this.rowData = data;
|
||||
},
|
||||
(err : any)=> {
|
||||
this.rowData = JSON.parse(err.error).message;
|
||||
}
|
||||
|
||||
);
|
||||
}
|
||||
|
||||
filteredRoles(roles : Role[]):any {
|
||||
return this.allRoles.filter(
|
||||
(r:Role) => !roles.some((item) => item.id === r.id),
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
// private _filter(value: string): string[] {
|
||||
// const filterValue = value.toLowerCase();
|
||||
// this.ac.
|
||||
// return this.options.filter(option => option.toLowerCase().includes(filterValue));
|
||||
//}
|
||||
|
||||
getAllRoles():any{
|
||||
this.roleService.getAllRoles().subscribe(
|
||||
(data : any) => {
|
||||
this.allRoles = data;
|
||||
},
|
||||
(err : any)=> {
|
||||
this.allRoles = JSON.parse(err.error).message;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
remove(role: string): void {
|
||||
// this.fruits.update(fruits => {
|
||||
// const index = fruits.indexOf(fruit);
|
||||
// if (index < 0) {
|
||||
// return fruits;
|
||||
// }
|
||||
|
||||
// fruits.splice(index, 1);
|
||||
// this.announcer.announce(`Removed ${fruit}`);
|
||||
// return [...fruits];
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
add(event: MatChipInputEvent): void {
|
||||
const value = (event.value || '').trim();
|
||||
|
||||
// Add our fruit
|
||||
if (value) {
|
||||
// this.fruits.update(fruits => [...fruits, value]);
|
||||
}
|
||||
|
||||
// Clear the input value
|
||||
this.currentRole.set('');
|
||||
}
|
||||
|
||||
selected(roles : Role[], event: MatAutocompleteSelectedEvent): void {
|
||||
|
||||
roles.push(event.option.value);
|
||||
this.currentRole.set('');
|
||||
event.option.deselect();
|
||||
}
|
||||
|
||||
|
||||
change($event: Event, roles: Role[]) {
|
||||
|
||||
roles.filter(
|
||||
(r:Role) => !roles.some((item) => item.name?.toLowerCase() === r.id),
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
<p>board-moderator works!</p>
|
||||
@@ -0,0 +1,25 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { BoardModeratorComponent } from './board-moderator.component';
|
||||
|
||||
describe('BoardModeratorComponent', () => {
|
||||
let component: BoardModeratorComponent;
|
||||
let fixture: ComponentFixture<BoardModeratorComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
declarations: [ BoardModeratorComponent ]
|
||||
})
|
||||
.compileComponents();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(BoardModeratorComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,16 @@
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'app-board-moderator',
|
||||
templateUrl: './board-moderator.component.html',
|
||||
styleUrls: ['./board-moderator.component.scss'],
|
||||
standalone: false
|
||||
})
|
||||
export class BoardModeratorComponent implements OnInit {
|
||||
|
||||
constructor() { }
|
||||
|
||||
ngOnInit(): void {
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
<p>board-user works!</p>
|
||||
@@ -0,0 +1,25 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { BoardUserComponent } from './board-user.component';
|
||||
|
||||
describe('BoardUserComponent', () => {
|
||||
let component: BoardUserComponent;
|
||||
let fixture: ComponentFixture<BoardUserComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
declarations: [ BoardUserComponent ]
|
||||
})
|
||||
.compileComponents();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(BoardUserComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,16 @@
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'app-board-user',
|
||||
templateUrl: './board-user.component.html',
|
||||
styleUrls: ['./board-user.component.scss'],
|
||||
standalone: false
|
||||
})
|
||||
export class BoardUserComponent implements OnInit {
|
||||
|
||||
constructor() { }
|
||||
|
||||
ngOnInit(): void {
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
<h1 mat-dialog-title>Hi </h1>
|
||||
<div mat-dialog-content>
|
||||
<p>User name</p>
|
||||
<mat-form-field class="fillField">
|
||||
<input matInput [(ngModel)]="data.username">
|
||||
</mat-form-field>
|
||||
<p>Password</p>
|
||||
<mat-form-field class="fillField">
|
||||
<mat-label>Enter your password</mat-label>
|
||||
<input matInput
|
||||
[type]="hide() ? 'password' : 'text'"
|
||||
[(ngModel)]="data.password"
|
||||
/>
|
||||
<button
|
||||
mat-icon-button
|
||||
matSuffix
|
||||
(click)="clickEvent($event)"
|
||||
[attr.aria-label]="'Hide password'"
|
||||
[attr.aria-pressed]="hide()"
|
||||
>
|
||||
<mat-icon>{{hide() ? 'visibility_off' : 'visibility'}}</mat-icon>
|
||||
</button>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
|
||||
<div mat-dialog-actions>
|
||||
<button mat-button mat-dialog-close (click)="onNoClick()">No Thanks</button>
|
||||
<button mat-button [mat-dialog-close]="data" cdkFocusInitial>Ok</button>
|
||||
</div>
|
||||
@@ -0,0 +1,4 @@
|
||||
|
||||
.fillField{
|
||||
width: 100%;
|
||||
};
|
||||
@@ -0,0 +1,23 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { DialogComponent } from './dialog.component';
|
||||
|
||||
describe('DialogComponent', () => {
|
||||
let component: DialogComponent;
|
||||
let fixture: ComponentFixture<DialogComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
imports: [DialogComponent]
|
||||
})
|
||||
.compileComponents();
|
||||
|
||||
fixture = TestBed.createComponent(DialogComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,47 @@
|
||||
import {ChangeDetectionStrategy, Component, Inject, inject, signal} from '@angular/core';
|
||||
import {MatButtonModule} from "@angular/material/button";
|
||||
import {
|
||||
MAT_DIALOG_DATA,
|
||||
MatDialogActions,
|
||||
MatDialogClose,
|
||||
MatDialogContent,
|
||||
MatDialogRef,
|
||||
MatDialogTitle
|
||||
} from "@angular/material/dialog";
|
||||
|
||||
|
||||
import {MatFormField, MatLabel, MatSuffix} from "@angular/material/form-field";
|
||||
import {MatInput} from "@angular/material/input";
|
||||
import {FormsModule} from "@angular/forms";
|
||||
import {MatIcon} from "@angular/material/icon";
|
||||
import {DialogData} from "../../app.component";
|
||||
|
||||
|
||||
|
||||
@Component({
|
||||
selector: 'app-dialog',
|
||||
imports: [MatButtonModule, MatLabel, MatDialogActions, MatDialogClose, MatDialogTitle, MatDialogContent, MatFormField, MatInput, FormsModule, MatIcon, MatSuffix],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
templateUrl: './dialog.component.html',
|
||||
styleUrl: './dialog.component.scss'
|
||||
})
|
||||
export class DialogComponent {
|
||||
readonly dialogRef = inject(MatDialogRef<DialogComponent>);
|
||||
hide = signal(true);
|
||||
|
||||
constructor(
|
||||
@Inject(MAT_DIALOG_DATA) public data:DialogData) {}
|
||||
|
||||
onNoClick() {
|
||||
this.dialogRef.close();
|
||||
}
|
||||
|
||||
|
||||
clickEvent(event: MouseEvent) {
|
||||
this.hide.set(!this.hide());
|
||||
event.stopPropagation();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
<div class="container">
|
||||
<header class="jumbotron">
|
||||
<p>{{ content }}</p>
|
||||
</header>
|
||||
</div>
|
||||
@@ -0,0 +1,25 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { HomeComponent } from './home.component';
|
||||
|
||||
describe('HomeComponent', () => {
|
||||
let component: HomeComponent;
|
||||
let fixture: ComponentFixture<HomeComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
declarations: [ HomeComponent ]
|
||||
})
|
||||
.compileComponents();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(HomeComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,26 @@
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
import {UserService} from '../../services/user.service';
|
||||
|
||||
|
||||
@Component({
|
||||
selector: 'app-home',
|
||||
templateUrl: './home.component.html',
|
||||
styleUrls: ['./home.component.scss'],
|
||||
standalone: false
|
||||
})
|
||||
export class HomeComponent implements OnInit {
|
||||
content?: string;
|
||||
|
||||
constructor(private userService: UserService) { }
|
||||
|
||||
ngOnInit(): void {
|
||||
// this.userService.getPublicContent().subscribe(
|
||||
// (data : any) => {
|
||||
// this.content = data;
|
||||
// },
|
||||
// (err : any) => {
|
||||
// this.content = JSON.parse(err.error).message;
|
||||
// }
|
||||
// );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
<div class="col-md-12">
|
||||
<div class="card card-container">
|
||||
<img
|
||||
id="profile-img"
|
||||
src="//ssl.gstatic.com/accounts/ui/avatar_2x.png"
|
||||
class="profile-img-card"
|
||||
/>
|
||||
<form
|
||||
*ngIf="!isLoggedIn"
|
||||
name="form"
|
||||
(ngSubmit)="f.form.valid && onSubmit()"
|
||||
#f="ngForm"
|
||||
novalidate
|
||||
>
|
||||
<div class="form-group">
|
||||
<label for="username">Username</label>
|
||||
<input
|
||||
type="text"
|
||||
class="form-control"
|
||||
id="username"
|
||||
[(ngModel)]="form.username"
|
||||
required
|
||||
#username="ngModel"
|
||||
/>
|
||||
<div
|
||||
class="alert alert-danger"
|
||||
role="alert"
|
||||
*ngIf="username.errors && f.submitted"
|
||||
>
|
||||
Username is required!
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="password">Password</label>
|
||||
<input
|
||||
type="password"
|
||||
class="form-control"
|
||||
id="password"
|
||||
[(ngModel)]="form.password"
|
||||
required
|
||||
minlength="6"
|
||||
#password="ngModel"
|
||||
/>
|
||||
<div
|
||||
class="alert alert-danger"
|
||||
role="alert"
|
||||
*ngIf="password.errors && f.submitted"
|
||||
>
|
||||
<div *ngIf="password.errors['required']">Password is required</div>
|
||||
<div *ngIf="password.errors['minlength']">
|
||||
Password must be at least 6 characters
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<button class="btn btn-primary btn-block">
|
||||
Login
|
||||
</button>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div
|
||||
class="alert alert-danger"
|
||||
role="alert"
|
||||
*ngIf="f.submitted && isLoginFailed"
|
||||
>
|
||||
Login failed: {{ errorMessage }}
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<div class="alert alert-success" *ngIf="isLoggedIn">
|
||||
Logged in as {{ roles }}.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1 @@
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { LoginComponent } from './login.component';
|
||||
|
||||
describe('LoginComponent', () => {
|
||||
let component: LoginComponent;
|
||||
let fixture: ComponentFixture<LoginComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
declarations: [ LoginComponent ]
|
||||
})
|
||||
.compileComponents();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(LoginComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,60 @@
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
|
||||
import {FormsModule} from "@angular/forms";
|
||||
import {NgIf} from "@angular/common";
|
||||
import {AuthService} from '../../services/auth.service';
|
||||
import {TokenStorageService} from '../../services/token-storage.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-login',
|
||||
templateUrl: './login.component.html',
|
||||
imports: [
|
||||
FormsModule,
|
||||
NgIf
|
||||
],
|
||||
styleUrls: ['./login.component.scss']
|
||||
})
|
||||
export class LoginComponent implements OnInit {
|
||||
|
||||
form: any = {
|
||||
username: null,
|
||||
password: null
|
||||
};
|
||||
isLoggedIn = false;
|
||||
isLoginFailed = false;
|
||||
errorMessage = '';
|
||||
roles: string[] = [];
|
||||
|
||||
constructor(private authService: AuthService, private tokenStorage: TokenStorageService) { }
|
||||
|
||||
ngOnInit(): void {
|
||||
if (this.tokenStorage.getToken()) {
|
||||
this.isLoggedIn = true;
|
||||
this.roles = this.tokenStorage.getUser().roles;
|
||||
}
|
||||
}
|
||||
|
||||
onSubmit(): void {
|
||||
const { username, password } = this.form;
|
||||
|
||||
this.authService.login(username, password).subscribe(
|
||||
data => {
|
||||
this.tokenStorage.saveToken(data.accessToken);
|
||||
this.tokenStorage.saveUser(data);
|
||||
|
||||
this.isLoginFailed = false;
|
||||
this.isLoggedIn = true;
|
||||
this.roles = this.tokenStorage.getUser().roles;
|
||||
this.reloadPage();
|
||||
},
|
||||
err => {
|
||||
this.errorMessage = err.error.message;
|
||||
this.isLoginFailed = true;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
reloadPage(): void {
|
||||
window.location.reload();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
<div class="container" *ngIf="currentUser; else loggedOut">
|
||||
<header class="jumbotron">
|
||||
<h3>
|
||||
<strong>{{ currentUser.username }}</strong> Profile
|
||||
</h3>
|
||||
</header>
|
||||
<p>
|
||||
<strong>Token:</strong>
|
||||
{{ currentUser.accessToken.substring(0, 20) }} ...
|
||||
{{ currentUser.accessToken.substr(currentUser.accessToken.length - 20) }}
|
||||
</p>
|
||||
<p>
|
||||
<strong>Email:</strong>
|
||||
{{ currentUser.email }}
|
||||
</p>
|
||||
<strong>Roles:</strong>
|
||||
<ul>
|
||||
<li *ngFor="let role of currentUser.roles">
|
||||
{{ role }}
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<ng-template #loggedOut>
|
||||
Please login.
|
||||
</ng-template>
|
||||
@@ -0,0 +1,25 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { ProfileComponent } from './profile.component';
|
||||
|
||||
describe('ProfileComponent', () => {
|
||||
let component: ProfileComponent;
|
||||
let fixture: ComponentFixture<ProfileComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
declarations: [ ProfileComponent ]
|
||||
})
|
||||
.compileComponents();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(ProfileComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,19 @@
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
import {TokenStorageService} from '../../services/token-storage.service';
|
||||
|
||||
|
||||
@Component({
|
||||
selector: 'app-profile',
|
||||
templateUrl: './profile.component.html',
|
||||
styleUrls: ['./profile.component.scss'],
|
||||
standalone: false
|
||||
})
|
||||
export class ProfileComponent implements OnInit {
|
||||
currentUser: any;
|
||||
|
||||
constructor(private token: TokenStorageService) { }
|
||||
|
||||
ngOnInit(): void {
|
||||
this.currentUser = this.token.getUser();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
<div class="col-md-12">
|
||||
<div id="register">
|
||||
<img id="foto"
|
||||
id="profile-img"
|
||||
src="//ssl.gstatic.com/accounts/ui/avatar_2x.png"
|
||||
class="profile-img-card"
|
||||
/>
|
||||
<form
|
||||
*ngIf="!isSuccessful"
|
||||
name="form"
|
||||
(ngSubmit)="f.form.valid && onSubmit()"
|
||||
#f="ngForm"
|
||||
novalidate
|
||||
>
|
||||
<div class="form-group">
|
||||
<label for="username">Username</label>
|
||||
<input
|
||||
type="text"
|
||||
class="form-control"
|
||||
id="username"
|
||||
[(ngModel)]="form.username"
|
||||
[ngModelOptions]="{standalone: true}"
|
||||
required
|
||||
minlength="3"
|
||||
maxlength="20"
|
||||
#username="ngModel"
|
||||
/>
|
||||
<div class="alert-danger" *ngIf="username.errors && f.submitted">
|
||||
<div *ngIf="username.errors['required']">Username is required</div>
|
||||
<div *ngIf="username.errors['minlength']">
|
||||
Username must be at least 3 characters
|
||||
</div>
|
||||
<div *ngIf="username.errors['maxlength']">
|
||||
Username must be at most 20 characters
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="email">Email</label>
|
||||
<input
|
||||
type="email"
|
||||
class="form-control"
|
||||
id="email"
|
||||
[(ngModel)]="form.email"
|
||||
[ngModelOptions]="{standalone: true}"
|
||||
required
|
||||
email
|
||||
#email="ngModel"
|
||||
/>
|
||||
<div class="alert-danger" *ngIf="email.errors && f.submitted">
|
||||
<div *ngIf="email.errors['required']">Email is required</div>
|
||||
<div *ngIf="email.errors['email']">
|
||||
Email must be a valid email address
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="password">Password</label>
|
||||
<input
|
||||
type="password"
|
||||
class="form-control"
|
||||
id="password"
|
||||
[(ngModel)]="form.password"
|
||||
[ngModelOptions]="{standalone: true}"
|
||||
required
|
||||
minlength="6"
|
||||
#password="ngModel"
|
||||
/>
|
||||
<div class="alert-danger" *ngIf="password.errors && f.submitted">
|
||||
<div *ngIf="password.errors['required']">Password is required</div>
|
||||
<div *ngIf="password.errors['minlength']">
|
||||
Password must be at least 6 characters
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<button class="btn btn-primary btn-block">Sign Up</button>
|
||||
</div>
|
||||
|
||||
<div class="alert alert-warning" *ngIf="f.submitted && isSignUpFailed">
|
||||
Signup failed!<br />{{ errorMessage }}
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<div class="alert alert-success" *ngIf="isSuccessful">
|
||||
Your registration is successful!
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,25 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { RegisterComponent } from './register.component';
|
||||
|
||||
describe('RegisterComponent', () => {
|
||||
let component: RegisterComponent;
|
||||
let fixture: ComponentFixture<RegisterComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
declarations: [ RegisterComponent ]
|
||||
})
|
||||
.compileComponents();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(RegisterComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,41 @@
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
import {AuthService} from '../../services/auth.service';
|
||||
|
||||
|
||||
@Component({
|
||||
selector: 'app-register',
|
||||
templateUrl: './register.component.html',
|
||||
styleUrls: ['./register.component.scss'],
|
||||
standalone: false
|
||||
})
|
||||
export class RegisterComponent implements OnInit {
|
||||
form: any = {
|
||||
username: null,
|
||||
email: null,
|
||||
password: null
|
||||
};
|
||||
isSuccessful = false;
|
||||
isSignUpFailed = false;
|
||||
errorMessage = '';
|
||||
|
||||
constructor(private authService: AuthService) { }
|
||||
|
||||
ngOnInit(): void {
|
||||
}
|
||||
|
||||
onSubmit(): void {
|
||||
const { username, email, password } = this.form;
|
||||
|
||||
this.authService.register(username, email, password).subscribe(
|
||||
data => {
|
||||
console.log(data);
|
||||
this.isSuccessful = true;
|
||||
this.isSignUpFailed = false;
|
||||
},
|
||||
err => {
|
||||
this.errorMessage = err.error.message;
|
||||
this.isSignUpFailed = true;
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
<div>
|
||||
<div *ngIf="currentTutorial.id" class="edit-form">
|
||||
<h4>Tutorial</h4>
|
||||
<form>
|
||||
<div class="form-group">
|
||||
<label for="title">Title</label>
|
||||
<input
|
||||
type="text"
|
||||
class="form-control"
|
||||
id="title"
|
||||
[(ngModel)]="currentTutorial.title"
|
||||
name="title"
|
||||
/>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="description">Description</label>
|
||||
<input
|
||||
type="text"
|
||||
class="form-control"
|
||||
id="description"
|
||||
[(ngModel)]="currentTutorial.description"
|
||||
name="description"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label><strong>Status:</strong></label>
|
||||
{{ currentTutorial.published ? "Published" : "Pending" }}
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<button
|
||||
class="badge badge-primary mr-2"
|
||||
*ngIf="currentTutorial.published"
|
||||
(click)="updatePublished(false)"
|
||||
>
|
||||
UnPublish
|
||||
</button>
|
||||
<button
|
||||
*ngIf="!currentTutorial.published"
|
||||
class="badge badge-primary mr-2"
|
||||
(click)="updatePublished(true)"
|
||||
>
|
||||
Publish
|
||||
</button>
|
||||
|
||||
<button class="badge badge-danger mr-2" (click)="deleteTutorial()">
|
||||
Delete
|
||||
</button>
|
||||
|
||||
<button
|
||||
type="submit"
|
||||
class="badge badge-success mb-2"
|
||||
(click)="updateTutorial()"
|
||||
>
|
||||
Update
|
||||
</button>
|
||||
<p>{{ message }}</p>
|
||||
</div>
|
||||
|
||||
<div *ngIf="!currentTutorial.id">
|
||||
<br />
|
||||
<p>Cannot access this Tutorial...</p>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,4 @@
|
||||
.edit-form {
|
||||
max-width: 400px;
|
||||
margin: auto;
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { TutorialDetailsComponent } from './tutorial-details.component';
|
||||
|
||||
describe('TutorialDetailsComponent', () => {
|
||||
let component: TutorialDetailsComponent;
|
||||
let fixture: ComponentFixture<TutorialDetailsComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
declarations: [ TutorialDetailsComponent ]
|
||||
})
|
||||
.compileComponents();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(TutorialDetailsComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,91 @@
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
|
||||
import { ActivatedRoute, Router } from '@angular/router';
|
||||
import {Tutorial} from '../../models/tutorial.model';
|
||||
import {TutorialService} from '../../services/tutorial.service';
|
||||
|
||||
|
||||
@Component({
|
||||
selector: 'app-tutorial-details',
|
||||
templateUrl: './tutorial-details.component.html',
|
||||
styleUrls: ['./tutorial-details.component.scss'],
|
||||
standalone: false
|
||||
})
|
||||
export class TutorialDetailsComponent implements OnInit {
|
||||
|
||||
currentTutorial: Tutorial = {
|
||||
title: '',
|
||||
description: '',
|
||||
published: false
|
||||
};
|
||||
message = '';
|
||||
|
||||
constructor(
|
||||
private tutorialService: TutorialService,
|
||||
private route: ActivatedRoute,
|
||||
private router: Router) { }
|
||||
|
||||
ngOnInit(): void {
|
||||
this.message = '';
|
||||
this.getTutorial(this.route.snapshot.params['id']);
|
||||
}
|
||||
|
||||
getTutorial(id: string): void {
|
||||
this.tutorialService.get(id)
|
||||
.subscribe(
|
||||
data => {
|
||||
this.currentTutorial = data;
|
||||
console.log(data);
|
||||
},
|
||||
error => {
|
||||
console.log(error);
|
||||
});
|
||||
}
|
||||
|
||||
updatePublished(status: boolean): void {
|
||||
const data = {
|
||||
title: this.currentTutorial.title,
|
||||
description: this.currentTutorial.description,
|
||||
published: status
|
||||
};
|
||||
|
||||
this.message = '';
|
||||
|
||||
this.tutorialService.update(this.currentTutorial.id, data)
|
||||
.subscribe(
|
||||
response => {
|
||||
this.currentTutorial.published = status;
|
||||
console.log(response);
|
||||
this.message = response.message ? response.message : 'The status was updated successfully!';
|
||||
},
|
||||
error => {
|
||||
console.log(error);
|
||||
});
|
||||
}
|
||||
|
||||
updateTutorial(): void {
|
||||
this.message = '';
|
||||
|
||||
this.tutorialService.update(this.currentTutorial.id, this.currentTutorial)
|
||||
.subscribe(
|
||||
response => {
|
||||
console.log(response);
|
||||
this.message = response.message ? response.message : 'This tutorial was updated successfully!';
|
||||
},
|
||||
error => {
|
||||
console.log(error);
|
||||
});
|
||||
}
|
||||
|
||||
deleteTutorial(): void {
|
||||
this.tutorialService.delete(this.currentTutorial.id)
|
||||
.subscribe(
|
||||
response => {
|
||||
console.log(response);
|
||||
this.router.navigate(['/tutorials']);
|
||||
},
|
||||
error => {
|
||||
console.log(error);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
<div class="list row">
|
||||
<div class="col-md-8">
|
||||
<div class="input-group mb-3">
|
||||
<input
|
||||
type="text"
|
||||
class="form-control"
|
||||
placeholder="Search by title"
|
||||
[(ngModel)]="title"
|
||||
/>
|
||||
<div class="input-group-append">
|
||||
<button
|
||||
class="btn btn-outline-secondary"
|
||||
type="button"
|
||||
(click)="searchTitle()"
|
||||
>
|
||||
Search
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<h4>Tutorials List</h4>
|
||||
<ul class="list-group">
|
||||
<li
|
||||
class="list-group-item"
|
||||
*ngFor="let tutorial of tutorials; let i = index"
|
||||
[class.active]="i == currentIndex"
|
||||
(click)="setActiveTutorial(tutorial, i)"
|
||||
>
|
||||
{{ tutorial.title }}
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<button class="m-3 btn btn-sm btn-danger" (click)="removeAllTutorials()">
|
||||
Remove All
|
||||
</button>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div *ngIf="currentTutorial.id">
|
||||
<h4>Tutorial</h4>
|
||||
<div>
|
||||
<label><strong>Title:</strong></label> {{ currentTutorial.title }}
|
||||
</div>
|
||||
<div>
|
||||
<label><strong>Description:</strong></label>
|
||||
{{ currentTutorial.description }}
|
||||
</div>
|
||||
<div>
|
||||
<label><strong>Status:</strong></label>
|
||||
{{ currentTutorial.published ? "Published" : "Pending" }}
|
||||
</div>
|
||||
|
||||
<a class="badge badge-warning" routerLink="/tutorials/{{ currentTutorial.id }}">
|
||||
Edit
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div *ngIf="!currentTutorial">
|
||||
<br />
|
||||
<p>Please click on a Tutorial...</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,5 @@
|
||||
.list {
|
||||
text-align: left;
|
||||
max-width: 750px;
|
||||
margin: auto;
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { TutorialsListComponent } from './tutorials-list.component';
|
||||
|
||||
describe('TutorialsListComponent', () => {
|
||||
let component: TutorialsListComponent;
|
||||
let fixture: ComponentFixture<TutorialsListComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
declarations: [ TutorialsListComponent ]
|
||||
})
|
||||
.compileComponents();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(TutorialsListComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,75 @@
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
|
||||
|
||||
import {Tutorial} from '../../models/tutorial.model';
|
||||
import {TutorialService} from '../../services/tutorial.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-tutorials-list',
|
||||
templateUrl: './tutorials-list.component.html',
|
||||
styleUrls: ['./tutorials-list.component.scss'],
|
||||
standalone: false
|
||||
})
|
||||
export class TutorialsListComponent implements OnInit {
|
||||
|
||||
tutorials?: Tutorial[];
|
||||
currentTutorial: Tutorial = {};
|
||||
currentIndex = -1;
|
||||
title = '';
|
||||
|
||||
constructor(private tutorialService: TutorialService) { }
|
||||
|
||||
ngOnInit(): void {
|
||||
this.retrieveTutorials();
|
||||
}
|
||||
|
||||
retrieveTutorials(): void {
|
||||
this.tutorialService.getAll()
|
||||
.subscribe(
|
||||
data => {
|
||||
this.tutorials = data;
|
||||
console.log(data);
|
||||
},
|
||||
error => {
|
||||
console.log(error);
|
||||
});
|
||||
}
|
||||
|
||||
refreshList(): void {
|
||||
this.retrieveTutorials();
|
||||
this.currentTutorial = {};
|
||||
this.currentIndex = -1;
|
||||
}
|
||||
|
||||
setActiveTutorial(tutorial: Tutorial, index: number): void {
|
||||
this.currentTutorial = tutorial;
|
||||
this.currentIndex = index;
|
||||
}
|
||||
|
||||
removeAllTutorials(): void {
|
||||
this.tutorialService.deleteAll()
|
||||
.subscribe(
|
||||
response => {
|
||||
console.log(response);
|
||||
this.refreshList();
|
||||
},
|
||||
error => {
|
||||
console.log(error);
|
||||
});
|
||||
}
|
||||
|
||||
searchTitle(): void {
|
||||
this.currentTutorial = {};
|
||||
this.currentIndex = -1;
|
||||
|
||||
this.tutorialService.findByTitle(this.title)
|
||||
.subscribe(
|
||||
data => {
|
||||
this.tutorials = data;
|
||||
console.log(data);
|
||||
},
|
||||
error => {
|
||||
console.log(error);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
import { HTTP_INTERCEPTORS, HttpEvent } from '@angular/common/http';
|
||||
import { Injectable } from '@angular/core';
|
||||
import { HttpInterceptor, HttpHandler, HttpRequest } from '@angular/common/http';
|
||||
|
||||
import { TokenStorageService } from '../services/token-storage.service';
|
||||
import { Observable } from 'rxjs';
|
||||
|
||||
const TOKEN_HEADER_KEY = 'Authorization'; // for Spring Boot back-end
|
||||
|
||||
@Injectable()
|
||||
export class AuthInterceptor implements HttpInterceptor {
|
||||
constructor(private token: TokenStorageService) { }
|
||||
|
||||
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
|
||||
let authReq = req;
|
||||
const token = this.token.getToken();
|
||||
if (token != null) {
|
||||
authReq = req.clone({ headers: req.headers.set(TOKEN_HEADER_KEY, 'Bearer ' + token) });
|
||||
}
|
||||
return next.handle(authReq);
|
||||
}
|
||||
}
|
||||
|
||||
export const authInterceptorProviders = [
|
||||
{ provide: HTTP_INTERCEPTORS, useClass: AuthInterceptor, multi: true }
|
||||
];
|
||||
@@ -0,0 +1,7 @@
|
||||
import { Role } from './role';
|
||||
|
||||
describe('Role', () => {
|
||||
it('should create an instance', () => {
|
||||
expect(new Role()).toBeTruthy();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,4 @@
|
||||
export class Role {
|
||||
id?:any;
|
||||
name?:string;
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
export class Setting {
|
||||
id?:any;
|
||||
name?:string;
|
||||
value?:boolean;
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
import { Tutorial } from './tutorial.model';
|
||||
|
||||
describe('Tutorial', () => {
|
||||
it('should create an instance', () => {
|
||||
expect(new Tutorial()).toBeTruthy();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,6 @@
|
||||
export class Tutorial {
|
||||
id?: any;
|
||||
title?: string;
|
||||
description?: string;
|
||||
published?: boolean;
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
import { User } from './user.model';
|
||||
|
||||
describe('User', () => {
|
||||
it('should create an instance', () => {
|
||||
expect(new User()).toBeTruthy();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,7 @@
|
||||
export class User {
|
||||
id?:any;
|
||||
username?:string;
|
||||
email?:string;
|
||||
password?:string;
|
||||
roles?:any;
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
import { TestBed } from '@angular/core/testing';
|
||||
|
||||
import { AuthService } from './auth.service';
|
||||
|
||||
describe('AuthService', () => {
|
||||
let service: AuthService;
|
||||
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({});
|
||||
service = TestBed.inject(AuthService);
|
||||
});
|
||||
|
||||
it('should be created', () => {
|
||||
expect(service).toBeTruthy();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,35 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { HttpClient, HttpHeaders } from '@angular/common/http';
|
||||
import { Observable } from 'rxjs';
|
||||
|
||||
const AUTH_API = 'http://localhost:8080/api/auth/';
|
||||
|
||||
const httpOptions = {
|
||||
headers: new HttpHeaders({ 'Content-Type': 'application/json' })
|
||||
};
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class AuthService {
|
||||
constructor(private http: HttpClient) { }
|
||||
|
||||
login(username: string, password: string): Observable<any> {
|
||||
return this.http.post(AUTH_API + 'signin', {
|
||||
username,
|
||||
password
|
||||
}, httpOptions);
|
||||
}
|
||||
|
||||
register(username: string, email: string, password: string): Observable<any> {
|
||||
return this.http.post(AUTH_API + 'signup', {
|
||||
username,
|
||||
email,
|
||||
password
|
||||
}, httpOptions);
|
||||
}
|
||||
|
||||
logout(): Observable<any> {
|
||||
return this.http.post(AUTH_API + 'signout', { }, httpOptions);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
import { TestBed } from '@angular/core/testing';
|
||||
|
||||
import { RolesService } from './roles.service';
|
||||
|
||||
describe('RolesService', () => {
|
||||
let service: RolesService;
|
||||
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({});
|
||||
service = TestBed.inject(RolesService);
|
||||
});
|
||||
|
||||
it('should be created', () => {
|
||||
expect(service).toBeTruthy();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,20 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { HttpClient } from '@angular/common/http';
|
||||
import { Observable } from 'rxjs';
|
||||
import { Role } from '../models/role';
|
||||
|
||||
const API_URL = 'http://localhost:8080/api/roles';
|
||||
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class RolesService {
|
||||
|
||||
constructor(private http: HttpClient) {}
|
||||
|
||||
getAllRoles(): Observable<Role[]> {
|
||||
return this.http.get<Role[]>(API_URL);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
import { TestBed } from '@angular/core/testing';
|
||||
|
||||
import { TokenStorageService } from './token-storage.service';
|
||||
|
||||
describe('TokenStorageService', () => {
|
||||
let service: TokenStorageService;
|
||||
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({});
|
||||
service = TestBed.inject(TokenStorageService);
|
||||
});
|
||||
|
||||
it('should be created', () => {
|
||||
expect(service).toBeTruthy();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,51 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
|
||||
const TOKEN_KEY = 'auth-token';
|
||||
const USER_KEY = 'auth-user';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class TokenStorageService {
|
||||
constructor() { }
|
||||
|
||||
signOut(): void {
|
||||
window.sessionStorage.clear();
|
||||
}
|
||||
|
||||
public saveToken(token: string): void {
|
||||
window.sessionStorage.removeItem(TOKEN_KEY);
|
||||
window.sessionStorage.setItem(TOKEN_KEY, token);
|
||||
}
|
||||
|
||||
public getToken(): string | null {
|
||||
return window.sessionStorage.getItem(TOKEN_KEY);
|
||||
}
|
||||
|
||||
public saveUser(user: any): void {
|
||||
window.sessionStorage.removeItem(USER_KEY);
|
||||
window.sessionStorage.setItem(USER_KEY, JSON.stringify(user));
|
||||
}
|
||||
|
||||
public getUser(): any {
|
||||
const user = window.sessionStorage.getItem(USER_KEY);
|
||||
if (user) {
|
||||
return JSON.parse(user);
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
clean(): void {
|
||||
window.sessionStorage.clear();
|
||||
}
|
||||
|
||||
public isLoggedIn(): boolean {
|
||||
const user = window.sessionStorage.getItem(USER_KEY);
|
||||
if (user) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
import { TestBed } from '@angular/core/testing';
|
||||
|
||||
import { TutorialService } from './tutorial.service';
|
||||
|
||||
describe('TutorialService', () => {
|
||||
let service: TutorialService;
|
||||
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({});
|
||||
service = TestBed.inject(TutorialService);
|
||||
});
|
||||
|
||||
it('should be created', () => {
|
||||
expect(service).toBeTruthy();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,42 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { HttpClient } from '@angular/common/http';
|
||||
import { Observable } from 'rxjs';
|
||||
import { Tutorial } from '../models/tutorial.model';
|
||||
|
||||
const baseUrl = 'http://localhost:8080/api/tutorials';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class TutorialService {
|
||||
|
||||
constructor(private http: HttpClient) { }
|
||||
|
||||
getAll(): Observable<Tutorial[]> {
|
||||
return this.http.get<Tutorial[]>(baseUrl);
|
||||
}
|
||||
|
||||
get(id: any): Observable<Tutorial> {
|
||||
return this.http.get(`${baseUrl}/${id}`);
|
||||
}
|
||||
|
||||
create(data: any): Observable<any> {
|
||||
return this.http.post(baseUrl, data);
|
||||
}
|
||||
|
||||
update(id: any, data: any): Observable<any> {
|
||||
return this.http.put(`${baseUrl}/${id}`, data);
|
||||
}
|
||||
|
||||
delete(id: any): Observable<any> {
|
||||
return this.http.delete(`${baseUrl}/${id}`);
|
||||
}
|
||||
|
||||
deleteAll(): Observable<any> {
|
||||
return this.http.delete(baseUrl);
|
||||
}
|
||||
|
||||
findByTitle(title: any): Observable<Tutorial[]> {
|
||||
return this.http.get<Tutorial[]>(`${baseUrl}?title=${title}`);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
import { TestBed } from '@angular/core/testing';
|
||||
|
||||
import { UserService } from './user.service';
|
||||
|
||||
describe('UserService', () => {
|
||||
let service: UserService;
|
||||
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({});
|
||||
service = TestBed.inject(UserService);
|
||||
});
|
||||
|
||||
it('should be created', () => {
|
||||
expect(service).toBeTruthy();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,31 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { HttpClient } from '@angular/common/http';
|
||||
import { Observable } from 'rxjs';
|
||||
import {User} from "../models/user.model";
|
||||
import {Tutorial} from "../models/tutorial.model";
|
||||
|
||||
const API_URL = 'http://localhost:8080/api/users';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class UserService {
|
||||
constructor(private http: HttpClient) { }
|
||||
|
||||
getPublicContent(): Observable<any> {
|
||||
return this.http.get(API_URL + '/all', { responseType: 'text' });
|
||||
}
|
||||
|
||||
getUserBoard(): Observable<any> {
|
||||
return this.http.get(API_URL + '/user', { responseType: 'text' });
|
||||
}
|
||||
|
||||
getModeratorBoard(): Observable<any> {
|
||||
return this.http.get(API_URL + '/mod', { responseType: 'text' });
|
||||
}
|
||||
|
||||
getAdminBoard(): Observable<User[]> {
|
||||
return this.http.get<User[]>(API_URL);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user