Management refactoring, creating unique entry in menu for management

This commit is contained in:
Sylvain MATHIEU 2022-01-05 15:13:31 +01:00
parent b8b7e4d151
commit 39627c28c8
16 changed files with 187 additions and 66 deletions

View File

@ -60,6 +60,7 @@ import {GroupManagementComponent} from "./components/group-management/group-mana
import {GroupDetailsComponent} from "@components/group-details/group-details.component";
import {UserDetailComponent} from "@components/user-management/user-detail/user-detail.component";
import {GroupDetailsResolver} from "@resolvers/group-details.resolver";
import {ManagementComponent} from "@components/management/management.component";
const routes: Routes = [
{
@ -81,13 +82,7 @@ const routes: Routes = [
{ path: 'settings', component: SettingsComponent },
{ path: 'settings/console', component: ConsoleComponent },
{
path: 'server/:server_id/user_management',
component: UserManagementComponent,
canActivate: [LoginGuard],
resolve: { server: ServerResolve },
},
{
path: 'server/:server_id/user_management/:user_id',
path: 'server/:server_id/management/users/:user_id',
component: UserDetailComponent,
canActivate: [LoginGuard],
resolve: { server: ServerResolve },
@ -206,8 +201,20 @@ const routes: Routes = [
{ path: 'server/:server_id/preferences/iou/templates/:template_id', component: IouTemplateDetailsComponent, canActivate: [LoginGuard] },
{ path: 'server/:server_id/preferences/iou/templates/:template_id/copy', component: CopyIouTemplateComponent, canActivate: [LoginGuard] },
{ path: 'server/:server_id/preferences/iou/addtemplate', component: AddIouTemplateComponent, canActivate: [LoginGuard] },
{ path: 'server/:server_id/group_management', component: GroupManagementComponent},
{ path: 'server/:server_id/group_management/:user_group_id', component: GroupDetailsComponent, resolve: {group: GroupDetailsResolver}},
{
path: 'server/:server_id/management',
component: ManagementComponent,
children: [
{
path: 'users',
component: UserManagementComponent
},
{
path: 'groups',
component: GroupManagementComponent
}
]},
{ path: 'server/:server_id/management/groups/:user_group_id', component: GroupDetailsComponent, resolve: {group: GroupDetailsResolver}},
],
},

View File

@ -286,6 +286,7 @@ import { AddUserToGroupDialogComponent } from './components/group-details/add-us
import { RemoveUserToGroupDialogComponent } from './components/group-details/remove-user-to-group-dialog/remove-user-to-group-dialog.component';
import { PaginatorPipe } from './components/group-details/paginator.pipe';
import { MembersFilterPipe } from './components/group-details/members-filter.pipe';
import { ManagementComponent } from './components/management/management.component';
@NgModule({
declarations: [
@ -486,7 +487,8 @@ import { MembersFilterPipe } from './components/group-details/members-filter.pip
AddUserToGroupDialogComponent,
RemoveUserToGroupDialogComponent,
PaginatorPipe,
MembersFilterPipe
MembersFilterPipe,
ManagementComponent
],
imports: [
BrowserModule,

View File

@ -1,6 +1,13 @@
<div class="content">
<div class="default-header">
<div class="row">
<div class="row align-items-center">
<a
mat-icon-button
matTooltip="Back to user management"
matTooltipClass="custom-tooltip"
[routerLink]="['/server', server.id, 'management', 'groups']">
<mat-icon aria-label="Back to user management">keyboard_arrow_left</mat-icon>
</a>
<h1 class="col">Groups {{group.name}} details</h1>
</div>
<div class="main">
@ -28,7 +35,7 @@
<div class="members">
<div>
<div class="title">
<div>Members: </div>
<div>Members:</div>
</div>
<mat-icon (click)="openAddUserDialog()" class="clickable">person_add</mat-icon>
</div>
@ -38,7 +45,7 @@
</mat-form-field>
</div>
<div *ngFor="let user of members | membersFilter: searchMembers | paginator: pageEvent">
<a href="/server/{{server.id}}/user_management/{{user.user_id}}">
<a href="/server/{{server.id}}/management/users/{{user.user_id}}">
<div>{{user.username}}</div>
</a>
<mat-icon class="clickable" (click)="openRemoveUserDialog(user)">delete</mat-icon>

View File

@ -1,4 +1,4 @@
<div class="content">
<div class="content" *ngIf="isReady; else loading">
<div class="default-header">
<div class="row">
<h1 class="col">Groups management</h1>
@ -37,7 +37,7 @@
<ng-container matColumnDef="name">
<th mat-header-cell *matHeaderCellDef mat-sort-header> Name</th>
<td mat-cell *matCellDef="let element"><a href="/server/{{server.id}}/group_management/{{element.user_group_id}}">{{element.name}}</a> </td>
<td mat-cell *matCellDef="let element"><a routerLink="/server/{{server.id}}/management/groups/{{element.user_group_id}}">{{element.name}}</a> </td>
</ng-container>
<ng-container matColumnDef="created_at">
@ -73,3 +73,8 @@
</mat-paginator>
</div>
</div>
<ng-template #loading>
<div>
<mat-spinner class="loader"></mat-spinner>
</div>
</ng-template>

View File

@ -13,3 +13,14 @@ table {
width: 160px;
margin: 20px;
}
.loader {
position: absolute;
margin: auto;
height: 175px;
bottom: 0;
left: 0;
right: 0;
top: 0;
width: 175px;
}

View File

@ -42,6 +42,7 @@ export class GroupManagementComponent implements OnInit {
groups: Group[];
dataSource = new MatTableDataSource<Group>();
searchText: string;
isReady = false;
constructor(
private route: ActivatedRoute,
@ -54,7 +55,7 @@ export class GroupManagementComponent implements OnInit {
ngOnInit(): void {
const serverId = this.route.snapshot.paramMap.get('server_id');
const serverId = this.route.parent.snapshot.paramMap.get('server_id');
this.serverService.get(+serverId).then((server: Server) => {
this.server = server;
this.refresh();
@ -106,6 +107,7 @@ export class GroupManagementComponent implements OnInit {
refresh() {
this.groupService.getGroups(this.server).subscribe((groups: Group[]) => {
this.isReady = true;
this.groups = groups;
this.dataSource.data = groups;
this.selection.clear();

View File

@ -0,0 +1,9 @@
<nav mat-tab-nav-bar>
<a mat-tab-link *ngFor="let link of links"
(click)="activeLink = link"
routerLink="./{{link}}"
routerLinkActive
[active]="rla.isActive"
#rla="routerLinkActive">{{link.charAt(0).toUpperCase() + link.slice(1)}} </a>
</nav>
<router-outlet></router-outlet>

View File

@ -0,0 +1,25 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { ManagementComponent } from './management.component';
describe('ManagementComponent', () => {
let component: ManagementComponent;
let fixture: ComponentFixture<ManagementComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ ManagementComponent ]
})
.compileComponents();
});
beforeEach(() => {
fixture = TestBed.createComponent(ManagementComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@ -0,0 +1,28 @@
import { Component, OnInit } from '@angular/core';
import {ActivatedRoute, Router} from "@angular/router";
import {Server} from "@models/server";
import {ServerService} from "@services/server.service";
@Component({
selector: 'app-management',
templateUrl: './management.component.html',
styleUrls: ['./management.component.scss']
})
export class ManagementComponent implements OnInit {
server: Server;
links = ['users', 'groups'];
activeLink: string = this.links[0];
constructor(
private route: ActivatedRoute,
public router: Router,
private serverService: ServerService) { }
ngOnInit(): void {
const serverId = this.route.snapshot.paramMap.get('server_id');
this.serverService.get(+serverId).then((server: Server) => {
this.server = server;
});
}
}

View File

@ -5,8 +5,8 @@
mat-icon-button
matTooltip="Back to user management"
matTooltipClass="custom-tooltip"
[routerLink]="['/server', server.id, 'user_management']">
<mat-icon aria-label="Back to user management">keyboard_arrow_left</mat-icon>
[routerLink]="['/server', server.id, 'management', 'users']">
<mat-icon aria-label="Back to user management">keyboard_arrow_left</mat-icon>
</button>
<h1 class="col">User Details</h1>
</div>
@ -17,32 +17,39 @@
<div *ngIf="user">
<form [formGroup]="editUserForm" class="input-field">
<mat-form-field class="input-field">
<input matInput type="text" formControlName="username" placeholder="Username" />
<input matInput type="text" formControlName="username" placeholder="Username"/>
<mat-error *ngIf="form.username?.touched && form.username?.errors && form.username?.errors.required"
>Username is required</mat-error>
>Username is required
</mat-error>
<mat-error *ngIf="form.username?.errors && form.username?.errors.pattern"
>Username is incorrect</mat-error>
>Username is incorrect
</mat-error>
<mat-error *ngIf="form.username?.errors && form.username?.errors.userExists"
>User with this username exists</mat-error>
>User with this username exists
</mat-error>
</mat-form-field>
<mat-form-field class="input-field">
<input matInput type="text" formControlName="full_name" placeholder="Full name" />
<input matInput type="text" formControlName="full_name" placeholder="Full name"/>
</mat-form-field>
<mat-form-field class="input-field">
<input matInput type="text" formControlName="email" placeholder="Email" />
<input matInput type="text" formControlName="email" placeholder="Email"/>
<mat-error *ngIf="form.email?.touched && form.email?.errors && form.email?.errors.required"
>Email is required</mat-error>
>Email is required
</mat-error>
<mat-error *ngIf="form.email?.errors && form.email?.errors.email"
>Email is invalid</mat-error>
>Email is invalid
</mat-error>
<mat-error *ngIf="form.email?.errors && form.email?.errors.emailExists"
>User with this email exists</mat-error>
>User with this email exists
</mat-error>
</mat-form-field>
<mat-form-field class="input-field">
<input matInput type="password" formControlName="password" placeholder="Password" />
<input matInput type="password" formControlName="password" placeholder="Password"/>
<mat-error *ngIf="form.password?.touched && form.password?.errors"
>Password must be between 6 and 100 characters</mat-error>
>Password must be between 6 and 100 characters
</mat-error>
</mat-form-field>
@ -63,7 +70,7 @@
<h5>Groups</h5>
<ul>
<li *ngFor="let group of groups">
<a [routerLink]="['/server', server.id, 'group_management', group.user_group_id]"
<a [routerLink]="['/server', server.id, 'management','groups', group.user_group_id]"
class="table-link">{{ group.name }}</a>
</li>
</ul>

View File

@ -1,8 +1,9 @@
<div class="content">
<div class="content" *ngIf="isReady; else loading">
<div class="default-header">
<div class="row">
<h1 class="col">User Management</h1>
<button class="col" mat-raised-button color="primary" (click)="deleteMultiple()" class="add-button" [disabled]="selection.selected.length == 0">
<button class="col" mat-raised-button color="primary" (click)="deleteMultiple()" class="add-button"
[disabled]="selection.selected.length == 0">
Delete Users
</button>
<button class="col" mat-raised-button color="primary" (click)="addUser()" class="add-button">
@ -13,14 +14,15 @@
<form>
<mat-form-field class="full-width">
<input matInput placeholder="Search by username, full name or email" [(ngModel)]="searchText" [ngModelOptions]="{ standalone: true }" />
<input matInput placeholder="Search by username, full name or email" [(ngModel)]="searchText"
[ngModelOptions]="{ standalone: true }"/>
</mat-form-field>
</form>
<div class="default-content">
<div class="mat-elevation-z8">
<mat-table #table [dataSource]="dataSource | userFilter: searchText" matSort>
<ng-container matColumnDef="select" >
<ng-container matColumnDef="select">
<mat-header-cell *matHeaderCellDef class="small-col">
<mat-checkbox (change)="$event ? masterToggle() : null"
[checked]="selection.hasValue() && isAllSelected()"
@ -36,40 +38,46 @@
</ng-container>
<ng-container matColumnDef="username">
<mat-header-cell *matHeaderCellDef mat-sort-header> Username </mat-header-cell>
<mat-header-cell *matHeaderCellDef mat-sort-header> Username</mat-header-cell>
<mat-cell *matCellDef="let row">
<a [routerLink]="['/server', server.id, 'user_management', row.user_id]"
<a [routerLink]="['/server', server.id, 'management','users', row.user_id]"
class="table-link" [matTooltip]="row.username">{{ row.username }}</a>
</mat-cell>
</ng-container>
<ng-container matColumnDef="full_name">
<mat-header-cell *matHeaderCellDef mat-sort-header> Full Name </mat-header-cell>
<mat-header-cell *matHeaderCellDef mat-sort-header> Full Name</mat-header-cell>
<mat-cell *matCellDef="let row">
<div [matTooltip]="row.full_name" matTooltipClass="custom-tooltip" class="overflow-col">{{ row.full_name }}</div>
<div [matTooltip]="row.full_name" matTooltipClass="custom-tooltip"
class="overflow-col">{{ row.full_name }}</div>
</mat-cell>
</ng-container>
<ng-container matColumnDef="email">
<mat-header-cell *matHeaderCellDef mat-sort-header> Mail </mat-header-cell>
<mat-header-cell *matHeaderCellDef mat-sort-header> Mail</mat-header-cell>
<mat-cell *matCellDef="let row">
<div [matTooltip]="row.email" matTooltipClass="custom-tooltip" class="overflow-col">{{ row.email }}</div>
</mat-cell>
</ng-container>
<ng-container matColumnDef="is_active">
<mat-header-cell *matHeaderCellDef mat-sort-header class="active-col"> Active </mat-header-cell>
<mat-header-cell *matHeaderCellDef mat-sort-header class="active-col"> Active</mat-header-cell>
<mat-cell *matCellDef="let row" class="active-col">{{row.is_active}}</mat-cell>
</ng-container>
<ng-container matColumnDef="last_login">
<mat-header-cell *matHeaderCellDef mat-sort-header> Last Login </mat-header-cell>
<mat-header-cell *matHeaderCellDef mat-sort-header> Last Login</mat-header-cell>
<mat-cell *matCellDef="let row" class="overflow-col">{{row.last_login}}</mat-cell>
</ng-container>
<ng-container matColumnDef="updated_at">
<mat-header-cell *matHeaderCellDef mat-sort-header> Last Update </mat-header-cell>
<mat-cell *matCellDef="let row" class="overflow-col">{{row.updated_at ? row.updated_at : row.created_at}}</mat-cell>
<mat-header-cell *matHeaderCellDef mat-sort-header> Last Update</mat-header-cell>
<mat-cell *matCellDef="let row"
class="overflow-col">{{row.updated_at ? row.updated_at : row.created_at}}</mat-cell>
</ng-container>
<ng-container matColumnDef="delete">
<mat-header-cell *matHeaderCellDef class="small-col"> </mat-header-cell>
<mat-cell *matCellDef="let row" class="small-col"><button mat-button (click)="onDelete(row)"><mat-icon>delete</mat-icon></button></mat-cell>
<mat-header-cell *matHeaderCellDef class="small-col"></mat-header-cell>
<mat-cell *matCellDef="let row" class="small-col">
<button mat-button (click)="onDelete(row)">
<mat-icon>delete</mat-icon>
</button>
</mat-cell>
</ng-container>
<mat-header-row *matHeaderRowDef="displayedColumns"></mat-header-row>
@ -83,3 +91,8 @@
</div>
</div>
</div>
<ng-template #loading>
<div>
<mat-spinner class="loader"></mat-spinner>
</div>
</ng-template>

View File

@ -28,3 +28,14 @@
.custom-tooltip {
font-size:100px;
}
.loader {
position: absolute;
margin: auto;
height: 175px;
bottom: 0;
left: 0;
right: 0;
top: 0;
width: 175px;
}

View File

@ -24,6 +24,7 @@ import {DeleteUserDialogComponent} from "@components/user-management/delete-user
import {ToasterService} from "@services/toaster.service";
import {MatPaginator} from "@angular/material/paginator";
import {MatTableDataSource} from "@angular/material/table";
import {ServerService} from "@services/server.service";
@Component({
selector: 'app-user-management',
@ -35,24 +36,27 @@ export class UserManagementComponent implements OnInit {
dataSource = new MatTableDataSource<User>();
displayedColumns = ['select', 'username', 'full_name', 'email', 'is_active', 'last_login', 'updated_at', 'delete'];
selection = new SelectionModel<User>(true, []);
searchText: string = '';
searchText = '';
@ViewChild(MatPaginator) paginator: MatPaginator;
@ViewChild(MatSort, { static: true }) sort: MatSort;
isReady = false;
constructor(
private route: ActivatedRoute,
private router: Router,
private userService: UserService,
private progressService: ProgressService,
private serverService: ServerService,
public dialog: MatDialog,
private toasterService: ToasterService) { }
ngOnInit() {
this.server = this.route.snapshot.data['server'];
if (!this.server) this.router.navigate(['/servers']);
this.refresh();
const serverId = this.route.parent.snapshot.paramMap.get('server_id');
this.serverService.get(+serverId).then((server: Server) => {
this.server = server;
this.refresh();
});
}
ngAfterViewInit() {
@ -67,12 +71,13 @@ export class UserManagementComponent implements OnInit {
default:
return item[property];
}
}
};
}
refresh() {
this.userService.list(this.server).subscribe(
(users: User[]) => {
this.isReady = true;
this.dataSource.data = users;
},
(error) => {

View File

@ -23,13 +23,9 @@
<mat-icon>settings</mat-icon>
<span>Settings</span>
</button>
<button mat-menu-item (click)="goToUserManagement()">
<button mat-menu-item (click)="goToManagement()">
<mat-icon>groups</mat-icon>
<span>User management</span>
</button>
<button mat-menu-item (click)="goToGroupManagement()">
<mat-icon>groups</mat-icon>
<span>Group management</span>
<span>Management</span>
</button>
<button mat-menu-item routerLink="/installed-software" [disabled]="!isInstalledSoftwareAvailable">
<mat-icon>cloud_download</mat-icon>

View File

@ -68,13 +68,6 @@ export class DefaultLayoutComponent implements OnInit, OnDestroy {
this.shouldStopServersOnClosing = this.electronService.isElectronApp;
}
goToGroupManagement() {
let serverId = this.router.url.split("/server/")[1].split("/")[0];
this.serverService.get(+serverId).then((server: Server) => {
this.router.navigate(['/server', server.id, 'group_management']);
});
}
goToUserInfo() {
let serverId = this.router.url.split("/server/")[1].split("/")[0];
this.serverService.get(+serverId).then((server: Server) => {
@ -137,10 +130,10 @@ export class DefaultLayoutComponent implements OnInit, OnDestroy {
this.routeSubscription.unsubscribe();
}
goToUserManagement() {
goToManagement() {
let serverId = this.router.url.split("/server/")[1].split("/")[0];
this.serverService.get(+serverId).then((server: Server) => {
this.router.navigate(['/server', server.id, 'user_management']);
this.router.navigate(['/server', server.id, 'management', 'users']);
});
}
}