| {{ 'routers.name' | translate }} | {{ 'routers.endpoint' | translate }} | {{ 'routers.access' | translate }} | {{ 'routers.backupPolicy' | translate }} | {{ 'routers.ping' | translate }} | {{ 'common.actions' | translate }} |
@@ -50,11 +50,20 @@
{{ pingLabel(routerItem) }}
-
+ {{ 'common.actions' | translate }}
+
+
|
@@ -100,6 +109,7 @@
+ {{ 'routers.nameValidationHint' | translate }}
diff --git a/frontend/src/app/features/routers/routers-page.component.ts b/frontend/src/app/features/routers/routers-page.component.ts
index d929c13..9d8b377 100644
--- a/frontend/src/app/features/routers/routers-page.component.ts
+++ b/frontend/src/app/features/routers/routers-page.component.ts
@@ -1,6 +1,7 @@
import { CommonModule } from '@angular/common';
import { Component, OnInit, inject } from '@angular/core';
import { FormBuilder, ReactiveFormsModule, Validators } from '@angular/forms';
+import { DEVICE_NAME_PATTERN, normalizeDeviceName } from '../../shared/utils/device-name';
import { Router } from '@angular/router';
import { TranslateModule } from '@ngx-translate/core';
import { ButtonModule } from 'primeng/button';
@@ -79,7 +80,7 @@ export class RoutersPageComponent implements OnInit {
{ label: 'SwitchOS', value: 'switchos' }
];
readonly form = this.fb.nonNullable.group({
- name: ['', Validators.required],
+ name: ['', [Validators.required, Validators.pattern(DEVICE_NAME_PATTERN)]],
device_type: ['routeros' as DeviceType, Validators.required],
host: ['', Validators.required],
port: [22, Validators.required],
@@ -159,7 +160,8 @@ export class RoutersPageComponent implements OnInit {
return;
}
this.saving = true;
- const payload = this.form.getRawValue();
+ const payload = { ...this.form.getRawValue(), name: normalizeDeviceName(this.form.controls.name.value) };
+ this.form.controls.name.setValue(payload.name, { emitEvent: false });
if (payload.device_type === 'switchos') {
payload.ssh_key = '';
}
diff --git a/frontend/src/app/shared/utils/device-name.ts b/frontend/src/app/shared/utils/device-name.ts
new file mode 100644
index 0000000..d5d992b
--- /dev/null
+++ b/frontend/src/app/shared/utils/device-name.ts
@@ -0,0 +1,5 @@
+export const DEVICE_NAME_PATTERN = /^[A-Za-z0-9_-]+$/;
+
+export function normalizeDeviceName(value: string | null | undefined): string {
+ return (value ?? '').trim();
+}
diff --git a/frontend/src/assets/i18n/en.json b/frontend/src/assets/i18n/en.json
index fa263af..12457b2 100644
--- a/frontend/src/assets/i18n/en.json
+++ b/frontend/src/assets/i18n/en.json
@@ -165,6 +165,7 @@
"editDialogTitle": "Edit device",
"host": "Host",
"port": "Port",
+ "nameValidationHint": "Use only letters, digits, dashes, and underscores without spaces.",
"sshUser": "Username",
"sshPrivateKey": "SSH private key",
"optionalPassword": "Optional password",
@@ -539,7 +540,6 @@
"host": "Host / URL",
"hostPlaceholder": "for example 192.168.88.1 or http://192.168.88.1",
"port": "Port",
- "username": "Username",
"password": "Password",
"passwordPlaceholder": "Leave empty when the device has no password",
"probeButton": "Check access",
diff --git a/frontend/src/assets/i18n/no.json b/frontend/src/assets/i18n/no.json
index f937800..f18b0dd 100644
--- a/frontend/src/assets/i18n/no.json
+++ b/frontend/src/assets/i18n/no.json
@@ -165,7 +165,8 @@
"editDialogTitle": "Rediger enhet",
"host": "Vert",
"port": "Port",
- "sshUser": "Bruker",
+ "nameValidationHint": "Use only letters, digits, dashes, and underscores without spaces.",
+ "sshUser": "Brukernavn",
"sshPrivateKey": "SSH privat nøkkel",
"optionalPassword": "Valgfritt passord",
"optionalPrivateKey": "Valgfri privat nøkkel",
@@ -521,7 +522,6 @@
"host": "Vert / URL",
"hostPlaceholder": "for eksempel 192.168.88.1 eller http://192.168.88.1",
"port": "Port",
- "username": "Brukernavn",
"password": "Passord",
"passwordPlaceholder": "La stå tomt hvis enheten ikke har passord",
"probeButton": "Sjekk tilgang",
diff --git a/frontend/src/assets/i18n/pl.json b/frontend/src/assets/i18n/pl.json
index 768ec40..0cb99e1 100644
--- a/frontend/src/assets/i18n/pl.json
+++ b/frontend/src/assets/i18n/pl.json
@@ -165,6 +165,7 @@
"editDialogTitle": "Edytuj urządzenie",
"host": "Host",
"port": "Port",
+ "nameValidationHint": "Użyj tylko liter, cyfr, myślnika i podkreślenia bez spacji.",
"sshUser": "Użytkownik",
"sshPrivateKey": "Klucz prywatny SSH",
"optionalPassword": "Opcjonalne hasło",
@@ -539,7 +540,6 @@
"host": "Host / URL",
"hostPlaceholder": "np. 192.168.88.1 albo http://192.168.88.1",
"port": "Port",
- "username": "Użytkownik",
"password": "Hasło",
"passwordPlaceholder": "Puste, jeśli urządzenie nie ma hasła",
"probeButton": "Sprawdź dostęp",
diff --git a/frontend/src/styles/pages.css b/frontend/src/styles/pages.css
index 50f8bed..4a935ff 100644
--- a/frontend/src/styles/pages.css
+++ b/frontend/src/styles/pages.css
@@ -3758,3 +3758,22 @@ body.dark-theme .device-toggle.is-active{background:linear-gradient(135deg,color
max-width: none;
}
}
+
+.form-field-error{
+ display:block;
+ margin-top:0.35rem;
+ font-size:0.78rem;
+ line-height:1.35;
+ color:var(--red-400);
+}
+
+.repository-table .table-row-menu__list .p-button{justify-content:flex-start;}
+
+@media (max-width: 960px){
+ .repository-table .table-row-menu{
+ display:inline-block !important;
+ }
+ .repository-table .table-row-menu__list{
+ min-width:min(220px,calc(100vw - 32px));
+ }
+}