Added constraint create client form (Mac). Updated groups tree UX

pull/19/head
Manuel Aranda Rosales 2025-03-26 21:49:42 +01:00
parent 7bff91aa42
commit edfab0be94
9 changed files with 74 additions and 12 deletions

View File

@ -98,7 +98,7 @@
<div class="tree-container"> <div class="tree-container">
<mat-tree [dataSource]="treeDataSource" [treeControl]="treeControl"> <mat-tree [dataSource]="treeDataSource" [treeControl]="treeControl">
<mat-tree-node [ngClass]="{'selected-node': selectedNode?.id === node.id}" <mat-tree-node [ngClass]="{'selected-node': selectedNode?.id === node.id}"
*matTreeNodeDef="let node; when: hasChild" matTreeNodePadding (click)="onNodeClick(node)"> *matTreeNodeDef="let node; when: hasChild" matTreeNodePadding (click)="onNodeClick($event, node)">
<button mat-icon-button matTreeNodeToggle [disabled]="!node.expandable" <button mat-icon-button matTreeNodeToggle [disabled]="!node.expandable"
[ngClass]="{'disabled-toggle': !node.expandable}"> [ngClass]="{'disabled-toggle': !node.expandable}">
<mat-icon>{{ treeControl.isExpanded(node) ? 'expand_more' : 'chevron_right' }}</mat-icon> <mat-icon>{{ treeControl.isExpanded(node) ? 'expand_more' : 'chevron_right' }}</mat-icon>
@ -114,12 +114,12 @@
}} }}
</mat-icon> </mat-icon>
<span>{{ node.name }}</span> <span>{{ node.name }}</span>
<button mat-icon-button [matMenuTriggerFor]="menuNode" (click)="onNodeClick(node)"> <button mat-icon-button [matMenuTriggerFor]="menuNode" (click)="onMenuClick($event, node)">
<mat-icon>more_vert</mat-icon> <mat-icon>more_vert</mat-icon>
</button> </button>
</mat-tree-node> </mat-tree-node>
<mat-tree-node [ngClass]="{'selected-node': selectedNode?.id === node.id}" <mat-tree-node [ngClass]="{'selected-node': selectedNode?.id === node.id}"
*matTreeNodeDef="let node; when: isLeafNode" matTreeNodePadding (click)="onNodeClick(node)"> *matTreeNodeDef="let node; when: isLeafNode" matTreeNodePadding (click)="onNodeClick($event, node)">
<button mat-icon-button matTreeNodeToggle [disabled]="true" class="disabled-toggle"></button> <button mat-icon-button matTreeNodeToggle [disabled]="true" class="disabled-toggle"></button>
<mat-icon style="color: green;"> <mat-icon style="color: green;">
{{ {{
@ -135,7 +135,7 @@
<ng-container *ngIf="node.type === 'client'"> <ng-container *ngIf="node.type === 'client'">
<span> - IP: {{ node.ip }}</span> <span> - IP: {{ node.ip }}</span>
</ng-container> </ng-container>
<button mat-icon-button [matMenuTriggerFor]="menuNode" (click)="onNodeClick(node)"> <button mat-icon-button [matMenuTriggerFor]="menuNode" (click)="onMenuClick($event, node)">
<mat-icon>more_vert</mat-icon> <mat-icon>more_vert</mat-icon>
</button> </button>
</mat-tree-node> </mat-tree-node>

View File

@ -196,6 +196,7 @@ export class GroupsComponent implements OnInit, OnDestroy {
hasClients: node.hasClients, hasClients: node.hasClients,
ip: node.ip, ip: node.ip,
'@id': node['@id'], '@id': node['@id'],
networkSettings: node.networkSettings,
}); });
@ -350,11 +351,16 @@ export class GroupsComponent implements OnInit, OnDestroy {
} }
onNodeClick(node: TreeNode): void { onNodeClick(event: MouseEvent, node: TreeNode): void {
event.stopPropagation();
this.selectedNode = node; this.selectedNode = node;
this.fetchClientsForNode(node); this.fetchClientsForNode(node);
} }
onMenuClick(event: Event, node: any): void {
event.stopPropagation();
}
public fetchClientsForNode(node: any, selectedClientsBeforeEdit: string[] = []): void { public fetchClientsForNode(node: any, selectedClientsBeforeEdit: string[] = []): void {
const params = new HttpParams({ fromObject: this.filters }); const params = new HttpParams({ fromObject: this.filters });

View File

@ -71,6 +71,7 @@ export interface TreeNode {
hasClients?: boolean; hasClients?: boolean;
clients?: Client[]; clients?: Client[];
ip?: string; ip?: string;
networkSettings?: Object;
} }
export interface FlatNode { export interface FlatNode {
@ -80,6 +81,7 @@ export interface FlatNode {
level: number; level: number;
expandable: boolean; expandable: boolean;
hasClients?: boolean; hasClients?: boolean;
networkSettings?: Object;
ip?: string; ip?: string;
'@id'?: string; '@id'?: string;
} }

View File

@ -84,7 +84,7 @@ export class ManageClientComponent implements OnInit {
serialNumber: [''], serialNumber: [''],
netiface: null, netiface: null,
netDriver: null, netDriver: null,
mac: ['', Validators.required], mac: ['', Validators.pattern(/^([0-9A-Fa-f]{2}:){5}[0-9A-Fa-f]{2}$/)],
ip: ['', Validators.required], ip: ['', Validators.required],
template: [null], template: [null],
hardwareProfile: [null], hardwareProfile: [null],

View File

@ -20,7 +20,7 @@
</mat-form-field> </mat-form-field>
<mat-form-field class="form-field" appearance="fill"> <mat-form-field class="form-field" appearance="fill">
<mat-label>Padre</mat-label> <mat-label>Padre</mat-label>
<mat-select formControlName="parent"> <mat-select formControlName="parent" (selectionChange)="onParentChange($event)">
<mat-select-trigger> <mat-select-trigger>
{{ getSelectedParentName() }} {{ getSelectedParentName() }}
</mat-select-trigger> </mat-select-trigger>

View File

@ -128,8 +128,28 @@ export class ManageOrganizationalUnitComponent implements OnInit {
this.parentUnitsWithPaths = this.parentUnits.map(unit => ({ this.parentUnitsWithPaths = this.parentUnits.map(unit => ({
id: unit['@id'], id: unit['@id'],
name: unit.name, name: unit.name,
path: this.dataService.getOrganizationalUnitPath(unit, this.parentUnits) path: this.dataService.getOrganizationalUnitPath(unit, this.parentUnits),
repository: unit.networkSettings?.repository?.['@id'],
hardwareProfile: unit.networkSettings?.hardwareProfile?.['@id'],
ogLive: unit.networkSettings?.ogLive?.['@id'],
menu: unit.networkSettings?.menu?.['@id'],
mcastIp: unit.networkSettings?.mcastIp,
mcastSpeed: unit.networkSettings?.mcastSpeed,
mcastPort: unit.networkSettings?.mcastPort,
mcastMode: unit.networkSettings?.mcastMode,
netiface: unit.networkSettings?.netiface,
p2pMode: unit.networkSettings?.p2pMode,
p2pTime: unit.networkSettings?.p2pTime,
dns: unit.networkSettings?.dns,
netmask: unit.networkSettings?.netmask,
router: unit.networkSettings?.router,
ntp: unit.networkSettings?.ntp
})); }));
const initialUnitId = this.generalFormGroup.get('parent')?.value;
if (initialUnitId) {
this.setOrganizationalUnitDefaults(initialUnitId);
}
this.loading = false; this.loading = false;
}, },
error => { error => {
@ -139,6 +159,33 @@ export class ManageOrganizationalUnitComponent implements OnInit {
); );
} }
onParentChange(event: any): void {
this.setOrganizationalUnitDefaults(event.value);
}
setOrganizationalUnitDefaults(unitId: string): void {
const selectedUnit: any = this.parentUnitsWithPaths.find(unit => unit.id === unitId);
if (selectedUnit) {
this.networkSettingsFormGroup.patchValue({
repository: selectedUnit.repository || null,
hardwareProfile: selectedUnit.hardwareProfile || null,
ogLive: selectedUnit.ogLive || null,
menu: selectedUnit.menu || null,
mcastIp: selectedUnit.mcastIp || null,
mcastSpeed: selectedUnit.mcastSpeed || null,
mcastPort: selectedUnit.mcastPort || null,
mcastMode: selectedUnit.mcastMode || null,
netiface: selectedUnit.netiface || null,
p2pMode: selectedUnit.p2pMode || null,
p2pTime: selectedUnit.p2pTime || null,
dns: selectedUnit.dns || null,
netmask: selectedUnit.netmask || null,
router: selectedUnit.router || null,
ntp: selectedUnit.ntp || null
});
}
}
getSelectedParentName(): string | undefined { getSelectedParentName(): string | undefined {
const parentId = this.generalFormGroup.get('parent')?.value; const parentId = this.generalFormGroup.get('parent')?.value;
return this.parentUnitsWithPaths.find(unit => unit.id === parentId)?.name; return this.parentUnitsWithPaths.find(unit => unit.id === parentId)?.name;

View File

@ -2,6 +2,12 @@
width: 100%; width: 100%;
} }
.dialog-content {
display: flex;
flex-direction: column;
padding: 40px;
}
.spacing-container { .spacing-container {
margin-top: 20px; margin-top: 20px;
margin-bottom: 16px; margin-bottom: 16px;

View File

@ -1,6 +1,6 @@
<h2 mat-dialog-title>{{ isEditMode ? 'Editar' : 'Añadir' }} subred</h2> <h2 mat-dialog-title>{{ isEditMode ? 'Editar' : 'Añadir' }} subred</h2>
<mat-dialog-content> <mat-dialog-content class="dialog-content">
<form [formGroup]="subnetForm"> <form [formGroup]="subnetForm">
<div class="spacing-container"> <div class="spacing-container">
<mat-form-field appearance="fill" class="full-width"> <mat-form-field appearance="fill" class="full-width">

View File

@ -1,6 +1,7 @@
.dialog-content { .dialog-content {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
padding: 40px;
} }
.repository-form { .repository-form {