Commit bf4a9e1d by Harangozó Péter

users & rights crud

parent ca671bbf
......@@ -19,7 +19,8 @@
"testTsconfig": "tsconfig.spec.json",
"prefix": "app",
"styles": [
"styles.less"
"styles.less",
"./assets/css/ng2-select.css"
],
"scripts": [],
"environmentSource": "environments/environment.ts",
......
......@@ -22,6 +22,7 @@
"@angular/router": "^3.4.0",
"@ng-bootstrap/ng-bootstrap": "^1.0.0-alpha.21",
"core-js": "^2.4.1",
"ng2-select": "^1.2.0",
"rxjs": "^5.1.0",
"zone.js": "^0.7.6"
},
......@@ -36,9 +37,9 @@
"karma": "~1.4.1",
"karma-chrome-launcher": "~2.0.0",
"karma-cli": "~1.0.1",
"karma-coverage-istanbul-reporter": "^0.2.0",
"karma-jasmine": "~1.1.0",
"karma-jasmine-html-reporter": "^0.2.2",
"karma-coverage-istanbul-reporter": "^0.2.0",
"protractor": "~5.1.0",
"ts-node": "~2.0.0",
"tslint": "~4.5.0",
......
//built in angular modules
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { HttpModule } from '@angular/http';
//external modules
import { NgbModule } from '@ng-bootstrap/ng-bootstrap';
import { SelectModule } from 'ng2-select';
//own modules
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { UsersComponent } from './modules/users/users.component';
......@@ -27,7 +32,8 @@ import { RightService } from './modules/rights/services/right.service';
FormsModule,
HttpModule,
AppRoutingModule,
NgbModule.forRoot()
NgbModule.forRoot(),
SelectModule,
],
providers: [UserService, RightService],
bootstrap: [AppComponent]
......
export class AppSettings {
public static API_ENDPOINT='http://192.168.50.183:1337/api/';
}
\ No newline at end of file
export class Right {
id: string;
Name: string;
}
......@@ -50,8 +50,13 @@ export class RightComponent implements OnInit {
}
save(): void {
this.rightService.createRight(this.right)
.then(() => this.goBack());
if (this.right.id)
this.rightService.updateRight(this.right)
.then(() => this.goBack());
else
this.rightService.createRight(this.right)
.then(() => this.goBack());
}
goBack(): void {
......
......@@ -17,7 +17,7 @@
<td>
<button type="button" class="btn btn-sm btn-outline-info" (click)="details(right.id)">Details</button>
<button type="button" class="btn btn-sm btn-outline-warning" (click)="edit(right.id)">Edit</button>
<button type="button" class="btn btn-sm btn-outline-danger">Delete</button>
<button type="button" class="btn btn-sm btn-outline-danger" (click)="delete(right.id)">Delete</button>
</td>
</tr>
</tbody>
......
......@@ -33,4 +33,8 @@ export class RightsComponent implements OnInit {
this.router.navigate(['/rights/edit', id]);
}
delete(id): void {
this.rightService.deleteRight(id).then(() => this.getRights());
}
}
......@@ -3,20 +3,20 @@ import { Headers, Http } from '@angular/http';
import 'rxjs/add/operator/toPromise';
import { NgbDateParserFormatter } from '@ng-bootstrap/ng-bootstrap';
import { AppSettings } from '../../../app.settings';
import { Right } from '../models/right';
@Injectable()
export class RightService {
private headers = new Headers({ 'Content-Type': 'application/json' });
private rightsUrl = 'api/right'; // URL to web api
private rightsUrl = AppSettings.API_ENDPOINT + 'right'; // URL to web api
constructor(
private http: Http,
private parserFormatter: NgbDateParserFormatter
) { }
getRights(): Promise<Right[]> {
return this.http.get('http://192.168.50.183:1337/' + this.rightsUrl)
return this.http.get(this.rightsUrl + "/get-rights-for-combo")
.toPromise()
.then(response =>
response.json().rights as Right[])
......@@ -25,19 +25,34 @@ export class RightService {
getRight(id: number): Promise<Right> {
const url = `${this.rightsUrl}/${id}`;
return this.http.get('http://192.168.50.183:1337/' + url)
return this.http.get(url)
.toPromise()
.then(response => response.json().right as Right);
}
createRight(right: Right): Promise<Right> {
return this.http
.post('http://192.168.50.183:1337/' + this.rightsUrl, JSON.stringify(right), { headers: this.headers })
.post(this.rightsUrl, JSON.stringify(right), { headers: this.headers })
.toPromise()
.then(res => res.json().right)
.catch(this.handleError);
}
updateRight(right: Right): Promise<Right> {
return this.http
.put(this.rightsUrl, JSON.stringify(right), { headers: this.headers })
.toPromise()
.then(res => res.json().right)
.catch(this.handleError);
}
deleteRight(id: number): Promise<void> {
const url = `${this.rightsUrl}/${id}`;
return this.http.delete(url)
.toPromise()
.then(() => { });
}
private handleError(error: any): Promise<any> {
console.error('An error occurred', error);
return Promise.reject(error.message || error);
......
export class User {
id: number;
BirthDate: any;
FirstName: string;
LastName: string;
UserName: string;
Rights: any[];
}
......@@ -3,19 +3,20 @@ import { Headers, Http } from '@angular/http';
import 'rxjs/add/operator/toPromise';
import { NgbDateParserFormatter } from '@ng-bootstrap/ng-bootstrap';
import { AppSettings } from '../../../app.settings';
import { User } from '../models/user';
@Injectable()
export class UserService {
private headers = new Headers({ 'Content-Type': 'application/json' });
private usersUrl = 'api/user'; // URL to web api
private usersUrl = AppSettings.API_ENDPOINT + 'user'; // URL to web api
constructor(
private http: Http,
private parserFormatter: NgbDateParserFormatter
) { }
getUsers(): Promise<User[]> {
return this.http.get('http://192.168.50.183:1337/' + this.usersUrl)
return this.http.get(this.usersUrl)
.toPromise()
.then(response =>
response.json().users as User[])
......@@ -24,7 +25,7 @@ export class UserService {
getUser(id: number): Promise<User> {
const url = `${this.usersUrl}/${id}`;
return this.http.get('http://192.168.50.183:1337/' + url)
return this.http.get(url)
.toPromise()
.then(response => {
let user = response.json().user as User;
......@@ -33,15 +34,33 @@ export class UserService {
});
}
deleteUser(id: number): Promise<void> {
const url = `${this.usersUrl}/${id}`;
return this.http.delete(url)
.toPromise()
.then(()=> {});
}
createUser(user: User): Promise<User> {
user.BirthDate = this.parserFormatter.format(user.BirthDate);
return this.http
.post('http://192.168.50.183:1337/' + this.usersUrl, JSON.stringify(user), { headers: this.headers })
.post(this.usersUrl, JSON.stringify(user), { headers: this.headers })
.toPromise()
.then(res => res.json().user)
.catch(this.handleError);
}
updateUser(user: User): Promise<User> {
user.BirthDate = this.parserFormatter.format(user.BirthDate);
return this.http
.put(this.usersUrl, JSON.stringify(user), { headers: this.headers })
.toPromise()
.then(res => {
return res.json().user;
})
.catch(this.handleError);
}
private handleError(error: any): Promise<any> {
console.error('An error occurred', error);
return Promise.reject(error.message || error);
......
<h2>User - {{title}}</h2>
<hr>
<button type="button" class="btn btn-sm btn-outline-primary" (click)="goBack()">Back</button>
<button type="button" class="btn btn-sm btn-outline-success float-right" (click)="save()" [hidden]="!editMode">Save</button>
<hr>
<div *ngIf="editMode">
<div class="form-group row">
<label for="first-name" class="col-2 col-form-label">Firstname</label>
<div class="col-10">
<input id="first-name" class="form-control" type="text" [(ngModel)]="user.FirstName" placeholder="Firstname" />
<div class="user-page">
<h2>User - {{title}}</h2>
<hr>
<button type="button" class="btn btn-sm btn-outline-primary" (click)="goBack()">Back</button>
<button type="button" class="btn btn-sm btn-outline-success float-right" (click)="save()" [hidden]="!editMode">Save</button>
<hr>
<div *ngIf="editMode">
<div class="form-group row">
<label for="first-name" class="col-2 col-form-label">Firstname</label>
<div class="col-10">
<input id="first-name" class="form-control" type="text" [(ngModel)]="user.FirstName" placeholder="Firstname" />
</div>
</div>
</div>
<div class="form-group row">
<label for="last-name" class="col-2 col-form-label">Lastname</label>
<div class="col-10">
<input id="last-name" class="form-control" type="text" [(ngModel)]="user.LastName" placeholder="Lastname" />
<div class="form-group row">
<label for="last-name" class="col-2 col-form-label">Lastname</label>
<div class="col-10">
<input id="last-name" class="form-control" type="text" [(ngModel)]="user.LastName" placeholder="Lastname" />
</div>
</div>
</div>
<div class="form-group row">
<label for="user-name" class="col-2 col-form-label">Username</label>
<div class="col-10">
<input id="user-name" class="form-control" type="text" [(ngModel)]="user.UserName" placeholder="Username" />
<div class="form-group row">
<label for="user-name" class="col-2 col-form-label">Username</label>
<div class="col-10">
<input id="user-name" class="form-control" type="text" [(ngModel)]="user.UserName" placeholder="Username" />
</div>
</div>
</div>
<div class="form-group row">
<label for="birthdate" class="col-2 col-form-label">Birthdate</label>
<div class="col-10 input-group">
<input class="form-control" placeholder="yyyy-mm-dd" name="dp" [(ngModel)]="user.BirthDate" ngbDatepicker #d="ngbDatepicker">
<div class="input-group-addon" (click)="d.toggle()">
<i class="fa fa-6 fa-calendar" aria-hidden="true" style="width: 1.2rem; height: 1rem; cursor: pointer;"></i>
<div class="form-group row">
<label for="birthdate" class="col-2 col-form-label">Birthdate</label>
<div class="col-10 input-group">
<input class="form-control" placeholder="yyyy-mm-dd" name="dp" [(ngModel)]="user.BirthDate" ngbDatepicker #d="ngbDatepicker">
<div class="input-group-addon" (click)="d.toggle()">
<i class="fa fa-6 fa-calendar" aria-hidden="true" style="width: 1.2rem; height: 1rem; cursor: pointer;"></i>
</div>
</div>
</div>
<div class="form-group row">
<label for="birthdate" class="col-2 col-form-label">Rights</label>
<div class="col-10">
<ng-select
[items]="rights"
[multiple]="true"
(selected)="selectedRight($event)"
(removed)="removedRight($event)"
[active]="user.Rights"
placeholder="No rights selected"></ng-select>
</div>
</div>
</div>
</div>
<div *ngIf="!editMode">
<div class="form-group row">
<label for="first-name" class="col-2 col-form-label">Firstname</label>
<div class="col-10">
<p class="form-control-static">
{{user.FirstName}}
</p>
<div *ngIf="!editMode">
<div class="form-group row">
<label for="first-name" class="col-2 col-form-label">Firstname</label>
<div class="col-10">
<p class="form-control-static">
{{user.FirstName}}
</p>
</div>
</div>
</div>
<div class="form-group row">
<label for="last-name" class="col-2 col-form-label">Lastname</label>
<div class="col-10">
<p class="form-control-static">
{{user.LastName}}
</p>
<div class="form-group row">
<label for="last-name" class="col-2 col-form-label">Lastname</label>
<div class="col-10">
<p class="form-control-static">
{{user.LastName}}
</p>
</div>
</div>
</div>
<div class="form-group row">
<label for="user-name" class="col-2 col-form-label">Username</label>
<div class="col-10">
<p class="form-control-static">
{{user.UserName}}
</p>
<div class="form-group row">
<label for="user-name" class="col-2 col-form-label">Username</label>
<div class="col-10">
<p class="form-control-static">
{{user.UserName}}
</p>
</div>
</div>
</div>
<div class="form-group row">
<label for="birthdate" class="col-2 col-form-label">Birthdate</label>
<div class="col-10 input-group">
<p class="form-control-static">
{{parserFormatter.format(user.BirthDate)}}
</p>
<div class="form-group row">
<label for="birthdate" class="col-2 col-form-label">Birthdate</label>
<div class="col-10 input-group">
<p class="form-control-static">
{{parserFormatter.format(user.BirthDate)}}
</p>
</div>
</div>
</div>
</div>
\ No newline at end of file
.user-page{
table{
max-width: 80%;
}
}
\ No newline at end of file
......@@ -5,7 +5,9 @@ import { Location } from '@angular/common';
import { NgbDateParserFormatter } from '@ng-bootstrap/ng-bootstrap';
import { User } from '../models/user';
import { Right } from '../../rights/models/right';
import { UserService } from '../services/user.service'
import { RightService } from '../../rights/services/right.service'
@Component({
selector: 'app-user',
......@@ -16,16 +18,33 @@ export class UserComponent implements OnInit {
user: User;
editMode: boolean;
title: string;
rights: any[];
constructor(
private userService: UserService,
private rightService: RightService,
private route: ActivatedRoute,
private location: Location,
private parserFormatter: NgbDateParserFormatter
) { }
selectedRight(selected): void {
this.user.Rights = this.user.Rights || [];
this.user.Rights.push(selected);
}
removedRight(selected): void {
this.user.Rights = this.user.Rights || [];
var found = this.user.Rights.find(u => u.id == selected.id);
var index = this.user.Rights.indexOf(found);
if(index > -1)
this.user.Rights.splice(index, 1);
}
ngOnInit(): void {
this.rightService.getRights().then(rights => this.rights = rights);
this.user = new User();
this.editMode = true;
this.route.params.subscribe(params => {
......@@ -33,6 +52,7 @@ export class UserComponent implements OnInit {
this.userService.getUser(params['id'])
.then(user => {
this.user = user
this.user.Rights = this.user.Rights || [];
});
}
if (this.location.path().indexOf('details') > -1) {
......@@ -45,15 +65,19 @@ export class UserComponent implements OnInit {
else {
this.title = "Create";
this.user = new User();
this.user.Rights = this.user.Rights || [];
}
}
});
}
save(): void {
this.userService.createUser(this.user)
.then(() => this.goBack());
if (this.user.id)
this.userService.updateUser(this.user)
.then(() => this.goBack());
else
this.userService.createUser(this.user)
.then(() => this.goBack());
}
goBack(): void {
......
......@@ -21,7 +21,7 @@
<td>
<button type="button" class="btn btn-sm btn-outline-info" (click)="details(user.id)">Details</button>
<button type="button" class="btn btn-sm btn-outline-warning" (click)="edit(user.id)">Edit</button>
<button type="button" class="btn btn-sm btn-outline-danger">Delete</button>
<button type="button" class="btn btn-sm btn-outline-danger" (click)="delete(user.id)">Delete</button>
</td>
</tr>
</tbody>
......
......@@ -37,4 +37,8 @@ export class UsersComponent implements OnInit {
this.router.navigate(['/users/edit', id]);
}
delete(id): void {
this.userService.deleteUser(id).then(()=>this.getUsers());
}
}
/* Style when highlighting a search. */
.ui-select-highlight {
font-weight: bold;
}
.ui-select-offscreen {
clip: rect(0 0 0 0) !important;
width: 1px !important;
height: 1px !important;
border: 0 !important;
margin: 0 !important;
padding: 0 !important;
overflow: hidden !important;
position: absolute !important;
outline: 0 !important;
left: 0px !important;
top: 0px !important;
}
.ui-select-choices-row:hover {
background-color: #f5f5f5;
}
/* Select2 theme */
/* Mark invalid Select2 */
.ng-dirty.ng-invalid > a.select2-choice {
border-color: #D44950;
}
.select2-result-single {
padding-left: 0;
}
.select2-locked > .select2-search-choice-close{
display:none;
}
.select-locked > .ui-select-match-close{
display:none;
}
body > .select2-container.open {
z-index: 9999; /* The z-index Select2 applies to the select2-drop */
}
/* Handle up direction Select2 */
.ui-select-container[theme="select2"].direction-up .ui-select-match {
border-radius: 4px; /* FIXME hardcoded value :-/ */
border-top-left-radius: 0;
border-top-right-radius: 0;
}
.ui-select-container[theme="select2"].direction-up .ui-select-dropdown {
border-radius: 4px; /* FIXME hardcoded value :-/ */
border-bottom-left-radius: 0;
border-bottom-right-radius: 0;
border-top-width: 1px; /* FIXME hardcoded value :-/ */
border-top-style: solid;
box-shadow: 0 -4px 8px rgba(0, 0, 0, 0.25);
margin-top: -4px; /* FIXME hardcoded value :-/ */
}
.ui-select-container[theme="select2"].direction-up .ui-select-dropdown .select2-search {
margin-top: 4px; /* FIXME hardcoded value :-/ */
}
.ui-select-container[theme="select2"].direction-up.select2-dropdown-open .ui-select-match {
border-bottom-color: #5897fb;
}
/* Selectize theme */
/* Helper class to show styles when focus */
.selectize-input.selectize-focus{
border-color: #007FBB !important;
}
/* Fix input width for Selectize theme */
.selectize-control > .selectize-input > input {
width: 100%;
}
/* Fix dropdown width for Selectize theme */
.selectize-control > .selectize-dropdown {
width: 100%;
}
/* Mark invalid Selectize */
.ng-dirty.ng-invalid > div.selectize-input {
border-color: #D44950;
}
/* Handle up direction Selectize */
.ui-select-container[theme="selectize"].direction-up .ui-select-dropdown {
box-shadow: 0 -4px 8px rgba(0, 0, 0, 0.25);
margin-top: -2px; /* FIXME hardcoded value :-/ */
}
/* Bootstrap theme */
/* Helper class to show styles when focus */
.btn-default-focus {
color: #333;
background-color: #EBEBEB;
border-color: #ADADAD;
text-decoration: none;
outline: 5px auto -webkit-focus-ring-color;
outline-offset: -2px;
box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(102, 175, 233, 0.6);
}
.ui-select-bootstrap .ui-select-toggle {
position: relative;
}
.ui-select-bootstrap .ui-select-toggle > .caret {
position: absolute;
height: 10px;
top: 50%;
right: 10px;
margin-top: -2px;
}
/* Fix Bootstrap dropdown position when inside a input-group */
.input-group > .ui-select-bootstrap.dropdown {
/* Instead of relative */
position: static;
}
.input-group > .ui-select-bootstrap > input.ui-select-search.form-control {
border-radius: 4px; /* FIXME hardcoded value :-/ */
border-top-right-radius: 0;
border-bottom-right-radius: 0;
}
.input-group > .ui-select-bootstrap > input.ui-select-search.form-control.direction-up {
border-radius: 4px !important; /* FIXME hardcoded value :-/ */
border-top-right-radius: 0 !important;
border-bottom-right-radius: 0 !important;
}
.ui-select-bootstrap > .ui-select-match > .btn{
/* Instead of center because of .btn */
text-align: left !important;
}
.ui-select-bootstrap > .ui-select-match > .caret {
position: absolute;
top: 45%;
right: 15px;
}
.ui-disabled {
background-color: #eceeef ;
border-radius: 4px;
position: absolute;
width: 100%;
height: 100%;
z-index: 5;
opacity: 0.6;
top: 0;
left: 0;
cursor: not-allowed;
}
/* See Scrollable Menu with Bootstrap 3 http://stackoverflow.com/questions/19227496 */
.ui-select-bootstrap > .ui-select-choices {
width: 100%;
height: auto;
max-height: 200px;
overflow-x: hidden;
margin-top: -1px;
}
body > .ui-select-bootstrap.open {
z-index: 1000; /* Standard Bootstrap dropdown z-index */
}
.ui-select-multiple.ui-select-bootstrap {
height: auto;
padding: 3px 3px 0 3px;
}
.ui-select-multiple.ui-select-bootstrap input.ui-select-search {
background-color: transparent !important; /* To prevent double background when disabled */
border: none;
outline: none;
height: 1.666666em;
margin-bottom: 3px;
}
.ui-select-multiple.ui-select-bootstrap .ui-select-match .close {
font-size: 1.6em;
line-height: 0.75;
}
.ui-select-multiple.ui-select-bootstrap .ui-select-match-item {
outline: 0;
margin: 0 3px 3px 0;
}
.ui-select-multiple .ui-select-match-item {
position: relative;
}
.ui-select-multiple .ui-select-match-item.dropping-before:before {
content: "";
position: absolute;
top: 0;
right: 100%;
height: 100%;
margin-right: 2px;
border-left: 1px solid #428bca;
}
.ui-select-multiple .ui-select-match-item.dropping-after:after {
content: "";
position: absolute;
top: 0;
left: 100%;
height: 100%;
margin-left: 2px;
border-right: 1px solid #428bca;
}
.ui-select-bootstrap .ui-select-choices-row>a {
display: block;
padding: 3px 20px;
clear: both;
font-weight: 400;
line-height: 1.42857143;
color: #333;
white-space: nowrap;
}
.ui-select-bootstrap .ui-select-choices-row>a:hover, .ui-select-bootstrap .ui-select-choices-row>a:focus {
text-decoration: none;
color: #262626;
background-color: #f5f5f5;
}
.ui-select-bootstrap .ui-select-choices-row.active>a {
color: #fff;
text-decoration: none;
outline: 0;
background-color: #428bca;
}
.ui-select-bootstrap .ui-select-choices-row.disabled>a,
.ui-select-bootstrap .ui-select-choices-row.active.disabled>a {
color: #777;
cursor: not-allowed;
background-color: #fff;
}
/* fix hide/show angular animation */
.ui-select-match.ng-hide-add,
.ui-select-search.ng-hide-add {
display: none !important;
}
/* Mark invalid Bootstrap */
.ui-select-bootstrap.ng-dirty.ng-invalid > button.btn.ui-select-match {
border-color: #D44950;
}
/* Handle up direction Bootstrap */
.ui-select-container[theme="bootstrap"].direction-up .ui-select-dropdown {
box-shadow: 0 -4px 8px rgba(0, 0, 0, 0.25);
}
\ No newline at end of file
/* You can add global styles to this file, and also import other style files */
ng-select{
.dropdown-menu{
display: block!important;
}
}
.btn-group-xs>.btn, .btn-xs {
padding: .25rem .5rem;
font-size: .875rem;
border-radius: .2rem;
}
\ No newline at end of file
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