Commit 68089e4f authored by Simone Vuotto's avatar Simone Vuotto

Add projects controllers

parent 9035e9df
......@@ -20,7 +20,8 @@
"prefix": "app",
"styles": [
"styles.css",
"../node_modules/bootswatch/dist/simplex/bootstrap.min.css"
"../node_modules/bootswatch/dist/simplex/bootstrap.min.css",
"../node_modules/font-awesome/css/font-awesome.min.css"
],
"scripts": [
"../node_modules/jquery/dist/jquery.min.js",
......
......@@ -381,11 +381,6 @@
"integrity": "sha1-SlKCrBZHKek2Gbz9OtFR+BfOkfU=",
"dev": true
},
"angular-in-memory-web-api": {
"version": "0.5.2",
"resolved": "https://registry.npmjs.org/angular-in-memory-web-api/-/angular-in-memory-web-api-0.5.2.tgz",
"integrity": "sha1-/y6mZqv9BN19FslT7JcuExigmCQ="
},
"angular2-jwt": {
"version": "0.2.3",
"resolved": "https://registry.npmjs.org/angular2-jwt/-/angular2-jwt-0.2.3.tgz",
......@@ -2954,6 +2949,11 @@
"readable-stream": "2.3.3"
}
},
"font-awesome": {
"version": "4.7.0",
"resolved": "https://registry.npmjs.org/font-awesome/-/font-awesome-4.7.0.tgz",
"integrity": "sha1-j6jPBBGhoxr9B7BtKQK7n8gVoTM="
},
"for-in": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz",
......
......@@ -25,6 +25,7 @@
"bootstrap": "^3.3.7",
"bootswatch": "^4.0.0-beta.2",
"core-js": "^2.4.1",
"font-awesome": "^4.7.0",
"jquery": "^3.2.1",
"rxjs": "^5.5.2",
"zone.js": "^0.8.14"
......
......@@ -3,11 +3,13 @@ import {RouterModule, Routes} from '@angular/router';
import {LoginComponent} from './login/login.component';
import {ProjectsComponent} from './projects/projects.component';
import {AuthGuard} from './auth.guard';
import { ProjectDetailsComponent } from './project-details/project-details.component';
const routes: Routes = [
{ path: '', component: LoginComponent },
{ path: 'projects', component: ProjectsComponent, canActivate: [AuthGuard] },
{ path: 'projects/:id', component: ProjectDetailsComponent, canActivate: [AuthGuard]},
// otherwise redirect to home
{ path: '**', redirectTo: '' }
......
// Modules
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { HTTP_INTERCEPTORS, HttpClientModule} from '@angular/common/http';
import { AppRoutingModule } from './app-routing.module';
......@@ -10,28 +10,34 @@ import { AppComponent } from './app.component';
import { LoginComponent } from './login/login.component';
import { ProjectsComponent } from './projects/projects.component';
import { AlertComponent } from './alert/alert.component';
import { ProjectDialogComponent } from './project-dialog/project-dialog.component';
// Services
import { AuthenticationService } from './authentication.service';
import { AlertService } from './alert/alert.service';
import { ProjectService } from './projects/project.service';
// Guards
import {AuthGuard} from './auth.guard';
// Http Interceptor
import { JwtInterceptor } from './jwt.interceptor.js';
import { ProjectDetailsComponent } from './project-details/project-details.component';
@NgModule({
declarations: [
AppComponent,
LoginComponent,
ProjectsComponent,
AlertComponent,
ProjectsComponent,
ProjectDialogComponent,
ProjectDetailsComponent,
],
imports: [
BrowserModule,
AppRoutingModule,
ReactiveFormsModule,
FormsModule,
HttpClientModule,
],
......@@ -44,6 +50,7 @@ import { JwtInterceptor } from './jwt.interceptor.js';
useClass: JwtInterceptor,
multi: true
},
ProjectService,
],
bootstrap: [ AppComponent ]
})
......
export class Project {
id: number;
name: string;
description: string;
type: ProjectType;
constructor(id: number, name: string, description: string, type: ProjectType) {
this.id = id;
this.name = name;
this.description = description;
this.type = type;
}
}
export class ProjectType {
id: number;
name: string;
constructor(id: number, name: string) {
this.id = id;
this.name = name;
}
}
import { Component, OnInit } from '@angular/core';
import { ActivatedRoute } from '@angular/router';
@Component({
selector: 'app-project-details',
templateUrl: './project-details.component.html',
styleUrls: ['./project-details.component.css']
})
export class ProjectDetailsComponent implements OnInit {
id: number;
constructor(private route: ActivatedRoute) { }
ngOnInit() {
this.id = +this.route.snapshot.paramMap.get('id');
}
}
<div>
<button type="button" class="btn btn-primary" data-toggle="modal" data-target="#newProjectModal">New Project</button>
</div>
<!-- Modal -->
<div class="modal" tabindex="-1" id="newProjectModal" role="dialog">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">New Project</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<form [formGroup]="newProjectForm">
<div class="modal-body">
<div class="form-group">
<label class="form-control-label" for="project-name">Name</label>
<input class="form-control" id="project-name" type="text" formControlName="name"
[ngClass]="{'is-valid': name.valid, 'is-invalid': !(name.valid || name.pristine)}">
<div [hidden]="name.valid || name.pristine"
class="alert alert-danger">
Name must be at least 3 characters long
</div>
</div>
<div class="form-group">
<label for="project-description">Description</label>
<textarea class="form-control"
id="project-description"
rows="2"
formControlName="description">
</textarea>
</div>
<div class="form-group">
<label class="form-control-label" for="project-type">Type</label>
<select class="form-control custom-select" id="project-type" formControlName="type">
<option *ngFor="let type of projectTypes" [value]="type" selected>{{type.name}}</option>
</select>
</div>
</div>
<div class="modal-footer">
<button type="submit" class="btn btn-primary" data-dismiss="modal" [disabled]="!newProjectForm.valid" (click)="createProject()">Save</button>
<button type="reset" class="btn btn-secondary" data-dismiss="modal" (click)="reset()">Close</button>
</div>
</form>
</div>
</div>
</div>
import { Component, OnInit } from '@angular/core';
import { Project, ProjectType } from '../models/project';
import { ProjectService } from '../projects/project.service';
import { FormBuilder, FormControl, FormGroup, Validators } from '@angular/forms';
import { AlertService } from '../alert/alert.service';
@Component({
selector: 'app-project-dialog',
templateUrl: './project-dialog.component.html'
})
export class ProjectDialogComponent implements OnInit {
projectTypes: ProjectType[];
newProjectForm: FormGroup;
project = new Project(null, '', '', null);
constructor( private fb: FormBuilder,
private projectService: ProjectService,
private alertService: AlertService) {
this.createForm();
}
ngOnInit() {
this.getProjectTypes();
}
createForm() {
this.newProjectForm = this.fb.group({
name: new FormControl(this.project.name, [ Validators.required, Validators.minLength(3) ]),
description: new FormControl(this.project.description),
type: new FormControl(this.project.type, Validators.required)
});
}
get name() { return this.newProjectForm.get('name'); }
get description() { return this.newProjectForm.get('description'); }
reset() {
this.newProjectForm.reset();
if (this.projectTypes.length > 0) {
this.newProjectForm.get('type').setValue(this.projectTypes[0]);
}
}
getProjectTypes() {
this.projectService.getProjectTypes().subscribe( projectsTypes => {
this.projectTypes = projectsTypes;
if (this.projectTypes.length > 0) {
this.newProjectForm.get('type').setValue(this.projectTypes[0]);
}
});
}
createProject() {
const formModel = this.newProjectForm.value;
this.project.name = formModel.name as string;
this.project.description = formModel.description as string;
this.project.type = formModel.type as ProjectType;
this.projectService.createProject(this.project).subscribe(
response => {
if (response.status === 200) {
this.alertService.success('New Project created successfully');
} else {
this.alertService.error('Error creating the new project');
}
},
error => {
this.alertService.error('Error creating the new project');
}
);
}
}
import { Injectable } from '@angular/core';
import { HttpClient, HttpHeaders, HttpResponse } from '@angular/common/http';
import { Observable } from 'rxjs/Observable';
import { ProjectType, Project } from '../models/project';
import { catchError } from 'rxjs/operators';
import { of } from 'rxjs/observable/of';
const httpOptions = {
headers: new HttpHeaders({ 'Content-Type': 'application/json' })
};
@Injectable()
export class ProjectService {
// URL to web api
private projectsUrl = 'api/projects';
private projectTypesUrl = 'api/projects/types';
constructor(private http: HttpClient) { }
/** GET project types */
getProjectTypes(): Observable<ProjectType[]> {
return this.http.get<ProjectType[]>(this.projectTypesUrl, httpOptions)
.pipe(
catchError(this.handleError('getProjectsType', []))
);
}
/** GET projects */
getProjects(): Observable<Project[]> {
return this.http.get<Project[]>(this.projectsUrl, httpOptions)
.pipe(
catchError(this.handleError('getProjects', []))
);
}
/** POST new project */
createProject(project: Project): Observable<HttpResponse<Project>> {
return this.http.post<Project>(this.projectsUrl, project, {
headers: new HttpHeaders({ 'Content-Type': 'application/json' }),
observe: 'response'})
.pipe(
catchError(this.handleError('createProject', null))
);
}
/**
* Handle Http operation that failed.
* Let the app continue.
* @param operation - name of the operation that failed
* @param result - optional value to return as the observable result
*/
private handleError<T> (operation = 'operation', result?: T) {
return (error: any): Observable<T> => {
console.error(operation + ': ' + error); // log to console instead
// Let the app keep running by returning an empty result.
return of(result as T);
};
}
}
#projects-container {
margin-top: 2em;
margin-bottom: 2em;
}
<h1>Your Projects</h1>
<div id="projects-container">
<div *ngIf="loading">
<i class='fa fa-spinner fa-spin'></i> Loading...
</div>
<div *ngIf="!loading && projects.length == 0">
You don't have any project yet, create one!
</div>
<div class="row">
<div class="col-md-4" *ngFor="let project of projects">
<div class="card border-primary mb-3" >
<div class="card-body text-dark">
<a routerLink="/projects/{{project.id}}">
<h4 class="card-title">{{ project.name }}</h4>
</a>
<p class="card-text">{{ project.description }}</p>
<p class="card-text"><b>Type:</b> {{ project.type.name }}</p>
</div>
<div class="card-footer text-right">
<a href="#" class="card-link text-primary">Edit</a>
</div>
</div>
</div>
</div>
</div>
<app-project-dialog></app-project-dialog>
import { Component, OnInit } from '@angular/core';
import { ProjectService } from './project.service';
import { Project } from '../models/project';
@Component({
selector: 'app-projects',
templateUrl: './projects.component.html',
styleUrls: ['./projects.component.css']
})
export class ProjectsComponent implements OnInit {
projects: Project[];
loading = true;
constructor(private projectService: ProjectService) { }
ngOnInit() {
this.getProjects();
}
getProjects() {
this.projectService.getProjects().subscribe( projects => {
this.projects = projects;
this.loading = false;
});
}
}
/* You can add global styles to this file, and also import other style files */
.modal-backdrop {
opacity: .5;
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment