mirror of
https://github.com/Start9Labs/start-os.git
synced 2026-03-31 04:23:40 +00:00
a bunch of UI cleanup around backups as well as other bug fixes and UII improvements
This commit is contained in:
@@ -34,15 +34,15 @@ import { StateService } from '../services/state.service'
|
|||||||
<tui-icon icon="@tui.circle-check-big" class="g-positive" />
|
<tui-icon icon="@tui.circle-check-big" class="g-positive" />
|
||||||
{{ 'Setup Complete!' | i18n }}
|
{{ 'Setup Complete!' | i18n }}
|
||||||
</h2>
|
</h2>
|
||||||
|
@if (!stateService.kiosk) {
|
||||||
|
<p tuiSubtitle>
|
||||||
|
{{
|
||||||
|
'http://start.local was for setup only. It will no longer work.'
|
||||||
|
| i18n
|
||||||
|
}}
|
||||||
|
</p>
|
||||||
|
}
|
||||||
</hgroup>
|
</hgroup>
|
||||||
@if (!stateService.kiosk) {
|
|
||||||
<p tuiSubtitle>
|
|
||||||
{{
|
|
||||||
'http://start.local was for setup only. It will no longer work.'
|
|
||||||
| i18n
|
|
||||||
}}
|
|
||||||
</p>
|
|
||||||
}
|
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
@if (!result) {
|
@if (!result) {
|
||||||
|
|||||||
@@ -302,15 +302,11 @@ export default {
|
|||||||
317: 'Originalpasswort',
|
317: 'Originalpasswort',
|
||||||
318: 'Originalpasswort eingeben',
|
318: 'Originalpasswort eingeben',
|
||||||
319: 'Sicherung wird gestartet',
|
319: 'Sicherung wird gestartet',
|
||||||
320: 'Sichern Sie StartOS und Dienstdaten, indem Sie sich mit einem Gerät im lokalen Netzwerk oder einem physischen Laufwerk verbinden, das an Ihren Server angeschlossen ist.',
|
|
||||||
321: 'Stellen Sie StartOS und Dienstdaten von einem Gerät im lokalen Netzwerk oder einem physischen Laufwerk mit vorhandener Sicherung wieder her.',
|
|
||||||
322: 'Letzte Sicherung',
|
322: 'Letzte Sicherung',
|
||||||
323: 'Ein Ordner auf einem anderen Computer, der mit demselben Netzwerk wie Ihr Start9-Server verbunden ist.',
|
325: 'Dienste auswählen',
|
||||||
324: 'Ein physisches Laufwerk, das direkt an Ihren Start9-Server angeschlossen ist.',
|
326: 'Server auswählen',
|
||||||
325: 'Dienste für Sicherung auswählen',
|
|
||||||
326: 'Serversicherung auswählen',
|
|
||||||
327: 'Netzwerkordner',
|
327: 'Netzwerkordner',
|
||||||
328: 'Neuen öffnen',
|
328: 'Neuen',
|
||||||
329: 'Hostname',
|
329: 'Hostname',
|
||||||
330: 'Pfad',
|
330: 'Pfad',
|
||||||
331: 'URL',
|
331: 'URL',
|
||||||
@@ -355,7 +351,6 @@ export default {
|
|||||||
372: 'Passwort erforderlich',
|
372: 'Passwort erforderlich',
|
||||||
373: 'Geben Sie das Master-Passwort ein, das zur Verschlüsselung dieser Sicherung verwendet wurde. Im nächsten Schritt wählen Sie die Dienste aus, die wiederhergestellt werden sollen.',
|
373: 'Geben Sie das Master-Passwort ein, das zur Verschlüsselung dieser Sicherung verwendet wurde. Im nächsten Schritt wählen Sie die Dienste aus, die wiederhergestellt werden sollen.',
|
||||||
374: 'Laufwerk wird entschlüsselt',
|
374: 'Laufwerk wird entschlüsselt',
|
||||||
375: 'Dienste zur Wiederherstellung auswählen',
|
|
||||||
376: 'Für Sicherung verfügbar',
|
376: 'Für Sicherung verfügbar',
|
||||||
377: 'StartOS-Sicherungen erkannt',
|
377: 'StartOS-Sicherungen erkannt',
|
||||||
378: 'Keine StartOS-Sicherungen erkannt',
|
378: 'Keine StartOS-Sicherungen erkannt',
|
||||||
@@ -726,4 +721,5 @@ export default {
|
|||||||
803: 'Dieses Laufwerk verwendet ext4 und wird automatisch in btrfs konvertiert. Ein Backup wird dringend empfohlen, bevor Sie fortfahren.',
|
803: 'Dieses Laufwerk verwendet ext4 und wird automatisch in btrfs konvertiert. Ein Backup wird dringend empfohlen, bevor Sie fortfahren.',
|
||||||
804: 'Ich habe ein Backup meiner Daten',
|
804: 'Ich habe ein Backup meiner Daten',
|
||||||
805: 'Öffentliche Domain hinzufügen',
|
805: 'Öffentliche Domain hinzufügen',
|
||||||
|
806: 'Ergebnis',
|
||||||
} satisfies i18n
|
} satisfies i18n
|
||||||
|
|||||||
@@ -301,15 +301,11 @@ export const ENGLISH: Record<string, number> = {
|
|||||||
'Original Password': 317,
|
'Original Password': 317,
|
||||||
'Enter original password': 318,
|
'Enter original password': 318,
|
||||||
'Beginning backup': 319,
|
'Beginning backup': 319,
|
||||||
'Back up StartOS and service data by connecting to a device on your local network or a physical drive connected to your server.': 320,
|
|
||||||
'Restore StartOS and service data from a device on your local network or a physical drive connected to your server that contains an existing backup.': 321,
|
|
||||||
'Last Backup': 322, // as in, the last time the server was backed up
|
'Last Backup': 322, // as in, the last time the server was backed up
|
||||||
'A folder on another computer that is connected to the same network as your Start9 server.': 323,
|
'Select services': 325,
|
||||||
'A physical drive that is plugged directly into your Start9 Server.': 324,
|
'Select server': 326,
|
||||||
'Select Services to Back Up': 325,
|
|
||||||
'Select server backup': 326,
|
|
||||||
'Network Folders': 327,
|
'Network Folders': 327,
|
||||||
'Open New': 328,
|
'New': 328,
|
||||||
'Hostname': 329,
|
'Hostname': 329,
|
||||||
'Path': 330, // as in, a URL path
|
'Path': 330, // as in, a URL path
|
||||||
'URL': 331,
|
'URL': 331,
|
||||||
@@ -354,7 +350,6 @@ export const ENGLISH: Record<string, number> = {
|
|||||||
'Password required': 372,
|
'Password required': 372,
|
||||||
'Enter the master password that was used to encrypt this backup. On the next screen, you will select the individual services you want to restore.': 373,
|
'Enter the master password that was used to encrypt this backup. On the next screen, you will select the individual services you want to restore.': 373,
|
||||||
'Decrypting drive': 374,
|
'Decrypting drive': 374,
|
||||||
'Select services to restore': 375,
|
|
||||||
'Available for backup': 376,
|
'Available for backup': 376,
|
||||||
'StartOS backups detected': 377,
|
'StartOS backups detected': 377,
|
||||||
'No StartOS backups detected': 378,
|
'No StartOS backups detected': 378,
|
||||||
@@ -727,4 +722,5 @@ export const ENGLISH: Record<string, number> = {
|
|||||||
'This drive uses ext4 and will be automatically converted to btrfs. A backup is strongly recommended before proceeding.': 803,
|
'This drive uses ext4 and will be automatically converted to btrfs. A backup is strongly recommended before proceeding.': 803,
|
||||||
'I have a backup of my data': 804,
|
'I have a backup of my data': 804,
|
||||||
'Add Public Domain': 805,
|
'Add Public Domain': 805,
|
||||||
|
'Result': 806,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -302,15 +302,11 @@ export default {
|
|||||||
317: 'Contraseña original',
|
317: 'Contraseña original',
|
||||||
318: 'Ingresa la contraseña original',
|
318: 'Ingresa la contraseña original',
|
||||||
319: 'Iniciando copia de seguridad',
|
319: 'Iniciando copia de seguridad',
|
||||||
320: 'Haz una copia de seguridad de StartOS y los datos de los servicios conectándote a un dispositivo en tu red local o a una unidad física conectada a tu servidor.',
|
|
||||||
321: 'Restaura StartOS y los datos de los servicios desde un dispositivo en tu red local o una unidad física conectada a tu servidor que contenga una copia de seguridad existente.',
|
|
||||||
322: 'Última copia de seguridad',
|
322: 'Última copia de seguridad',
|
||||||
323: 'Una carpeta en otro ordenador conectado a la misma red que tu servidor Start9.',
|
325: 'Seleccionar servicios',
|
||||||
324: 'Una unidad física conectada directamente a tu servidor Start9.',
|
326: 'Seleccionar servidor',
|
||||||
325: 'Seleccionar servicios para respaldar',
|
|
||||||
326: 'Seleccionar copia de seguridad del servidor',
|
|
||||||
327: 'Carpetas de red',
|
327: 'Carpetas de red',
|
||||||
328: 'Abrir nuevo',
|
328: 'Nuevo',
|
||||||
329: 'Nombre del host',
|
329: 'Nombre del host',
|
||||||
330: 'Ruta',
|
330: 'Ruta',
|
||||||
331: 'URL',
|
331: 'URL',
|
||||||
@@ -355,7 +351,6 @@ export default {
|
|||||||
372: 'Se requiere contraseña',
|
372: 'Se requiere contraseña',
|
||||||
373: 'Ingresa la contraseña maestra que se usó para cifrar esta copia de seguridad. En la siguiente pantalla, podrás seleccionar los servicios individuales que deseas restaurar.',
|
373: 'Ingresa la contraseña maestra que se usó para cifrar esta copia de seguridad. En la siguiente pantalla, podrás seleccionar los servicios individuales que deseas restaurar.',
|
||||||
374: 'Descifrando unidad',
|
374: 'Descifrando unidad',
|
||||||
375: 'Seleccionar servicios para restaurar',
|
|
||||||
376: 'Disponible para copia de seguridad',
|
376: 'Disponible para copia de seguridad',
|
||||||
377: 'Copias de seguridad de StartOS detectadas',
|
377: 'Copias de seguridad de StartOS detectadas',
|
||||||
378: 'No se detectaron copias de seguridad de StartOS',
|
378: 'No se detectaron copias de seguridad de StartOS',
|
||||||
@@ -726,4 +721,5 @@ export default {
|
|||||||
803: 'Esta unidad usa ext4 y se convertirá automáticamente a btrfs. Se recomienda encarecidamente hacer una copia de seguridad antes de continuar.',
|
803: 'Esta unidad usa ext4 y se convertirá automáticamente a btrfs. Se recomienda encarecidamente hacer una copia de seguridad antes de continuar.',
|
||||||
804: 'Tengo una copia de seguridad de mis datos',
|
804: 'Tengo una copia de seguridad de mis datos',
|
||||||
805: 'Agregar dominio público',
|
805: 'Agregar dominio público',
|
||||||
|
806: 'Resultado',
|
||||||
} satisfies i18n
|
} satisfies i18n
|
||||||
|
|||||||
@@ -302,15 +302,11 @@ export default {
|
|||||||
317: 'Mot de passe d’origine',
|
317: 'Mot de passe d’origine',
|
||||||
318: 'Entrez le mot de passe d’origine',
|
318: 'Entrez le mot de passe d’origine',
|
||||||
319: 'Démarrage de la sauvegarde',
|
319: 'Démarrage de la sauvegarde',
|
||||||
320: 'Sauvegardez StartOS et les données des services en connectant un appareil sur votre réseau local ou un disque physique connecté à votre serveur.',
|
|
||||||
321: 'Restaurez StartOS et les données des services à partir d’un appareil sur votre réseau local ou d’un disque physique connecté à votre serveur contenant une sauvegarde.',
|
|
||||||
322: 'Dernière sauvegarde',
|
322: 'Dernière sauvegarde',
|
||||||
323: 'Un dossier sur un autre ordinateur connecté au même réseau que votre serveur Start9.',
|
325: 'Sélectionner les services',
|
||||||
324: 'Un disque physique branché directement à votre serveur Start9.',
|
326: 'Sélectionner le serveur',
|
||||||
325: 'Sélectionner les services à sauvegarder',
|
|
||||||
326: 'Sélectionner la sauvegarde du serveur',
|
|
||||||
327: 'Dossiers réseau',
|
327: 'Dossiers réseau',
|
||||||
328: 'Ouvrir nouveau',
|
328: 'Nouveau',
|
||||||
329: 'Nom d’hôte',
|
329: 'Nom d’hôte',
|
||||||
330: 'Chemin',
|
330: 'Chemin',
|
||||||
331: 'URL',
|
331: 'URL',
|
||||||
@@ -355,7 +351,6 @@ export default {
|
|||||||
372: 'Mot de passe requis',
|
372: 'Mot de passe requis',
|
||||||
373: 'Entrez le mot de passe principal utilisé pour chiffrer cette sauvegarde. Vous pourrez choisir les services à restaurer à l’écran suivant.',
|
373: 'Entrez le mot de passe principal utilisé pour chiffrer cette sauvegarde. Vous pourrez choisir les services à restaurer à l’écran suivant.',
|
||||||
374: 'Déchiffrement du disque',
|
374: 'Déchiffrement du disque',
|
||||||
375: 'Sélectionner les services à restaurer',
|
|
||||||
376: 'Disponible pour la sauvegarde',
|
376: 'Disponible pour la sauvegarde',
|
||||||
377: 'Sauvegardes StartOS détectées',
|
377: 'Sauvegardes StartOS détectées',
|
||||||
378: 'Aucune sauvegarde StartOS détectée',
|
378: 'Aucune sauvegarde StartOS détectée',
|
||||||
@@ -726,4 +721,5 @@ export default {
|
|||||||
803: 'Ce disque utilise ext4 et sera automatiquement converti en btrfs. Il est fortement recommandé de faire une sauvegarde avant de continuer.',
|
803: 'Ce disque utilise ext4 et sera automatiquement converti en btrfs. Il est fortement recommandé de faire une sauvegarde avant de continuer.',
|
||||||
804: "J'ai une sauvegarde de mes données",
|
804: "J'ai une sauvegarde de mes données",
|
||||||
805: 'Ajouter un domaine public',
|
805: 'Ajouter un domaine public',
|
||||||
|
806: 'Résultat',
|
||||||
} satisfies i18n
|
} satisfies i18n
|
||||||
|
|||||||
@@ -302,15 +302,11 @@ export default {
|
|||||||
317: 'Oryginalne hasło',
|
317: 'Oryginalne hasło',
|
||||||
318: 'Wprowadź oryginalne hasło',
|
318: 'Wprowadź oryginalne hasło',
|
||||||
319: 'Rozpoczynanie tworzenia kopii zapasowej',
|
319: 'Rozpoczynanie tworzenia kopii zapasowej',
|
||||||
320: 'Utwórz kopię zapasową StartOS i danych serwisów, łącząc się z urządzeniem w sieci lokalnej lub z fizycznym dyskiem podłączonym do Twojego serwera.',
|
|
||||||
321: 'Przywróć StartOS i dane serwisów z urządzenia w sieci lokalnej lub z fizycznego dysku podłączonego do Twojego serwera, który zawiera istniejącą kopię zapasową.',
|
|
||||||
322: 'Ostatnia kopia zapasowa',
|
322: 'Ostatnia kopia zapasowa',
|
||||||
323: 'Folder na innym komputerze, który jest podłączony do tej samej sieci co twój serwer Start9.',
|
325: 'Wybierz serwisy',
|
||||||
324: 'Fizyczny dysk, który jest podłączony bezpośrednio do Twojego serwera Start9.',
|
326: 'Wybierz serwer',
|
||||||
325: 'Wybierz serwisy do kopii zapasowej',
|
|
||||||
326: 'Wybierz kopię zapasową serwera',
|
|
||||||
327: 'Foldery sieciowe',
|
327: 'Foldery sieciowe',
|
||||||
328: 'Otwórz nowy',
|
328: 'Nowy',
|
||||||
329: 'Nazwa hosta',
|
329: 'Nazwa hosta',
|
||||||
330: 'Ścieżka',
|
330: 'Ścieżka',
|
||||||
331: 'URL',
|
331: 'URL',
|
||||||
@@ -355,7 +351,6 @@ export default {
|
|||||||
372: 'Wymagane hasło',
|
372: 'Wymagane hasło',
|
||||||
373: 'Wprowadź hasło główne, które zostało użyte do zaszyfrowania tej kopii zapasowej. Na następnym ekranie wybierzesz poszczególne serwisy, które chcesz przywrócić.',
|
373: 'Wprowadź hasło główne, które zostało użyte do zaszyfrowania tej kopii zapasowej. Na następnym ekranie wybierzesz poszczególne serwisy, które chcesz przywrócić.',
|
||||||
374: 'Odszyfrowywanie dysku',
|
374: 'Odszyfrowywanie dysku',
|
||||||
375: 'Wybierz serwisy do przywrócenia',
|
|
||||||
376: 'Dostępne do kopii zapasowej',
|
376: 'Dostępne do kopii zapasowej',
|
||||||
377: 'Wykryto kopie zapasowe StartOS',
|
377: 'Wykryto kopie zapasowe StartOS',
|
||||||
378: 'Nie wykryto kopii zapasowych StartOS',
|
378: 'Nie wykryto kopii zapasowych StartOS',
|
||||||
@@ -726,4 +721,5 @@ export default {
|
|||||||
803: 'Ten dysk używa ext4 i zostanie automatycznie skonwertowany na btrfs. Zdecydowanie zaleca się wykonanie kopii zapasowej przed kontynuowaniem.',
|
803: 'Ten dysk używa ext4 i zostanie automatycznie skonwertowany na btrfs. Zdecydowanie zaleca się wykonanie kopii zapasowej przed kontynuowaniem.',
|
||||||
804: 'Mam kopię zapasową moich danych',
|
804: 'Mam kopię zapasową moich danych',
|
||||||
805: 'Dodaj domenę publiczną',
|
805: 'Dodaj domenę publiczną',
|
||||||
|
806: 'Wynik',
|
||||||
} satisfies i18n
|
} satisfies i18n
|
||||||
|
|||||||
@@ -169,7 +169,7 @@ export class DevicesAdd {
|
|||||||
})
|
})
|
||||||
|
|
||||||
this.dialogs
|
this.dialogs
|
||||||
.open(DEVICES_CONFIG, { data: config, closable: false })
|
.open(DEVICES_CONFIG, { data: config, closable: false, size: 'm' })
|
||||||
.subscribe()
|
.subscribe()
|
||||||
}
|
}
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ import { QrCodeComponent } from 'ng-qrcode'
|
|||||||
</tui-segmented>
|
</tui-segmented>
|
||||||
</aside>
|
</aside>
|
||||||
</header>
|
</header>
|
||||||
@if (segmented?.activeItemIndex) {
|
@if (segmented?.activeItemIndex()) {
|
||||||
<qr-code [value]="config" size="352" />
|
<qr-code [value]="config" size="352" />
|
||||||
} @else {
|
} @else {
|
||||||
<tui-textfield>
|
<tui-textfield>
|
||||||
|
|||||||
@@ -159,7 +159,9 @@ export default class Devices {
|
|||||||
try {
|
try {
|
||||||
const data = await this.api.showDeviceConfig({ subnet: subnet.range, ip })
|
const data = await this.api.showDeviceConfig({ subnet: subnet.range, ip })
|
||||||
|
|
||||||
this.dialogs.open(DEVICES_CONFIG, { data, closable: false }).subscribe()
|
this.dialogs
|
||||||
|
.open(DEVICES_CONFIG, { data, closable: false, size: 'm' })
|
||||||
|
.subscribe()
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
console.log(e)
|
console.log(e)
|
||||||
this.errorService.handleError(e)
|
this.errorService.handleError(e)
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import {
|
|||||||
} from '@angular/core'
|
} from '@angular/core'
|
||||||
import { toSignal } from '@angular/core/rxjs-interop'
|
import { toSignal } from '@angular/core/rxjs-interop'
|
||||||
import { i18nKey, i18nPipe } from '@start9labs/shared'
|
import { i18nKey, i18nPipe } from '@start9labs/shared'
|
||||||
import { TuiDialogContext, TuiIcon, TuiTitle, TuiCell } from '@taiga-ui/core'
|
import { TuiDialogContext, TuiIcon } from '@taiga-ui/core'
|
||||||
import { injectContext, PolymorpheusComponent } from '@taiga-ui/polymorpheus'
|
import { injectContext, PolymorpheusComponent } from '@taiga-ui/polymorpheus'
|
||||||
import { PatchDB } from 'patch-db-client'
|
import { PatchDB } from 'patch-db-client'
|
||||||
import { map } from 'rxjs'
|
import { map } from 'rxjs'
|
||||||
@@ -17,41 +17,58 @@ import { DataModel } from '../services/patch-db/data-model'
|
|||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
template: `
|
template: `
|
||||||
<h3 class="g-title">
|
<p class="timestamp">{{ data.createdAt | date: 'medium' }}</p>
|
||||||
{{ 'Completed' | i18n }}: {{ data.createdAt | date: 'medium' }}
|
<table class="g-table">
|
||||||
</h3>
|
<thead>
|
||||||
<div tuiCell>
|
<tr>
|
||||||
<div tuiTitle>
|
<th>{{ 'Title' | i18n }}</th>
|
||||||
<strong>{{ 'System data' | i18n }}</strong>
|
<th>{{ 'Result' | i18n }}</th>
|
||||||
<div tuiSubtitle [style.color]="system().color">
|
</tr>
|
||||||
{{ system().result | i18n }}
|
</thead>
|
||||||
</div>
|
<tbody>
|
||||||
</div>
|
<tr>
|
||||||
<tui-icon [icon]="system().icon" [style.color]="system().color" />
|
<td>{{ 'System data' | i18n }}</td>
|
||||||
</div>
|
<td [style.color]="system().color">
|
||||||
@if (pkgTitles(); as titles) {
|
<tui-icon [icon]="system().icon" />
|
||||||
@for (pkg of data.content.packages | keyvalue; track $index) {
|
{{ system().result | i18n }}
|
||||||
<div tuiCell>
|
</td>
|
||||||
<div tuiTitle>
|
</tr>
|
||||||
<strong>{{ titles[pkg.key] || pkg.key }}</strong>
|
@if (pkgTitles(); as titles) {
|
||||||
<div tuiSubtitle [style.color]="getColor(pkg.value.error)">
|
@for (pkg of data.content.packages | keyvalue; track $index) {
|
||||||
{{
|
<tr>
|
||||||
pkg.value.error
|
<td>{{ titles[pkg.key] || pkg.key }}</td>
|
||||||
? ('Failed' | i18n) + ': ' + pkg.value.error
|
<td [style.color]="getColor(pkg.value.error)">
|
||||||
: ('Succeeded' | i18n)
|
<tui-icon [icon]="getIcon(pkg.value.error)" />
|
||||||
}}
|
{{
|
||||||
</div>
|
pkg.value.error
|
||||||
</div>
|
? ('Failed' | i18n) + ': ' + pkg.value.error
|
||||||
<tui-icon
|
: ('Succeeded' | i18n)
|
||||||
[icon]="getIcon(pkg.value.error)"
|
}}
|
||||||
[style.color]="getColor(pkg.value.error)"
|
</td>
|
||||||
/>
|
</tr>
|
||||||
</div>
|
}
|
||||||
}
|
}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
`,
|
||||||
|
styles: `
|
||||||
|
.timestamp {
|
||||||
|
color: var(--tui-text-secondary);
|
||||||
|
margin: 0 0 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
td:first-child {
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
tui-icon {
|
||||||
|
font-size: 1rem;
|
||||||
|
vertical-align: sub;
|
||||||
|
margin-inline-end: 0.25rem;
|
||||||
}
|
}
|
||||||
`,
|
`,
|
||||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||||
imports: [CommonModule, TuiIcon, TuiCell, TuiTitle, i18nPipe],
|
imports: [CommonModule, TuiIcon, i18nPipe],
|
||||||
})
|
})
|
||||||
export class BackupsReportModal {
|
export class BackupsReportModal {
|
||||||
private readonly i18n = inject(i18nPipe)
|
private readonly i18n = inject(i18nPipe)
|
||||||
|
|||||||
@@ -7,7 +7,13 @@ import {
|
|||||||
} from '@angular/core'
|
} from '@angular/core'
|
||||||
import { ErrorService, i18nPipe } from '@start9labs/shared'
|
import { ErrorService, i18nPipe } from '@start9labs/shared'
|
||||||
import { ISB, utils } from '@start9labs/start-sdk'
|
import { ISB, utils } from '@start9labs/start-sdk'
|
||||||
import { TuiButton, TuiDataList, TuiDropdown, TuiInput } from '@taiga-ui/core'
|
import {
|
||||||
|
TuiButton,
|
||||||
|
TuiDataList,
|
||||||
|
TuiDropdown,
|
||||||
|
TuiIcon,
|
||||||
|
TuiInput,
|
||||||
|
} from '@taiga-ui/core'
|
||||||
import { TuiNotificationMiddleService } from '@taiga-ui/kit'
|
import { TuiNotificationMiddleService } from '@taiga-ui/kit'
|
||||||
import { PatchDB } from 'patch-db-client'
|
import { PatchDB } from 'patch-db-client'
|
||||||
import { firstValueFrom } from 'rxjs'
|
import { firstValueFrom } from 'rxjs'
|
||||||
@@ -33,7 +39,18 @@ import { InterfaceAddressItemComponent } from './item.component'
|
|||||||
selector: 'section[gatewayGroup]',
|
selector: 'section[gatewayGroup]',
|
||||||
template: `
|
template: `
|
||||||
<header>
|
<header>
|
||||||
{{ 'Gateway' | i18n }}: {{ gatewayGroup().gatewayName }}
|
@switch (gatewayGroup().deviceType) {
|
||||||
|
@case ('ethernet') {
|
||||||
|
<tui-icon icon="@tui.ethernet-port" />
|
||||||
|
}
|
||||||
|
@case ('wireless') {
|
||||||
|
<tui-icon icon="@tui.wifi" />
|
||||||
|
}
|
||||||
|
@case ('wireguard') {
|
||||||
|
<tui-icon icon="@tui.shield" />
|
||||||
|
}
|
||||||
|
}
|
||||||
|
{{ gatewayGroup().gatewayName }}
|
||||||
@if (gatewayGroup().isWireguard) {
|
@if (gatewayGroup().isWireguard) {
|
||||||
<button
|
<button
|
||||||
tuiButton
|
tuiButton
|
||||||
@@ -93,6 +110,11 @@ import { InterfaceAddressItemComponent } from './item.component'
|
|||||||
</table>
|
</table>
|
||||||
`,
|
`,
|
||||||
styles: `
|
styles: `
|
||||||
|
header tui-icon {
|
||||||
|
font-size: 1.25rem;
|
||||||
|
margin-inline-end: 0.375rem;
|
||||||
|
}
|
||||||
|
|
||||||
:host ::ng-deep {
|
:host ::ng-deep {
|
||||||
th:first-child {
|
th:first-child {
|
||||||
width: 5rem;
|
width: 5rem;
|
||||||
@@ -104,6 +126,7 @@ import { InterfaceAddressItemComponent } from './item.component'
|
|||||||
TuiButton,
|
TuiButton,
|
||||||
TuiDropdown,
|
TuiDropdown,
|
||||||
TuiDataList,
|
TuiDataList,
|
||||||
|
TuiIcon,
|
||||||
TuiInput,
|
TuiInput,
|
||||||
TableComponent,
|
TableComponent,
|
||||||
PlaceholderComponent,
|
PlaceholderComponent,
|
||||||
|
|||||||
@@ -209,7 +209,7 @@ import {
|
|||||||
styles: `
|
styles: `
|
||||||
.plugin-icon {
|
.plugin-icon {
|
||||||
height: 1.25rem;
|
height: 1.25rem;
|
||||||
margin-right: 0.25rem;
|
margin-inline-end: 0.375rem;
|
||||||
border-radius: 100%;
|
border-radius: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -158,6 +158,7 @@ export class InterfaceService {
|
|||||||
return {
|
return {
|
||||||
gatewayId: g.id,
|
gatewayId: g.id,
|
||||||
gatewayName: g.name,
|
gatewayName: g.name,
|
||||||
|
deviceType: g.ipInfo?.deviceType || 'ethernet',
|
||||||
isWireguard: g.ipInfo?.deviceType === 'wireguard',
|
isWireguard: g.ipInfo?.deviceType === 'wireguard',
|
||||||
addresses,
|
addresses,
|
||||||
}
|
}
|
||||||
@@ -314,6 +315,7 @@ export type GatewayAddress = {
|
|||||||
export type GatewayAddressGroup = {
|
export type GatewayAddressGroup = {
|
||||||
gatewayId: string
|
gatewayId: string
|
||||||
gatewayName: string
|
gatewayName: string
|
||||||
|
deviceType: string
|
||||||
isWireguard: boolean
|
isWireguard: boolean
|
||||||
addresses: GatewayAddress[]
|
addresses: GatewayAddress[]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -120,5 +120,5 @@ export class BackupsBackupModal {
|
|||||||
export const BACKUP = new PolymorpheusComponent(BackupsBackupModal)
|
export const BACKUP = new PolymorpheusComponent(BackupsBackupModal)
|
||||||
|
|
||||||
export const BACKUP_OPTIONS: Partial<TuiDialogOptions<unknown>> = {
|
export const BACKUP_OPTIONS: Partial<TuiDialogOptions<unknown>> = {
|
||||||
label: 'Select Services to Back Up',
|
label: 'Select services',
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -235,6 +235,7 @@ export class BackupsHistoryModal {
|
|||||||
content: report,
|
content: report,
|
||||||
createdAt: completedAt,
|
createdAt: completedAt,
|
||||||
},
|
},
|
||||||
|
size: 'l',
|
||||||
})
|
})
|
||||||
.subscribe()
|
.subscribe()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -50,16 +50,11 @@ import { DataModel } from 'src/app/services/patch-db/data-model'
|
|||||||
<td class="content">
|
<td class="content">
|
||||||
<tui-line-clamp
|
<tui-line-clamp
|
||||||
style="pointer-events: none"
|
style="pointer-events: none"
|
||||||
[linesLimit]="4"
|
[linesLimit]="3"
|
||||||
[lineHeight]="21"
|
[lineHeight]="21"
|
||||||
[content]="item.message"
|
[content]="item.message"
|
||||||
(overflownChange)="overflow = $event"
|
(overflownChange)="overflow = $event"
|
||||||
/>
|
/>
|
||||||
@if (overflow) {
|
|
||||||
<button tuiLink (click.stop)="onClick(item)">
|
|
||||||
{{ 'View full' | i18n }}
|
|
||||||
</button>
|
|
||||||
}
|
|
||||||
@if ([1, 2].includes(item.code)) {
|
@if ([1, 2].includes(item.code)) {
|
||||||
<button tuiLink (click.stop)="onClick(item)">
|
<button tuiLink (click.stop)="onClick(item)">
|
||||||
{{
|
{{
|
||||||
@@ -68,6 +63,10 @@ import { DataModel } from 'src/app/services/patch-db/data-model'
|
|||||||
: ('View details' | i18n)
|
: ('View details' | i18n)
|
||||||
}}
|
}}
|
||||||
</button>
|
</button>
|
||||||
|
} @else if (overflow) {
|
||||||
|
<button tuiLink (click.stop)="onClick(item)">
|
||||||
|
{{ 'View full' | i18n }}
|
||||||
|
</button>
|
||||||
}
|
}
|
||||||
</td>
|
</td>
|
||||||
}
|
}
|
||||||
@@ -186,12 +185,7 @@ export class NotificationItemComponent {
|
|||||||
overflow = false
|
overflow = false
|
||||||
|
|
||||||
onClick(item: ServerNotification<number>) {
|
onClick(item: ServerNotification<number>) {
|
||||||
if (this.overflow) {
|
this.service.viewModal(item)
|
||||||
this.service.viewModal(item, true)
|
item.seen = true
|
||||||
item.seen = true
|
|
||||||
} else if ([1, 2].includes(item.code)) {
|
|
||||||
this.service.viewModal(item)
|
|
||||||
item.seen = true
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,10 +9,20 @@ import { AuthoritiesTableComponent } from './table.component'
|
|||||||
@Component({
|
@Component({
|
||||||
template: `
|
template: `
|
||||||
<ng-container *title>
|
<ng-container *title>
|
||||||
<a routerLink=".." tuiIconButton iconStart="@tui.arrow-left">
|
<div>
|
||||||
{{ 'Back' | i18n }}
|
<a routerLink=".." tuiIconButton iconStart="@tui.arrow-left">
|
||||||
</a>
|
{{ 'Back' | i18n }}
|
||||||
{{ 'Certificate Authorities' | i18n }}
|
</a>
|
||||||
|
{{ 'Certificate Authorities' | i18n }}
|
||||||
|
<a
|
||||||
|
tuiIconButton
|
||||||
|
size="xs"
|
||||||
|
docsLink
|
||||||
|
path="/start-os/trust-ca.html"
|
||||||
|
appearance="icon"
|
||||||
|
iconStart="@tui.book-open-text"
|
||||||
|
></a>
|
||||||
|
</div>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
<section class="g-card">
|
<section class="g-card">
|
||||||
<header>
|
<header>
|
||||||
|
|||||||
@@ -9,13 +9,7 @@ import { toSignal } from '@angular/core/rxjs-interop'
|
|||||||
import { ActivatedRoute, RouterLink } from '@angular/router'
|
import { ActivatedRoute, RouterLink } from '@angular/router'
|
||||||
import { DialogService, DocsLinkDirective, i18nPipe } from '@start9labs/shared'
|
import { DialogService, DocsLinkDirective, i18nPipe } from '@start9labs/shared'
|
||||||
import { TuiMapperPipe } from '@taiga-ui/cdk'
|
import { TuiMapperPipe } from '@taiga-ui/cdk'
|
||||||
import {
|
import { TuiButton, TuiLoader, TuiNotification, TuiTitle } from '@taiga-ui/core'
|
||||||
TuiButton,
|
|
||||||
TuiLink,
|
|
||||||
TuiLoader,
|
|
||||||
TuiNotification,
|
|
||||||
TuiTitle,
|
|
||||||
} from '@taiga-ui/core'
|
|
||||||
import { TuiHeader } from '@taiga-ui/layout'
|
import { TuiHeader } from '@taiga-ui/layout'
|
||||||
import { PatchDB } from 'patch-db-client'
|
import { PatchDB } from 'patch-db-client'
|
||||||
import {
|
import {
|
||||||
@@ -35,12 +29,28 @@ import { BACKUP_RESTORE } from './restore.component'
|
|||||||
@Component({
|
@Component({
|
||||||
template: `
|
template: `
|
||||||
<ng-container *title>
|
<ng-container *title>
|
||||||
<a routerLink=".." tuiIconButton iconStart="@tui.arrow-left">
|
<div>
|
||||||
{{ 'Back' | i18n }}
|
<a routerLink=".." tuiIconButton iconStart="@tui.arrow-left">
|
||||||
</a>
|
{{ 'Back' | i18n }}
|
||||||
{{
|
</a>
|
||||||
type === 'create' ? ('Create Backup' | i18n) : ('Restore Backup' | i18n)
|
{{
|
||||||
}}
|
type === 'create'
|
||||||
|
? ('Create Backup' | i18n)
|
||||||
|
: ('Restore Backup' | i18n)
|
||||||
|
}}
|
||||||
|
<a
|
||||||
|
tuiIconButton
|
||||||
|
size="xs"
|
||||||
|
docsLink
|
||||||
|
[path]="
|
||||||
|
type === 'create'
|
||||||
|
? '/start-os/backup-create.html'
|
||||||
|
: '/start-os/backup-restore.html'
|
||||||
|
"
|
||||||
|
appearance="icon"
|
||||||
|
iconStart="@tui.book-open-text"
|
||||||
|
></a>
|
||||||
|
</div>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
|
|
||||||
<header tuiHeader>
|
<header tuiHeader>
|
||||||
@@ -51,36 +61,19 @@ import { BACKUP_RESTORE } from './restore.component'
|
|||||||
? ('Create Backup' | i18n)
|
? ('Create Backup' | i18n)
|
||||||
: ('Restore Backup' | i18n)
|
: ('Restore Backup' | i18n)
|
||||||
}}
|
}}
|
||||||
|
<a
|
||||||
|
tuiIconButton
|
||||||
|
size="xs"
|
||||||
|
docsLink
|
||||||
|
[path]="
|
||||||
|
type === 'create'
|
||||||
|
? '/start-os/backup-create.html'
|
||||||
|
: '/start-os/backup-restore.html'
|
||||||
|
"
|
||||||
|
appearance="icon"
|
||||||
|
iconStart="@tui.book-open-text"
|
||||||
|
></a>
|
||||||
</h3>
|
</h3>
|
||||||
<p tuiSubtitle>
|
|
||||||
@if (type === 'create') {
|
|
||||||
{{
|
|
||||||
'Back up StartOS and service data by connecting to a device on your local network or a physical drive connected to your server.'
|
|
||||||
| i18n
|
|
||||||
}}
|
|
||||||
<a
|
|
||||||
tuiLink
|
|
||||||
docsLink
|
|
||||||
path="/start-os/backup-create.html"
|
|
||||||
appearance="action-grayscale"
|
|
||||||
iconEnd="@tui.external-link"
|
|
||||||
[textContent]="'View instructions' | i18n"
|
|
||||||
></a>
|
|
||||||
} @else {
|
|
||||||
{{
|
|
||||||
'Restore StartOS and service data from a device on your local network or a physical drive connected to your server that contains an existing backup.'
|
|
||||||
| i18n
|
|
||||||
}}
|
|
||||||
<a
|
|
||||||
tuiLink
|
|
||||||
docsLink
|
|
||||||
path="/start-os/backup-restore.html"
|
|
||||||
appearance="action-grayscale"
|
|
||||||
iconEnd="@tui.external-link"
|
|
||||||
[textContent]="'View instructions' | i18n"
|
|
||||||
></a>
|
|
||||||
}
|
|
||||||
</p>
|
|
||||||
</hgroup>
|
</hgroup>
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
@@ -109,30 +102,16 @@ import { BACKUP_RESTORE } from './restore.component'
|
|||||||
[style.height.rem]="20"
|
[style.height.rem]="20"
|
||||||
/>
|
/>
|
||||||
} @else {
|
} @else {
|
||||||
<section (networkFolders)="onTarget($event)">
|
<section (networkFolders)="onTarget($event)"></section>
|
||||||
{{
|
<section (physicalFolders)="onTarget($event)"></section>
|
||||||
'A folder on another computer that is connected to the same network as your Start9 server.'
|
|
||||||
| i18n
|
|
||||||
}}
|
|
||||||
<a
|
|
||||||
tuiLink
|
|
||||||
docsLink
|
|
||||||
path="/start-os/backup-create.html"
|
|
||||||
fragment="#network-folder"
|
|
||||||
appearance="action-grayscale"
|
|
||||||
iconEnd="@tui.external-link"
|
|
||||||
[textContent]="'View instructions' | i18n"
|
|
||||||
></a>
|
|
||||||
</section>
|
|
||||||
<section (physicalFolders)="onTarget($event)">
|
|
||||||
{{
|
|
||||||
'A physical drive that is plugged directly into your Start9 Server.'
|
|
||||||
| i18n
|
|
||||||
}}
|
|
||||||
</section>
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
`,
|
`,
|
||||||
|
styles: `
|
||||||
|
:host-context(tui-root._mobile) [tuiHeader] {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
`,
|
||||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||||
imports: [
|
imports: [
|
||||||
AsyncPipe,
|
AsyncPipe,
|
||||||
@@ -140,7 +119,6 @@ import { BACKUP_RESTORE } from './restore.component'
|
|||||||
RouterLink,
|
RouterLink,
|
||||||
TuiButton,
|
TuiButton,
|
||||||
TuiLoader,
|
TuiLoader,
|
||||||
TuiLink,
|
|
||||||
TuiHeader,
|
TuiHeader,
|
||||||
TuiTitle,
|
TuiTitle,
|
||||||
TuiNotification,
|
TuiNotification,
|
||||||
@@ -184,12 +162,22 @@ export default class SystemBackupComponent implements OnInit {
|
|||||||
}
|
}
|
||||||
|
|
||||||
onTarget(target: MappedBackupTarget<CifsBackupTarget | DiskBackupTarget>) {
|
onTarget(target: MappedBackupTarget<CifsBackupTarget | DiskBackupTarget>) {
|
||||||
const component = this.type === 'create' ? BACKUP : BACKUP_RESTORE
|
if (this.type === 'create') {
|
||||||
const label =
|
this.dialog
|
||||||
this.type === 'create'
|
.openComponent(BACKUP, {
|
||||||
? 'Select Services to Back Up'
|
label: 'Select services',
|
||||||
: 'Select server backup'
|
data: target,
|
||||||
|
size: 'm',
|
||||||
this.dialog.openComponent(component, { label, data: target }).subscribe()
|
})
|
||||||
|
.subscribe()
|
||||||
|
} else {
|
||||||
|
this.dialog
|
||||||
|
.openComponent(BACKUP_RESTORE, {
|
||||||
|
label: 'Select server',
|
||||||
|
data: target,
|
||||||
|
size: 'l',
|
||||||
|
})
|
||||||
|
.subscribe()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,8 +7,8 @@ import {
|
|||||||
import { ActivatedRoute } from '@angular/router'
|
import { ActivatedRoute } from '@angular/router'
|
||||||
import { DialogService, ErrorService, i18nPipe } from '@start9labs/shared'
|
import { DialogService, ErrorService, i18nPipe } from '@start9labs/shared'
|
||||||
import { ISB, T } from '@start9labs/start-sdk'
|
import { ISB, T } from '@start9labs/start-sdk'
|
||||||
import { TuiButton, TuiIcon } from '@taiga-ui/core'
|
import { TuiButton, TuiDataList, TuiDropdown, TuiIcon } from '@taiga-ui/core'
|
||||||
import { TuiNotificationMiddleService, TuiTooltip } from '@taiga-ui/kit'
|
import { TuiNotificationMiddleService } from '@taiga-ui/kit'
|
||||||
import { filter } from 'rxjs'
|
import { filter } from 'rxjs'
|
||||||
import { FormComponent } from 'src/app/routes/portal/components/form.component'
|
import { FormComponent } from 'src/app/routes/portal/components/form.component'
|
||||||
import { PlaceholderComponent } from 'src/app/routes/portal/components/placeholder.component'
|
import { PlaceholderComponent } from 'src/app/routes/portal/components/placeholder.component'
|
||||||
@@ -28,10 +28,14 @@ const ERROR =
|
|||||||
template: `
|
template: `
|
||||||
<header>
|
<header>
|
||||||
{{ 'Network Folders' | i18n }}
|
{{ 'Network Folders' | i18n }}
|
||||||
<tui-icon [tuiTooltip]="cifs" />
|
<button
|
||||||
<ng-template #cifs><ng-content /></ng-template>
|
tuiButton
|
||||||
<button tuiButton size="xs" iconStart="@tui.plus" (click)="add()">
|
size="xs"
|
||||||
{{ 'Open New' | i18n }}
|
iconStart="@tui.plus"
|
||||||
|
[style.margin-inline-start]="'auto'"
|
||||||
|
(click)="add()"
|
||||||
|
>
|
||||||
|
{{ 'New' | i18n }}
|
||||||
</button>
|
</button>
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
@@ -59,24 +63,29 @@ const ERROR =
|
|||||||
<td class="name">{{ target.entry.path.split('/').pop() }}</td>
|
<td class="name">{{ target.entry.path.split('/').pop() }}</td>
|
||||||
<td>{{ target.entry.hostname }}</td>
|
<td>{{ target.entry.hostname }}</td>
|
||||||
<td>{{ target.entry.path }}</td>
|
<td>{{ target.entry.path }}</td>
|
||||||
<td>
|
<td (click)="$event.stopPropagation()">
|
||||||
<button
|
<button
|
||||||
tuiIconButton
|
tuiIconButton
|
||||||
|
tuiDropdown
|
||||||
size="s"
|
size="s"
|
||||||
appearance="action-destructive"
|
appearance="flat-grayscale"
|
||||||
iconStart="@tui.trash"
|
iconStart="@tui.ellipsis-vertical"
|
||||||
(click.stop)="forget(target, $index)"
|
[tuiDropdownOpen]="!!opens[$index]"
|
||||||
|
(tuiDropdownOpenChange)="opens[$index] = $event"
|
||||||
>
|
>
|
||||||
Forget
|
{{ 'More' | i18n }}
|
||||||
</button>
|
<tui-data-list *tuiDropdown>
|
||||||
<button
|
<button tuiOption (click)="edit(target)">
|
||||||
tuiIconButton
|
{{ 'Edit' | i18n }}
|
||||||
appearance="icon"
|
</button>
|
||||||
size="xs"
|
<button
|
||||||
iconStart="@tui.pencil"
|
tuiOption
|
||||||
(click.stop)="edit(target)"
|
class="g-negative"
|
||||||
>
|
(click)="forget(target, $index)"
|
||||||
Edit
|
>
|
||||||
|
{{ 'Delete' | i18n }}
|
||||||
|
</button>
|
||||||
|
</tui-data-list>
|
||||||
</button>
|
</button>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
@@ -106,7 +115,7 @@ const ERROR =
|
|||||||
}
|
}
|
||||||
|
|
||||||
td:first-child {
|
td:first-child {
|
||||||
width: 13rem;
|
width: 16rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
td:last-child {
|
td:last-child {
|
||||||
@@ -114,10 +123,6 @@ const ERROR =
|
|||||||
text-align: right;
|
text-align: right;
|
||||||
}
|
}
|
||||||
|
|
||||||
[tuiButton] {
|
|
||||||
margin-inline-start: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
span {
|
span {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
@@ -166,8 +171,9 @@ const ERROR =
|
|||||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||||
imports: [
|
imports: [
|
||||||
TuiButton,
|
TuiButton,
|
||||||
|
TuiDataList,
|
||||||
|
TuiDropdown,
|
||||||
TuiIcon,
|
TuiIcon,
|
||||||
TuiTooltip,
|
|
||||||
PlaceholderComponent,
|
PlaceholderComponent,
|
||||||
BackupStatusComponent,
|
BackupStatusComponent,
|
||||||
TableComponent,
|
TableComponent,
|
||||||
@@ -186,6 +192,8 @@ export class BackupNetworkComponent {
|
|||||||
readonly service = inject(BackupService)
|
readonly service = inject(BackupService)
|
||||||
readonly networkFolders = output<MappedBackupTarget<CifsBackupTarget>>()
|
readonly networkFolders = output<MappedBackupTarget<CifsBackupTarget>>()
|
||||||
|
|
||||||
|
opens: Record<number, boolean> = {}
|
||||||
|
|
||||||
select(target: MappedBackupTarget<CifsBackupTarget>) {
|
select(target: MappedBackupTarget<CifsBackupTarget>) {
|
||||||
if (!target.entry.mountable) {
|
if (!target.entry.mountable) {
|
||||||
this.dialog.openAlert(ERROR, { label: 'Unable to connect' }).subscribe()
|
this.dialog.openAlert(ERROR, { label: 'Unable to connect' }).subscribe()
|
||||||
@@ -321,7 +329,6 @@ export class BackupNetworkComponent {
|
|||||||
),
|
),
|
||||||
required: true,
|
required: true,
|
||||||
default: null,
|
default: null,
|
||||||
placeholder: 'My Network Folder',
|
|
||||||
}),
|
}),
|
||||||
password: ISB.Value.text({
|
password: ISB.Value.text({
|
||||||
name: this.i18n.transform('Password')!,
|
name: this.i18n.transform('Password')!,
|
||||||
@@ -331,7 +338,6 @@ export class BackupNetworkComponent {
|
|||||||
required: false,
|
required: false,
|
||||||
default: null,
|
default: null,
|
||||||
masked: true,
|
masked: true,
|
||||||
placeholder: 'My Network Folder',
|
|
||||||
}),
|
}),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,9 +5,8 @@ import {
|
|||||||
output,
|
output,
|
||||||
} from '@angular/core'
|
} from '@angular/core'
|
||||||
import { ActivatedRoute } from '@angular/router'
|
import { ActivatedRoute } from '@angular/router'
|
||||||
import { ConvertBytesPipe, DialogService, i18nPipe } from '@start9labs/shared'
|
import { DialogService, i18nPipe } from '@start9labs/shared'
|
||||||
import { TuiButton, TuiIcon } from '@taiga-ui/core'
|
import { TuiButton } from '@taiga-ui/core'
|
||||||
import { TuiTooltip } from '@taiga-ui/kit'
|
|
||||||
import { PlaceholderComponent } from 'src/app/routes/portal/components/placeholder.component'
|
import { PlaceholderComponent } from 'src/app/routes/portal/components/placeholder.component'
|
||||||
import { TableComponent } from 'src/app/routes/portal/components/table.component'
|
import { TableComponent } from 'src/app/routes/portal/components/table.component'
|
||||||
import { DiskBackupTarget } from 'src/app/services/api/api.types'
|
import { DiskBackupTarget } from 'src/app/services/api/api.types'
|
||||||
@@ -19,11 +18,9 @@ import { BackupStatusComponent } from './status.component'
|
|||||||
template: `
|
template: `
|
||||||
<header>
|
<header>
|
||||||
{{ 'Physical Drives' | i18n }}
|
{{ 'Physical Drives' | i18n }}
|
||||||
<tui-icon [tuiTooltip]="drives" />
|
|
||||||
<ng-template #drives><ng-content /></ng-template>
|
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
<table [appTable]="['Status', 'Name', 'Model', 'Capacity']">
|
<table [appTable]="['Status', 'Logicalname', 'Name', 'Capacity']">
|
||||||
@for (target of service.drives(); track $index) {
|
@for (target of service.drives(); track $index) {
|
||||||
<tr
|
<tr
|
||||||
tabindex="0"
|
tabindex="0"
|
||||||
@@ -33,14 +30,9 @@ import { BackupStatusComponent } from './status.component'
|
|||||||
<td>
|
<td>
|
||||||
<span [backupStatus]="target.hasAnyBackup" [physical]="true"></span>
|
<span [backupStatus]="target.hasAnyBackup" [physical]="true"></span>
|
||||||
</td>
|
</td>
|
||||||
<td class="name">
|
<td class="name">{{ target.entry.logicalname }}</td>
|
||||||
{{ target.entry.label || target.entry.logicalname }}
|
<td>{{ driveName(target.entry) }}</td>
|
||||||
</td>
|
<td>{{ formatCapacity(target.entry.capacity) }}</td>
|
||||||
<td>
|
|
||||||
{{ target.entry.vendor || 'Unknown Vendor' }} -
|
|
||||||
{{ target.entry.model || 'Unknown Model' }}
|
|
||||||
</td>
|
|
||||||
<td>{{ target.entry.capacity | convertBytes }}</td>
|
|
||||||
</tr>
|
</tr>
|
||||||
} @empty {
|
} @empty {
|
||||||
<tr>
|
<tr>
|
||||||
@@ -74,10 +66,6 @@ import { BackupStatusComponent } from './status.component'
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
td:first-child {
|
|
||||||
width: 13rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
:host-context(tui-root._mobile) {
|
:host-context(tui-root._mobile) {
|
||||||
tr {
|
tr {
|
||||||
grid-template-columns: min-content 1fr 4rem;
|
grid-template-columns: min-content 1fr 4rem;
|
||||||
@@ -109,9 +97,6 @@ import { BackupStatusComponent } from './status.component'
|
|||||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||||
imports: [
|
imports: [
|
||||||
TuiButton,
|
TuiButton,
|
||||||
TuiIcon,
|
|
||||||
TuiTooltip,
|
|
||||||
ConvertBytesPipe,
|
|
||||||
PlaceholderComponent,
|
PlaceholderComponent,
|
||||||
BackupStatusComponent,
|
BackupStatusComponent,
|
||||||
TableComponent,
|
TableComponent,
|
||||||
@@ -122,9 +107,26 @@ export class BackupPhysicalComponent {
|
|||||||
private readonly dialog = inject(DialogService)
|
private readonly dialog = inject(DialogService)
|
||||||
private readonly type = inject(ActivatedRoute).snapshot.data['type']
|
private readonly type = inject(ActivatedRoute).snapshot.data['type']
|
||||||
|
|
||||||
|
private readonly i18n = inject(i18nPipe)
|
||||||
|
|
||||||
readonly service = inject(BackupService)
|
readonly service = inject(BackupService)
|
||||||
readonly physicalFolders = output<MappedBackupTarget<DiskBackupTarget>>()
|
readonly physicalFolders = output<MappedBackupTarget<DiskBackupTarget>>()
|
||||||
|
|
||||||
|
driveName(entry: DiskBackupTarget): string {
|
||||||
|
return (
|
||||||
|
[entry.vendor, entry.model].filter(Boolean).join(' ') ||
|
||||||
|
this.i18n.transform('Unknown Drive')
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
formatCapacity(bytes: number): string {
|
||||||
|
const gb = bytes / 1e9
|
||||||
|
if (gb >= 1000) {
|
||||||
|
return `${(gb / 1000).toFixed(1)} TB`
|
||||||
|
}
|
||||||
|
return `${gb.toFixed(0)} GB`
|
||||||
|
}
|
||||||
|
|
||||||
select(target: MappedBackupTarget<DiskBackupTarget>) {
|
select(target: MappedBackupTarget<DiskBackupTarget>) {
|
||||||
if (this.type === 'restore' && !target.hasAnyBackup) {
|
if (this.type === 'restore' && !target.hasAnyBackup) {
|
||||||
this.dialog
|
this.dialog
|
||||||
|
|||||||
@@ -22,24 +22,42 @@ import { DataModel } from 'src/app/services/patch-db/data-model'
|
|||||||
</span>
|
</span>
|
||||||
<span tuiTitle>
|
<span tuiTitle>
|
||||||
{{ (pkg.value | toManifest).title }}
|
{{ (pkg.value | toManifest).title }}
|
||||||
<span tuiSubtitle>
|
</span>
|
||||||
@if (progress.complete) {
|
<span class="status">
|
||||||
<tui-icon icon="@tui.check" class="g-positive" />
|
@if (progress.complete) {
|
||||||
{{ 'complete' | i18n }}
|
<tui-icon icon="@tui.check" class="g-positive" />
|
||||||
|
{{ 'complete' | i18n }}
|
||||||
|
} @else {
|
||||||
|
@if ((pkg.key | tuiMapper: toStatus | async) === 'backing-up') {
|
||||||
|
<tui-loader size="s" />
|
||||||
|
{{ 'backing up' | i18n }}
|
||||||
} @else {
|
} @else {
|
||||||
@if ((pkg.key | tuiMapper: toStatus | async) === 'backing-up') {
|
<tui-icon icon="@tui.clock" />
|
||||||
<tui-loader size="s" />
|
{{ 'waiting' | i18n }}
|
||||||
{{ 'backing up' | i18n }}
|
|
||||||
} @else {
|
|
||||||
{{ 'waiting' | i18n }}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
</span>
|
}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
`,
|
`,
|
||||||
|
styles: `
|
||||||
|
:host {
|
||||||
|
max-width: 36rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.status {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.25rem;
|
||||||
|
margin-inline-start: auto;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.status tui-icon {
|
||||||
|
font-size: 1rem;
|
||||||
|
}
|
||||||
|
`,
|
||||||
host: { class: 'g-card' },
|
host: { class: 'g-card' },
|
||||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||||
imports: [
|
imports: [
|
||||||
|
|||||||
@@ -3,12 +3,12 @@ import { ChangeDetectionStrategy, Component, inject } from '@angular/core'
|
|||||||
import {
|
import {
|
||||||
DialogService,
|
DialogService,
|
||||||
ErrorService,
|
ErrorService,
|
||||||
i18nPipe,
|
|
||||||
StartOSDiskInfo,
|
StartOSDiskInfo,
|
||||||
} from '@start9labs/shared'
|
} from '@start9labs/shared'
|
||||||
import { TuiCell, TuiTitle } from '@taiga-ui/core'
|
import { TuiButton } from '@taiga-ui/core'
|
||||||
import { TuiNotificationMiddleService } from '@taiga-ui/kit'
|
import { TuiNotificationMiddleService } from '@taiga-ui/kit'
|
||||||
import { injectContext, PolymorpheusComponent } from '@taiga-ui/polymorpheus'
|
import { injectContext, PolymorpheusComponent } from '@taiga-ui/polymorpheus'
|
||||||
|
import { TableComponent } from 'src/app/routes/portal/components/table.component'
|
||||||
import { ApiService } from 'src/app/services/api/embassy-api.service'
|
import { ApiService } from 'src/app/services/api/embassy-api.service'
|
||||||
import { verifyPassword } from 'src/app/utils/verify-password'
|
import { verifyPassword } from 'src/app/utils/verify-password'
|
||||||
import { BackupContext } from './backup.types'
|
import { BackupContext } from './backup.types'
|
||||||
@@ -16,31 +16,49 @@ import { RECOVER } from './recover.component'
|
|||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
template: `
|
template: `
|
||||||
@for (server of target.entry.startOs | keyvalue; track $index) {
|
<table [appTable]="['Hostname', 'StartOS Version', 'Created', null]">
|
||||||
<button
|
@for (server of target.entry.startOs | keyvalue; track $index) {
|
||||||
tuiCell
|
<tr>
|
||||||
class="g-stretch"
|
<td class="name">{{ server.value.hostname }}.local</td>
|
||||||
(click)="onClick(server.key, server.value)"
|
<td>{{ server.value.version }}</td>
|
||||||
>
|
<td>{{ server.value.timestamp | date: 'medium' }}</td>
|
||||||
<span tuiTitle>
|
<td>
|
||||||
<span tuiSubtitle>
|
<button
|
||||||
<b>{{ 'Local Hostname' | i18n }}</b>
|
tuiButton
|
||||||
: {{ server.value.hostname }}.local
|
size="s"
|
||||||
</span>
|
(click)="onClick(server.key, server.value)"
|
||||||
<span tuiSubtitle>
|
>
|
||||||
<b>{{ 'StartOS Version' | i18n }}</b>
|
Select
|
||||||
: {{ server.value.version }}
|
</button>
|
||||||
</span>
|
</td>
|
||||||
<span tuiSubtitle>
|
</tr>
|
||||||
<b>{{ 'Created' | i18n }}</b>
|
}
|
||||||
: {{ server.value.timestamp | date: 'medium' }}
|
</table>
|
||||||
</span>
|
`,
|
||||||
</span>
|
styles: `
|
||||||
</button>
|
td:last-child {
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
:host-context(tui-root._mobile) {
|
||||||
|
tr {
|
||||||
|
grid-template-columns: 1fr auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.name {
|
||||||
|
color: var(--tui-text-primary);
|
||||||
|
font: var(--tui-typography-body-m);
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
td:last-child {
|
||||||
|
grid-area: 1 / 2 / 4 / 2;
|
||||||
|
align-self: center;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
`,
|
`,
|
||||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||||
imports: [KeyValuePipe, DatePipe, TuiCell, TuiTitle, i18nPipe],
|
imports: [KeyValuePipe, DatePipe, TuiButton, TableComponent],
|
||||||
})
|
})
|
||||||
export class BackupRestoreComponent {
|
export class BackupRestoreComponent {
|
||||||
private readonly dialog = inject(DialogService)
|
private readonly dialog = inject(DialogService)
|
||||||
@@ -82,7 +100,7 @@ export class BackupRestoreComponent {
|
|||||||
|
|
||||||
this.context.$implicit.complete()
|
this.context.$implicit.complete()
|
||||||
this.dialog
|
this.dialog
|
||||||
.openComponent(RECOVER, { label: 'Select services to restore', data })
|
.openComponent(RECOVER, { label: 'Select services', data })
|
||||||
.subscribe()
|
.subscribe()
|
||||||
} finally {
|
} finally {
|
||||||
loader.unsubscribe()
|
loader.unsubscribe()
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ import { TuiIcon } from '@taiga-ui/core'
|
|||||||
template: `
|
template: `
|
||||||
@if (type === 'create') {
|
@if (type === 'create') {
|
||||||
<tui-icon
|
<tui-icon
|
||||||
[icon]="physical() ? '@tui.ethernet-port' : '@tui.signal-high'"
|
[icon]="physical() ? '@tui.hard-drive' : '@tui.signal-high'"
|
||||||
class="g-positive"
|
class="g-positive"
|
||||||
/>
|
/>
|
||||||
{{ 'Available for backup' | i18n }}
|
{{ 'Available for backup' | i18n }}
|
||||||
@@ -22,7 +22,7 @@ import { TuiIcon } from '@taiga-ui/core'
|
|||||||
<tui-icon icon="@tui.save" class="g-positive" />
|
<tui-icon icon="@tui.save" class="g-positive" />
|
||||||
{{ 'StartOS backups detected' | i18n }}
|
{{ 'StartOS backups detected' | i18n }}
|
||||||
} @else {
|
} @else {
|
||||||
<tui-icon icon="@tui.save-off" class="g-negative" />
|
<tui-icon icon="@tui.file-x" class="g-negative" />
|
||||||
{{ 'No StartOS backups detected' | i18n }}
|
{{ 'No StartOS backups detected' | i18n }}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -37,6 +37,8 @@ import { TuiIcon } from '@taiga-ui/core'
|
|||||||
|
|
||||||
tui-icon {
|
tui-icon {
|
||||||
font-size: 1rem;
|
font-size: 1rem;
|
||||||
|
min-width: 1.25rem;
|
||||||
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
:host-context(tui-root._mobile) {
|
:host-context(tui-root._mobile) {
|
||||||
|
|||||||
@@ -27,10 +27,20 @@ const ipv6 =
|
|||||||
@Component({
|
@Component({
|
||||||
template: `
|
template: `
|
||||||
<ng-container *title>
|
<ng-container *title>
|
||||||
<a routerLink=".." tuiIconButton iconStart="@tui.arrow-left">
|
<div>
|
||||||
{{ 'Back' | i18n }}
|
<a routerLink=".." tuiIconButton iconStart="@tui.arrow-left">
|
||||||
</a>
|
{{ 'Back' | i18n }}
|
||||||
{{ 'DNS Servers' | i18n }}
|
</a>
|
||||||
|
{{ 'DNS Servers' | i18n }}
|
||||||
|
<a
|
||||||
|
tuiIconButton
|
||||||
|
size="xs"
|
||||||
|
docsLink
|
||||||
|
path="/start-os/dns.html"
|
||||||
|
appearance="icon"
|
||||||
|
iconStart="@tui.book-open-text"
|
||||||
|
></a>
|
||||||
|
</div>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
@if (data(); as d) {
|
@if (data(); as d) {
|
||||||
<form [formGroup]="d.form">
|
<form [formGroup]="d.form">
|
||||||
@@ -76,6 +86,10 @@ const ipv6 =
|
|||||||
max-width: 36rem;
|
max-width: 36rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
:host-context(tui-root._mobile) [tuiHeader] {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
form header,
|
form header,
|
||||||
form footer {
|
form footer {
|
||||||
margin: 1rem 0;
|
margin: 1rem 0;
|
||||||
|
|||||||
@@ -29,10 +29,20 @@ import { GatewaysTableComponent } from './table.component'
|
|||||||
@Component({
|
@Component({
|
||||||
template: `
|
template: `
|
||||||
<ng-container *title>
|
<ng-container *title>
|
||||||
<a routerLink=".." tuiIconButton iconStart="@tui.arrow-left">
|
<div>
|
||||||
{{ 'Back' | i18n }}
|
<a routerLink=".." tuiIconButton iconStart="@tui.arrow-left">
|
||||||
</a>
|
{{ 'Back' | i18n }}
|
||||||
{{ 'Gateways' | i18n }}
|
</a>
|
||||||
|
{{ 'Gateways' | i18n }}
|
||||||
|
<a
|
||||||
|
tuiIconButton
|
||||||
|
size="xs"
|
||||||
|
docsLink
|
||||||
|
path="/start-os/gateways.html"
|
||||||
|
appearance="icon"
|
||||||
|
iconStart="@tui.book-open-text"
|
||||||
|
></a>
|
||||||
|
</div>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
|
|
||||||
<section class="g-card">
|
<section class="g-card">
|
||||||
@@ -121,6 +131,10 @@ import { GatewaysTableComponent } from './table.component'
|
|||||||
}
|
}
|
||||||
`,
|
`,
|
||||||
styles: `
|
styles: `
|
||||||
|
:host-context(tui-root._mobile) .g-card > header > [docsLink] {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
.outbound {
|
.outbound {
|
||||||
max-width: 24rem;
|
max-width: 24rem;
|
||||||
margin-top: 2rem;
|
margin-top: 2rem;
|
||||||
|
|||||||
@@ -40,10 +40,20 @@ function detectProviderKey(host: string | undefined): string {
|
|||||||
@Component({
|
@Component({
|
||||||
template: `
|
template: `
|
||||||
<ng-container *title>
|
<ng-container *title>
|
||||||
<a routerLink=".." tuiIconButton iconStart="@tui.arrow-left">
|
<div>
|
||||||
{{ 'Back' | i18n }}
|
<a routerLink=".." tuiIconButton iconStart="@tui.arrow-left">
|
||||||
</a>
|
{{ 'Back' | i18n }}
|
||||||
SMTP
|
</a>
|
||||||
|
SMTP
|
||||||
|
<a
|
||||||
|
tuiIconButton
|
||||||
|
size="xs"
|
||||||
|
docsLink
|
||||||
|
path="/start-os/smtp.html"
|
||||||
|
appearance="icon"
|
||||||
|
iconStart="@tui.book-open-text"
|
||||||
|
></a>
|
||||||
|
</div>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
@if (form$ | async; as data) {
|
@if (form$ | async; as data) {
|
||||||
<form [formGroup]="data.form">
|
<form [formGroup]="data.form">
|
||||||
@@ -129,6 +139,10 @@ function detectProviderKey(host: string | undefined): string {
|
|||||||
max-width: 36rem;
|
max-width: 36rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
:host-context(tui-root._mobile) form:first-of-type > [tuiHeader] {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
form header,
|
form header,
|
||||||
form footer {
|
form footer {
|
||||||
margin: 1rem 0;
|
margin: 1rem 0;
|
||||||
|
|||||||
@@ -26,15 +26,11 @@ import { SSHTableComponent } from './table.component'
|
|||||||
@Component({
|
@Component({
|
||||||
template: `
|
template: `
|
||||||
<ng-container *title>
|
<ng-container *title>
|
||||||
<a routerLink=".." tuiIconButton iconStart="@tui.arrow-left">
|
<div>
|
||||||
{{ 'Back' | i18n }}
|
<a routerLink=".." tuiIconButton iconStart="@tui.arrow-left">
|
||||||
</a>
|
{{ 'Back' | i18n }}
|
||||||
SSH
|
</a>
|
||||||
</ng-container>
|
SSH
|
||||||
@let keys = keys$ | async;
|
|
||||||
<section class="g-card">
|
|
||||||
<header>
|
|
||||||
{{ 'SSH Keys' | i18n }}
|
|
||||||
<a
|
<a
|
||||||
tuiIconButton
|
tuiIconButton
|
||||||
size="xs"
|
size="xs"
|
||||||
@@ -42,9 +38,13 @@ import { SSHTableComponent } from './table.component'
|
|||||||
path="/start-os/ssh.html"
|
path="/start-os/ssh.html"
|
||||||
appearance="icon"
|
appearance="icon"
|
||||||
iconStart="@tui.book-open-text"
|
iconStart="@tui.book-open-text"
|
||||||
>
|
></a>
|
||||||
{{ 'Documentation' | i18n }}
|
</div>
|
||||||
</a>
|
</ng-container>
|
||||||
|
@let keys = keys$ | async;
|
||||||
|
<section class="g-card">
|
||||||
|
<header>
|
||||||
|
{{ 'SSH Keys' | i18n }}
|
||||||
<button
|
<button
|
||||||
tuiButton
|
tuiButton
|
||||||
size="xs"
|
size="xs"
|
||||||
|
|||||||
@@ -52,10 +52,20 @@ import { wifiSpec } from './wifi.const'
|
|||||||
@Component({
|
@Component({
|
||||||
template: `
|
template: `
|
||||||
<ng-container *title>
|
<ng-container *title>
|
||||||
<a routerLink=".." tuiIconButton iconStart="@tui.arrow-left">
|
<div>
|
||||||
{{ 'Back' | i18n }}
|
<a routerLink=".." tuiIconButton iconStart="@tui.arrow-left">
|
||||||
</a>
|
{{ 'Back' | i18n }}
|
||||||
WiFi
|
</a>
|
||||||
|
WiFi
|
||||||
|
<a
|
||||||
|
tuiIconButton
|
||||||
|
size="xs"
|
||||||
|
docsLink
|
||||||
|
path="/start-os/wifi.html"
|
||||||
|
appearance="icon"
|
||||||
|
iconStart="@tui.book-open-text"
|
||||||
|
></a>
|
||||||
|
</div>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
<section class="g-card">
|
<section class="g-card">
|
||||||
<header>
|
<header>
|
||||||
@@ -108,7 +118,7 @@ import { wifiSpec } from './wifi.const'
|
|||||||
<div tuiCardLarge="compact" [wifi]="data.available"></div>
|
<div tuiCardLarge="compact" [wifi]="data.available"></div>
|
||||||
}
|
}
|
||||||
<p>
|
<p>
|
||||||
<button tuiButton (click)="other(data)" appearance="flat">
|
<button tuiButton (click)="other(data)" appearance="grayscale">
|
||||||
+ {{ 'Connect to hidden network' | i18n }}
|
+ {{ 'Connect to hidden network' | i18n }}
|
||||||
</button>
|
</button>
|
||||||
</p>
|
</p>
|
||||||
@@ -131,6 +141,10 @@ import { wifiSpec } from './wifi.const'
|
|||||||
:host {
|
:host {
|
||||||
max-width: 36rem;
|
max-width: 36rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
:host-context(tui-root._mobile) header > [docsLink] {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
`,
|
`,
|
||||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||||
imports: [
|
imports: [
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ export class NotificationService {
|
|||||||
getColor(notification: ServerNotification<number>): string {
|
getColor(notification: ServerNotification<number>): string {
|
||||||
switch (notification.level) {
|
switch (notification.level) {
|
||||||
case 'info':
|
case 'info':
|
||||||
return 'var(--tui-status-info)'
|
return 'var(--tui-text-secondary)'
|
||||||
case 'success':
|
case 'success':
|
||||||
return 'var(--tui-status-positive)'
|
return 'var(--tui-status-positive)'
|
||||||
case 'warning':
|
case 'warning':
|
||||||
@@ -62,26 +62,42 @@ export class NotificationService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
viewModal(notification: ServerNotification<number>, full = false) {
|
viewModal(notification: ServerNotification<number>) {
|
||||||
const { data, createdAt, code, title, message } = notification
|
const { data, createdAt, code, title, message } = notification
|
||||||
|
|
||||||
if (code === 1) {
|
switch (code) {
|
||||||
// Backup Report
|
// Backup report - structured report with per-service results
|
||||||
this.dialogs
|
case 1:
|
||||||
.openComponent(full ? message : REPORT, {
|
this.dialogs
|
||||||
label: 'Backup Report',
|
.openComponent(REPORT, {
|
||||||
data: { content: data, createdAt },
|
label: 'Backup Report',
|
||||||
})
|
data: { content: data, createdAt },
|
||||||
.subscribe()
|
size: 'l',
|
||||||
} else {
|
})
|
||||||
// Markdown viewer
|
.subscribe()
|
||||||
this.dialogs
|
break
|
||||||
.openComponent(full ? message : MARKDOWN, {
|
|
||||||
label: title as i18nKey,
|
// OS update - data contains the full release notes markdown
|
||||||
data: of(data),
|
case 2:
|
||||||
size: 'l',
|
this.dialogs
|
||||||
})
|
.openComponent(MARKDOWN, {
|
||||||
.subscribe()
|
label: title as i18nKey,
|
||||||
|
data: of(data),
|
||||||
|
size: 'l',
|
||||||
|
})
|
||||||
|
.subscribe()
|
||||||
|
break
|
||||||
|
|
||||||
|
// General notification - show the message text
|
||||||
|
default:
|
||||||
|
this.dialogs
|
||||||
|
.openComponent(MARKDOWN, {
|
||||||
|
label: title as i18nKey,
|
||||||
|
data: of(message),
|
||||||
|
size: 'l',
|
||||||
|
})
|
||||||
|
.subscribe()
|
||||||
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user