Skip to content
Snippets Groups Projects
Commit 906019dd authored by François Agneray's avatar François Agneray
Browse files

Admin => add surveys and databases routes

parent e2ad7333
No related branches found
No related tags found
No related merge requests found
Showing
with 286 additions and 72 deletions
...@@ -2,13 +2,23 @@ import { NgModule } from '@angular/core'; ...@@ -2,13 +2,23 @@ import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router'; import { RouterModule, Routes } from '@angular/router';
import { AdminComponent } from './containers/admin.component'; import { AdminComponent } from './containers/admin.component';
import { InstanceListComponent } from './containers/instance-list.component'; import { InstanceListComponent } from './containers/instance/instance-list.component';
import { SurveyComponent } from './containers/survey/survey.component';
import { SurveyListComponent } from './containers/survey/survey-list.component';
import { DatabaseListComponent } from './containers/database/database-list.component';
const routes: Routes = [ const routes: Routes = [
{ {
path: 'admin', component: AdminComponent, children: [ path: 'admin', component: AdminComponent, children: [
{ path: '', redirectTo: 'instance-list', pathMatch: 'full' }, { path: '', redirectTo: 'instance-list', pathMatch: 'full' },
{ path: 'instance-list', component: InstanceListComponent } { path: 'instance-list', component: InstanceListComponent },
{
path: 'survey', component: SurveyComponent, children: [
{ path: '', redirectTo: 'survey-list', pathMatch: 'full' },
{ path: 'survey-list', component: SurveyListComponent },
{ path: 'database-list', component: DatabaseListComponent }
]
}
] ]
} }
]; ];
...@@ -21,5 +31,8 @@ export class AdminRoutingModule { } ...@@ -21,5 +31,8 @@ export class AdminRoutingModule { }
export const routedComponents = [ export const routedComponents = [
AdminComponent, AdminComponent,
InstanceListComponent InstanceListComponent,
SurveyComponent,
SurveyListComponent,
DatabaseListComponent
]; ];
/**
* This file is part of Anis Client.
*
* @copyright Laboratoire d'Astrophysique de Marseille / CNRS
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
.dropdown-up {
top: 80% !important;
right: 5px !important;
}
img {
height: 60px;
}
import { Component, Input, Output, EventEmitter, ChangeDetectionStrategy } from '@angular/core';
import { UserProfile } from 'src/app/auth/user-profile.model';
import { environment } from 'src/environments/environment'
@Component({
selector: 'app-admin-nav',
templateUrl: 'admin-nav.component.html',
styleUrls: [ 'admin-nav.component.scss' ],
changeDetection: ChangeDetectionStrategy.OnPush
})
export class AdminNavComponent {
@Input() isAuthenticated: boolean;
@Input() userProfile: UserProfile = null;
@Output() login: EventEmitter<any> = new EventEmitter();
@Output() logout: EventEmitter<any> = new EventEmitter();
@Output() openEditProfile: EventEmitter<any> = new EventEmitter();
baseHref: string = environment.baseHref;
authenticationEnabled: boolean = environment.authenticationEnabled;
}
<div class="table-responsive">
<table class="table table-striped">
<thead>
<tr>
<th scope="col">ID</th>
<th scope="col">Label</th>
<th scope="col">Name</th>
<th scope="col">Type</th>
<th scope="col">Host</th>
<th scope="col">Port</th>
<th scope="col">Login</th>
<th scope="col">Password</th>
<th scope="col">Nb surveys</th>
<th scope="col">Edit</th>
<th scope="col">Delete</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let database of databaseList">
<td class="align-middle">{{ database.id }}</td>
<td class="align-middle">{{ database.label }}</td>
<td class="align-middle">{{ database.dbname }}</td>
<td class="align-middle">{{ database.dbtype }}</td>
<td class="align-middle">{{ database.dbhost }}</td>
<td class="align-middle">{{ database.dbport }}</td>
<td class="align-middle">{{ database.dblogin }}</td>
<td class="align-middle">*******</td>
<td class="align-middle">{{ getNbSurveyByDatabase(database.id) }}</td>
<td class="align-middle">
<a title="Edit this database" routerLink="/edit-database/{{database.id}}" class="btn btn-outline-primary">
<span class="fas fa-edit"></span>
</a>
</td>
<td class="align-middle">
<app-delete-btn
[disabled]="!isNoSurveyAttachedToDatabase(database.id)"
[type]="'database'"
[label]="database.label"
(confirm)="deleteDatabase.emit(database)">
</app-delete-btn>
</td>
</tr>
</tbody>
</table>
</div>
import { Component, Input, Output, ChangeDetectionStrategy, EventEmitter, TemplateRef } from '@angular/core';
import { Database, Survey } from 'src/app/metamodel/store/models';
@Component({
selector: 'app-database-table',
templateUrl: 'database-table.component.html',
changeDetection: ChangeDetectionStrategy.OnPush
})
export class DatabaseTableComponent {
@Input() databaseList: Database[];
@Input() surveyList: Survey[];
@Output() deleteDatabase: EventEmitter<Database> = new EventEmitter();
isNoSurveyAttachedToDatabase(idDatabase: number): boolean {
return this.getNbSurveyByDatabase(idDatabase) === 0;
}
getNbSurveyByDatabase(idDatabase: number): number {
return this.surveyList.filter(p => p.id_database === idDatabase).length
}
}
import { AdminNavComponent } from "./admin-nav.component"; import { InstanceCardComponent } from "./instance/instance-card.component";
import { InstanceCardComponent } from "./instance-card.component"; import { DeleteBtnComponent } from "./shared/delete-btn.component";
import { SurveyTableComponent } from "./survey/survey-table.component";
import { DatabaseTableComponent } from "./database/database-table.component";
export const dummiesComponents = [ export const dummiesComponents = [
AdminNavComponent, InstanceCardComponent,
InstanceCardComponent DeleteBtnComponent,
SurveyTableComponent,
DatabaseTableComponent
]; ];
...@@ -16,22 +16,10 @@ ...@@ -16,22 +16,10 @@
<span class="fas fa-edit"></span> <span class="fas fa-edit"></span>
</a> </a>
&nbsp; &nbsp;
<button title="Delete this instance" (click)="openModal(template, instance); $event.stopPropagation()" class="btn btn-outline-danger"> <app-delete-btn
<span class="fas fa-trash-alt"></span> [type]="'instance'"
</button> [label]="instance.label"
(confirm)="deleteInstance.emit(instance)">
</app-delete-btn>
</div> </div>
</div> </div>
<ng-template #template>
<div class="modal-header">
<h4 class="modal-title pull-left">Confirm</h4>
</div>
<div class="modal-body">
<p>Are you sure you want to delete this instance : <strong>{{ instanceForDel.label }}</strong> ?</p>
<p>
<button (click)="modalRef.hide()" class="btn btn-outline-primary">No</button>
&nbsp;
<button (click)="confirmDel()" class="btn btn-outline-danger">Yes</button>
</p>
</div>
</ng-template>
import { Component, Input, Output, ChangeDetectionStrategy, EventEmitter, TemplateRef } from '@angular/core'; import { Component, Input, Output, ChangeDetectionStrategy, EventEmitter } from '@angular/core';
import { BsModalService } from 'ngx-bootstrap/modal';
import { BsModalRef } from 'ngx-bootstrap/modal/bs-modal-ref.service';
import { Instance } from 'src/app/metamodel/store/models'; import { Instance } from 'src/app/metamodel/store/models';
...@@ -14,19 +11,4 @@ import { Instance } from 'src/app/metamodel/store/models'; ...@@ -14,19 +11,4 @@ import { Instance } from 'src/app/metamodel/store/models';
export class InstanceCardComponent { export class InstanceCardComponent {
@Input() instance: Instance; @Input() instance: Instance;
@Output() deleteInstance: EventEmitter<Instance> = new EventEmitter(); @Output() deleteInstance: EventEmitter<Instance> = new EventEmitter();
modalRef: BsModalRef;
instanceForDel: Instance;
constructor(private modalService: BsModalService) { }
openModal(template: TemplateRef<any>, instance: Instance) {
this.instanceForDel = instance;
this.modalRef = this.modalService.show(template);
}
confirmDel() {
this.deleteInstance.emit(this.instanceForDel);
this.modalRef.hide();
}
} }
<button [disabled]="disabled" title="Delete this {{ type }}" (click)="openModal(template); $event.stopPropagation()" class="btn btn-outline-danger">
<span class="fas fa-trash-alt"></span>
</button>
<ng-template #template>
<div class="modal-header">
<h4 class="modal-title pull-left">Confirm</h4>
</div>
<div class="modal-body">
<p>Are you sure you want to delete this {{ type }} : <strong>{{ label }}</strong> ?</p>
<p>
<button (click)="this.abort.emit(); this.modalRef.hide()" class="btn btn-outline-primary">No</button>
&nbsp;
<button (click)="this.confirm.emit(); this.modalRef.hide()" class="btn btn-outline-danger">Yes</button>
</p>
</div>
</ng-template>
import { Component, Input, Output, ChangeDetectionStrategy, EventEmitter, TemplateRef } from '@angular/core';
import { BsModalService } from 'ngx-bootstrap/modal';
import { BsModalRef } from 'ngx-bootstrap/modal/bs-modal-ref.service';
@Component({
selector: 'app-delete-btn',
templateUrl: 'delete-btn.component.html',
changeDetection: ChangeDetectionStrategy.OnPush
})
export class DeleteBtnComponent {
@Input() type: string;
@Input() label: string;
@Input() disabled: boolean = false;
@Output() confirm: EventEmitter<{}> = new EventEmitter();
@Output() abort: EventEmitter<{}> = new EventEmitter();
modalRef: BsModalRef;
constructor(private modalService: BsModalService) { }
openModal(template: TemplateRef<any>) {
this.modalRef = this.modalService.show(template);
}
}
<div class="table-responsive">
<table class="table table-striped">
<thead>
<tr>
<th scope="col">Name</th>
<th scope="col">Label</th>
<th scope="col">Description</th>
<th scope="col">Link</th>
<th scope="col">Manager</th>
<th scope="col">Database</th>
<th scope="col">Nb datasets</th>
<th scope="col">Edit</th>
<th scope="col">Delete</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let survey of surveyList">
<td class="align-middle">{{ survey.name }}</td>
<td class="align-middle">{{ survey.label }}</td>
<td class="align-middle">{{ survey.description }}</td>
<td class="align-middle"><a [href]="survey.link" target="_blank">{{ survey.link }}</a></td>
<td class="align-middle">{{ survey.manager }}</td>
<td class="align-middle">{{ getDatabaseById(survey.id_database).label }}</td>
<td class="align-middle">{{ survey.nb_datasets }}</td>
<td class="align-middle">
<a title="Edit this survey" routerLink="/edit-survey/{{survey.name}}" class="btn btn-outline-primary">
<span class="fas fa-edit"></span>
</a>
</td>
<td class="align-middle">
<app-delete-btn
[type]="'survey'"
[label]="survey.label"
(confirm)="deleteSurvey.emit(survey)">
</app-delete-btn>
</td>
</tr>
</tbody>
</table>
</div>
import { Component, Input, Output, ChangeDetectionStrategy, EventEmitter } from '@angular/core';
import { Survey, Database } from 'src/app/metamodel/store/models';
@Component({
selector: 'app-survey-table',
templateUrl: 'survey-table.component.html',
changeDetection: ChangeDetectionStrategy.OnPush
})
export class SurveyTableComponent {
@Input() surveyList: Survey[];
@Input() databaseList: Database[];
@Output() deleteSurvey: EventEmitter<Survey> = new EventEmitter();
getDatabaseById(idDatabase: number): Database {
return this.databaseList.find(database => database.id === idDatabase);
}
}
<header> <header>
<app-admin-nav <app-navbar
[links]="links"
[isAuthenticated]="isAuthenticated | async" [isAuthenticated]="isAuthenticated | async"
[userProfile]="userProfile | async" [userProfile]="userProfile | async"
(login)="login()" (login)="login()"
(logout)="logout()" (logout)="logout()"
(openEditProfile)="openEditProfile()"> (openEditProfile)="openEditProfile()">
</app-admin-nav> </app-navbar>
</header> </header>
<main role="main" class="container-fluid pb-4"> <main role="main" class="container-fluid pb-4">
<router-outlet></router-outlet> <router-outlet></router-outlet>
......
...@@ -26,6 +26,12 @@ import * as authSelector from 'src/app/auth/auth.selector'; ...@@ -26,6 +26,12 @@ import * as authSelector from 'src/app/auth/auth.selector';
* @implements OnInit * @implements OnInit
*/ */
export class AdminComponent { export class AdminComponent {
public links = [
{ label: 'Portal', icon: 'fas fa-level-up-alt', routerLink: '/portal-home' },
{ label: 'Instances', icon: 'fas fa-tools', routerLink: 'instance-list' },
{ label: 'Surveys', icon: 'fas fa-database', routerLink: 'survey'},
{ label: 'Settings', icon: 'fas fa-wrench', routerLink: 'settings'}
];
public isAuthenticated: Observable<boolean>; public isAuthenticated: Observable<boolean>;
public userProfile: Observable<UserProfile>; public userProfile: Observable<UserProfile>;
public userRoles: Observable<string[]>; public userRoles: Observable<string[]>;
......
<app-spinner *ngIf="(surveyListIsLoading | async) || (databaseListIsLoading | async)"></app-spinner>
<div *ngIf="(surveyListIsLoaded | async) && (databaseListIsLoaded | async)">
<div class="row">
<div class="col-12">
<button title="Add a new database" class="btn btn-outline-success float-right" routerLink="/new-database">
<span class="fas fa-plus"></span> New database
</button>
</div>
</div>
<div class="row mt-1">
<div class="col-12">
<app-database-table
[databaseList]="databaseList | async"
[surveyList]="surveyList | async"
(deleteDatabase)="deleteDatabase($event)">
</app-database-table>
</div>
</div>
</div>
import { Component, OnInit } from '@angular/core';
import { Observable } from 'rxjs';
import { Store } from '@ngrx/store';
import { Database, Survey } from 'src/app/metamodel/store/models';
import * as databaseActions from 'src/app/metamodel/store/actions/database.actions';
import * as surveyActions from 'src/app/metamodel/store/actions/survey.actions';
import * as databaseSelector from 'src/app/metamodel/store/selectors/database.selector';
import * as surveySelector from 'src/app/metamodel/store/selectors/survey.selector';
@Component({
selector: 'app-database-list',
templateUrl: 'database-list.component.html'
})
export class DatabaseListComponent implements OnInit {
public databaseListIsLoading: Observable<boolean>;
public databaseListIsLoaded: Observable<boolean>;
public databaseList: Observable<Database[]>;
public surveyListIsLoading: Observable<boolean>;
public surveyListIsLoaded: Observable<boolean>;
public surveyList: Observable<Survey[]>;
constructor(private store: Store<{ }>) {
this.databaseListIsLoading = store.select(databaseSelector.selectDatabaseListIsLoading);
this.databaseListIsLoaded = store.select(databaseSelector.selectDatabaseListIsLoaded);
this.databaseList = store.select(databaseSelector.selectAllDatabases);
this.surveyListIsLoading = store.select(surveySelector.selectSurveyListIsLoading);
this.surveyListIsLoaded = store.select(surveySelector.selectSurveyListIsLoaded);
this.surveyList = store.select(surveySelector.selectAllSurveys);
}
ngOnInit() {
this.store.dispatch(databaseActions.loadDatabaseList());
this.store.dispatch(surveyActions.loadSurveyList());
}
deleteDatabase(database: Database) {
// this.store.dispatch(new databaseActions.DeleteDatabaseAction(database));
}
}
...@@ -7,15 +7,13 @@ ...@@ -7,15 +7,13 @@
</div> </div>
<div class="container"> <div class="container">
<div *ngIf="instanceListIsLoading | async" class="row justify-content-center mt-5"> <app-spinner *ngIf="instanceListIsLoading | async"></app-spinner>
<span class="fas fa-circle-notch fa-spin fa-3x"></span>
<span class="sr-only">Loading...</span>
</div>
<div *ngIf="instanceListIsLoaded | async" class="row row-cols-1 row-cols-sm-2 row-cols-md-3"> <div *ngIf="instanceListIsLoaded | async" class="row row-cols-1 row-cols-sm-2 row-cols-md-3">
<app-instance-card <app-instance-card
*ngFor="let instance of (instanceList | async)" *ngFor="let instance of (instanceList | async)"
[instance]="instance"> [instance]="instance"
(deleteInstance)="deleteInstance($event)">
</app-instance-card> </app-instance-card>
<div class="col mb-3 h-100 d-table"> <div class="col mb-3 h-100 d-table">
<div routerLink="/new-instance" class="card card-add d-table-cell align-middle pointer" title="Add a new instance"> <div routerLink="/new-instance" class="card card-add d-table-cell align-middle pointer" title="Add a new instance">
......
...@@ -26,7 +26,7 @@ export class InstanceListComponent implements OnInit { ...@@ -26,7 +26,7 @@ export class InstanceListComponent implements OnInit {
this.store.dispatch(instanceActions.loadInstanceList()); this.store.dispatch(instanceActions.loadInstanceList());
} }
/* deleteInstance(instance: Instance) { deleteInstance(instance: Instance) {
this.store.dispatch(new instanceActions.DeleteInstanceAction(instance)); console.log(`Instance ${instance.name} deleted`);
} */ }
} }
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment