From e6af7e9885e40e2567e0c8fdd3a1637e42c924ec Mon Sep 17 00:00:00 2001 From: Alex Inkin Date: Thu, 27 Mar 2025 16:41:47 +0400 Subject: [PATCH] feat: finalize desktop and mobile design of system routes (#2855) * feat: finalize desktop and mobile design of system routes * clean up messaging and mobile tabbar utilities --------- Co-authored-by: Matt Hill --- web/package-lock.json | 200 +++++++++--------- web/package.json | 22 +- web/projects/shared/styles/taiga.scss | 17 -- .../ui/src/app/i18n/i18n.providers.ts | 7 +- .../interfaces/actions.component.ts | 4 +- .../interfaces/interface.component.ts | 8 - .../portal/components/table.component.ts | 5 + .../portal/components/tabs.component.ts | 21 -- .../services/dashboard/dashboard.component.ts | 4 + .../system/routes/acme/acme.component.ts | 32 ++- .../system/routes/acme/info.component.ts | 24 --- .../routes/backups/backups.component.ts | 108 ++++++++-- .../routes/backups/network.component.ts | 160 +++++++++----- .../routes/backups/physical.component.ts | 123 +++++++---- .../system/routes/backups/status.component.ts | 9 +- .../system/routes/email/email.component.ts | 73 +++++-- .../system/routes/email/info.component.ts | 33 --- .../routes/interfaces/interfaces.component.ts | 21 +- .../routes/password/password.component.ts | 39 ++-- .../routes/sessions/sessions.component.ts | 23 +- .../system/routes/sessions/table.component.ts | 116 +++++----- .../system/routes/ssh/info.component.ts | 24 --- .../routes/system/routes/ssh/ssh.component.ts | 29 ++- .../system/routes/ssh/table.component.ts | 25 +-- .../system/routes/wifi/info.component.ts | 25 --- .../system/routes/wifi/table.component.ts | 27 ++- .../system/routes/wifi/wifi.component.ts | 39 +++- .../portal/routes/system/system.component.ts | 10 +- .../ui/src/app/utils/system-utilities.ts | 1 + web/projects/ui/src/styles.scss | 18 +- 30 files changed, 701 insertions(+), 546 deletions(-) delete mode 100644 web/projects/ui/src/app/routes/portal/routes/system/routes/acme/info.component.ts delete mode 100644 web/projects/ui/src/app/routes/portal/routes/system/routes/email/info.component.ts delete mode 100644 web/projects/ui/src/app/routes/portal/routes/system/routes/ssh/info.component.ts delete mode 100644 web/projects/ui/src/app/routes/portal/routes/system/routes/wifi/info.component.ts diff --git a/web/package-lock.json b/web/package-lock.json index 26b9d7c95..39a3138f0 100644 --- a/web/package-lock.json +++ b/web/package-lock.json @@ -25,17 +25,17 @@ "@noble/hashes": "^1.4.0", "@start9labs/argon2": "^0.2.2", "@start9labs/start-sdk": "file:../sdk/baseDist", - "@taiga-ui/addon-charts": "4.28.0", - "@taiga-ui/addon-commerce": "4.28.0", - "@taiga-ui/addon-mobile": "4.28.0", - "@taiga-ui/addon-table": "4.28.0", - "@taiga-ui/cdk": "4.28.0", - "@taiga-ui/core": "4.28.0", - "@taiga-ui/event-plugins": "4.4.1", - "@taiga-ui/icons": "4.28.0", - "@taiga-ui/kit": "4.28.0", - "@taiga-ui/layout": "4.28.0", - "@taiga-ui/legacy": "4.28.0", + "@taiga-ui/addon-charts": "4.30.0", + "@taiga-ui/addon-commerce": "4.30.0", + "@taiga-ui/addon-mobile": "4.30.0", + "@taiga-ui/addon-table": "4.30.0", + "@taiga-ui/cdk": "4.30.0", + "@taiga-ui/core": "4.30.0", + "@taiga-ui/event-plugins": "4.5.0", + "@taiga-ui/icons": "4.30.0", + "@taiga-ui/kit": "4.30.0", + "@taiga-ui/layout": "4.30.0", + "@taiga-ui/legacy": "4.30.0", "@taiga-ui/polymorpheus": "4.9.0", "@tinkoff/ng-dompurify": "4.0.0", "ansi-to-html": "^0.7.2", @@ -3422,9 +3422,9 @@ } }, "node_modules/@maskito/angular": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/@maskito/angular/-/angular-3.4.0.tgz", - "integrity": "sha512-iMFP/siEgU9Ki+g1PReZlA5+LlBMp6inqXGG5KCezhmDleZnG5lL9gxk3+ktJvKu+2kayLcwyBeUKXPwMBVt9w==", + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/@maskito/angular/-/angular-3.5.0.tgz", + "integrity": "sha512-5uwar32qsGdZNHUgZpFnICg9tJKCXbZEGk2ZnchHzDIfN5ojNT7wKzoq8NhpRlGb3p4qQCE+PXb5GERkcWM/Sw==", "license": "Apache-2.0", "peer": true, "dependencies": { @@ -3433,35 +3433,35 @@ "peerDependencies": { "@angular/core": ">=16.0.0", "@angular/forms": ">=16.0.0", - "@maskito/core": "^3.4.0" + "@maskito/core": "^3.5.0" } }, "node_modules/@maskito/core": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/@maskito/core/-/core-3.4.0.tgz", - "integrity": "sha512-gFM6qk675YwOEGhxu9Xm6/sl1TZBRab6+B3Gstqml7xJopHHZ0rUOrWXwmX0z2JI+1PsgUL/ftV/CSZ8CpIONg==", + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/@maskito/core/-/core-3.5.0.tgz", + "integrity": "sha512-zgmBjXeXc7BSBaw8jQw25dnwkFmKDvdj5rHzhEIxYhgGtnpli236F0YWPIOYzIwADjbefwDq1o7qpJfMsdDO4Q==", "license": "Apache-2.0", "peer": true }, "node_modules/@maskito/kit": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/@maskito/kit/-/kit-3.4.0.tgz", - "integrity": "sha512-jkexr7wjAqFeMpyc7s0IlinL+3F9xC4BYUHDQcEqlAJisDgVFtGCZZK/RvV1C+HGDn2gtzzVrJ3G/OY66k6EXg==", + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/@maskito/kit/-/kit-3.5.0.tgz", + "integrity": "sha512-QnpZsPTINgK4ScA4pMMJagoj+ufIXc/VGOP61AsQa/H/lmXII4pEZTLzpmMNUYmCEIEyjHR2DIbfEed04sktvQ==", "license": "Apache-2.0", "peer": true, "peerDependencies": { - "@maskito/core": "^3.4.0" + "@maskito/core": "^3.5.0" } }, "node_modules/@maskito/phone": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/@maskito/phone/-/phone-3.4.0.tgz", - "integrity": "sha512-KR6JuuWhTumIOCUV3CzPhh1niCXcuqsogNsLW3YfdmeVo8GygS9isnHNbSaAA/b9OnmIEkh25mur6x3yEJuYjA==", + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/@maskito/phone/-/phone-3.5.0.tgz", + "integrity": "sha512-qh/GGRFn8cZBY/JUTLa5yeSSKSVlekggKeiCbf0eX0I53/HM2pNZ/5667S8SXwn5WjIEeB79Eltl8MNvK74yvA==", "license": "Apache-2.0", "peer": true, "peerDependencies": { - "@maskito/core": "^3.4.0", - "@maskito/kit": "^3.4.0", + "@maskito/core": "^3.5.0", + "@maskito/kit": "^3.5.0", "libphonenumber-js": ">=1.0.0" } }, @@ -4417,9 +4417,9 @@ "link": true }, "node_modules/@taiga-ui/addon-charts": { - "version": "4.28.0", - "resolved": "https://registry.npmjs.org/@taiga-ui/addon-charts/-/addon-charts-4.28.0.tgz", - "integrity": "sha512-Lvi2R8Y50kBbfbru31YHon+CEpnOzAx0G4GnqjN2goTLNQ6iX7pgUeyRyiXI4ay1yLrzVIOZJhSmBwWSDocZEg==", + "version": "4.30.0", + "resolved": "https://registry.npmjs.org/@taiga-ui/addon-charts/-/addon-charts-4.30.0.tgz", + "integrity": "sha512-QrM2Oh4hUcg/I0K3KWFkc/dbTCYZn2n5GU2FSpZaK6I7pwjfRoMjBU7vswPLVVdmgeWTJxxoQlbfYnbUbkMAJw==", "license": "Apache-2.0", "dependencies": { "tslib": ">=2.8.1" @@ -4428,15 +4428,15 @@ "@angular/common": ">=16.0.0", "@angular/core": ">=16.0.0", "@ng-web-apis/common": "^4.11.1", - "@taiga-ui/cdk": "^4.28.0", - "@taiga-ui/core": "^4.28.0", - "@taiga-ui/polymorpheus": "^4.8.0" + "@taiga-ui/cdk": "^4.30.0", + "@taiga-ui/core": "^4.30.0", + "@taiga-ui/polymorpheus": "^4.9.0" } }, "node_modules/@taiga-ui/addon-commerce": { - "version": "4.28.0", - "resolved": "https://registry.npmjs.org/@taiga-ui/addon-commerce/-/addon-commerce-4.28.0.tgz", - "integrity": "sha512-VYygBL7oySCZYLBimGJPx/VGGtUGhpes3XwBHAPBmmyiVxct0kxXzhCQdAvNMQcSvDzXDBjg3wmJiUbZA/uHGQ==", + "version": "4.30.0", + "resolved": "https://registry.npmjs.org/@taiga-ui/addon-commerce/-/addon-commerce-4.30.0.tgz", + "integrity": "sha512-6diktxvxMpWjbEHXThS0pTrURdUiF/47jf2jdBFkMwX3BbbekisM1qkwxY24V7q8fN0IIxfO8CVEjTeLRrCw5g==", "license": "Apache-2.0", "dependencies": { "tslib": ">=2.8.1" @@ -4445,22 +4445,22 @@ "@angular/common": ">=16.0.0", "@angular/core": ">=16.0.0", "@angular/forms": ">=16.0.0", - "@maskito/angular": "^3.4.0", - "@maskito/core": "^3.4.0", - "@maskito/kit": "^3.4.0", + "@maskito/angular": "^3.5.0", + "@maskito/core": "^3.5.0", + "@maskito/kit": "^3.5.0", "@ng-web-apis/common": "^4.11.1", - "@taiga-ui/cdk": "^4.28.0", - "@taiga-ui/core": "^4.28.0", - "@taiga-ui/i18n": "^4.28.0", - "@taiga-ui/kit": "^4.28.0", - "@taiga-ui/polymorpheus": "^4.8.0", + "@taiga-ui/cdk": "^4.30.0", + "@taiga-ui/core": "^4.30.0", + "@taiga-ui/i18n": "^4.30.0", + "@taiga-ui/kit": "^4.30.0", + "@taiga-ui/polymorpheus": "^4.9.0", "rxjs": ">=7.0.0" } }, "node_modules/@taiga-ui/addon-mobile": { - "version": "4.28.0", - "resolved": "https://registry.npmjs.org/@taiga-ui/addon-mobile/-/addon-mobile-4.28.0.tgz", - "integrity": "sha512-1RRaX37Ddl24q4nHrMEz6iDqHWi/mkTyXQ+kADX7+ydx9JkbU2H4R+qXrOx4+GUi93Y05HvAWCNCToBu3Ytt2A==", + "version": "4.30.0", + "resolved": "https://registry.npmjs.org/@taiga-ui/addon-mobile/-/addon-mobile-4.30.0.tgz", + "integrity": "sha512-8cYyU0UDLUd74v+Zjs4m9S4AsSWchUojAexDLvaAHzfi0x+tdtA+ZN0h49v8AmOWHK0v69z4FMjyyc52p/jiDw==", "license": "Apache-2.0", "dependencies": { "tslib": ">=2.8.1" @@ -4470,18 +4470,18 @@ "@angular/common": ">=16.0.0", "@angular/core": ">=16.0.0", "@ng-web-apis/common": "^4.11.1", - "@taiga-ui/cdk": "^4.28.0", - "@taiga-ui/core": "^4.28.0", - "@taiga-ui/kit": "^4.28.0", - "@taiga-ui/layout": "^4.28.0", - "@taiga-ui/polymorpheus": "^4.8.0", + "@taiga-ui/cdk": "^4.30.0", + "@taiga-ui/core": "^4.30.0", + "@taiga-ui/kit": "^4.30.0", + "@taiga-ui/layout": "^4.30.0", + "@taiga-ui/polymorpheus": "^4.9.0", "rxjs": ">=7.0.0" } }, "node_modules/@taiga-ui/addon-table": { - "version": "4.28.0", - "resolved": "https://registry.npmjs.org/@taiga-ui/addon-table/-/addon-table-4.28.0.tgz", - "integrity": "sha512-C8MW6kJ3T9zy51rSxqYApll+S84oizK6C85gZyDM3gEV2RAlK2DP+r657ZlEwEgobrFCtBZe++TT7ZoKpQBBHg==", + "version": "4.30.0", + "resolved": "https://registry.npmjs.org/@taiga-ui/addon-table/-/addon-table-4.30.0.tgz", + "integrity": "sha512-OdCEwlrMs42Z2pINK1wvNk7OZmAlkj+mbgHTyMGdrUdA49dFZfYXNpVUCwVOqHAm2PDOeVN4ybZ8FSbzYefJyw==", "license": "Apache-2.0", "dependencies": { "tslib": ">=2.8.1" @@ -4490,18 +4490,18 @@ "@angular/common": ">=16.0.0", "@angular/core": ">=16.0.0", "@ng-web-apis/intersection-observer": "^4.11.1", - "@taiga-ui/cdk": "^4.28.0", - "@taiga-ui/core": "^4.28.0", - "@taiga-ui/i18n": "^4.28.0", - "@taiga-ui/kit": "^4.28.0", - "@taiga-ui/polymorpheus": "^4.8.0", + "@taiga-ui/cdk": "^4.30.0", + "@taiga-ui/core": "^4.30.0", + "@taiga-ui/i18n": "^4.30.0", + "@taiga-ui/kit": "^4.30.0", + "@taiga-ui/polymorpheus": "^4.9.0", "rxjs": ">=7.0.0" } }, "node_modules/@taiga-ui/cdk": { - "version": "4.28.0", - "resolved": "https://registry.npmjs.org/@taiga-ui/cdk/-/cdk-4.28.0.tgz", - "integrity": "sha512-P2vK+4WDnSt/nnilqxvDS4lyMAEH/M73z9YSzyH5mEwVTNxD3m82jJgpHqV5Re7geooAyaKqS6MJwDxaN0+9eQ==", + "version": "4.30.0", + "resolved": "https://registry.npmjs.org/@taiga-ui/cdk/-/cdk-4.30.0.tgz", + "integrity": "sha512-ndfnLOnL6vriItm5lq8/0slzj03CatkGVYG8zAT3fx00Vuam5Wf8Sh6h2ObqCFAljT7WJxHqMF9A1cBfLPI/iQ==", "license": "Apache-2.0", "dependencies": { "tslib": "2.8.1" @@ -4525,14 +4525,14 @@ "@ng-web-apis/resize-observer": "^4.11.1", "@ng-web-apis/screen-orientation": "^4.11.1", "@taiga-ui/event-plugins": "^4.4.1", - "@taiga-ui/polymorpheus": "^4.8.0", + "@taiga-ui/polymorpheus": "^4.9.0", "rxjs": ">=7.0.0" } }, "node_modules/@taiga-ui/core": { - "version": "4.28.0", - "resolved": "https://registry.npmjs.org/@taiga-ui/core/-/core-4.28.0.tgz", - "integrity": "sha512-4eP6PJvmHZCrV/9apxfu6Bgj7L72yjVg1R5c4j1MsVmMESLCCRGlk0hPPvuxVQ+ZYrOZwNeWyKHPZDPL5uQawA==", + "version": "4.30.0", + "resolved": "https://registry.npmjs.org/@taiga-ui/core/-/core-4.30.0.tgz", + "integrity": "sha512-IeZ6QBpSuv7k4bQx2BSDr8N3dDiMDwgnnwkkKqtJ0yJayZ/ZlCMq3nUQA0kg3VjH2spJeUbdqkDqpEuzrWJGkA==", "license": "Apache-2.0", "dependencies": { "tslib": ">=2.8.1" @@ -4546,17 +4546,17 @@ "@angular/router": ">=16.0.0", "@ng-web-apis/common": "^4.11.1", "@ng-web-apis/mutation-observer": "^4.11.1", - "@taiga-ui/cdk": "^4.28.0", + "@taiga-ui/cdk": "^4.30.0", "@taiga-ui/event-plugins": "^4.4.1", - "@taiga-ui/i18n": "^4.28.0", - "@taiga-ui/polymorpheus": "^4.8.0", + "@taiga-ui/i18n": "^4.30.0", + "@taiga-ui/polymorpheus": "^4.9.0", "rxjs": ">=7.0.0" } }, "node_modules/@taiga-ui/event-plugins": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/@taiga-ui/event-plugins/-/event-plugins-4.4.1.tgz", - "integrity": "sha512-gwEkgyZsbAdRfmb98KlKWivYVF88eP0bOtbHwfj8Ec8DgJ5809qFqeWvJEIxZZ829iox1m8z2UuVrqN2/tI1tQ==", + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/@taiga-ui/event-plugins/-/event-plugins-4.5.0.tgz", + "integrity": "sha512-bMW36eqr4Q+EnUM8ZNjx1Sw8POIAcyALY74xVPq9UHoQ3NqnRkeEDnZdfPhq9IYxtC3sO2BttNjWYcvBAkU2+A==", "license": "Apache-2.0", "dependencies": { "tslib": "^2.3.0" @@ -4568,9 +4568,9 @@ } }, "node_modules/@taiga-ui/i18n": { - "version": "4.28.0", - "resolved": "https://registry.npmjs.org/@taiga-ui/i18n/-/i18n-4.28.0.tgz", - "integrity": "sha512-kM7bbqllzir4nEk3X+YMKATm23UoKJeWSGmwnjLEmhWkpNAGqfErDRbE2puf+jXy7eufGhaB7ht/mK4+HkLXbw==", + "version": "4.30.0", + "resolved": "https://registry.npmjs.org/@taiga-ui/i18n/-/i18n-4.30.0.tgz", + "integrity": "sha512-OvtUqSRQE988XfiH1MS7Wd3Eg6dE1mkP2sqYRLw0HyE5Oc9hgHMwdPstSaoMN9aeJRVZnKXGsYmX4iaQ3x7drw==", "license": "Apache-2.0", "peer": true, "dependencies": { @@ -4583,18 +4583,18 @@ } }, "node_modules/@taiga-ui/icons": { - "version": "4.28.0", - "resolved": "https://registry.npmjs.org/@taiga-ui/icons/-/icons-4.28.0.tgz", - "integrity": "sha512-1QS7gvYHuTRUUodE58OXm+4Ree5FhFe0co0Lj+3sqeqkYb495z5q3CXBNiXD3y8IcDTjNuYkxKxEthbPnQrsVQ==", + "version": "4.30.0", + "resolved": "https://registry.npmjs.org/@taiga-ui/icons/-/icons-4.30.0.tgz", + "integrity": "sha512-EAbvw1ii4UVDgt9+5t7NQkV0WBqkVm5SGixH0ux8Vb4qhhLJJwp5xvXOCGt5QPzviT7nFGqXD6EqB23aYcuusg==", "license": "Apache-2.0", "dependencies": { "tslib": "^2.3.0" } }, "node_modules/@taiga-ui/kit": { - "version": "4.28.0", - "resolved": "https://registry.npmjs.org/@taiga-ui/kit/-/kit-4.28.0.tgz", - "integrity": "sha512-JEHUZhWU0vgPorvO3l9POzWKPbFQA57jFh9Iv5/RlWxMI8EUI+OKH5J8z1ptX+RJE2dWB9+Yi84zasgr8TWcSA==", + "version": "4.30.0", + "resolved": "https://registry.npmjs.org/@taiga-ui/kit/-/kit-4.30.0.tgz", + "integrity": "sha512-tCHZbsiq1u19ariarFuP9iwnNSxJGicQnYvJYy2+QojL65KsC9p8VgZv36rpggpuPEUXRXwmhyz2Qi6fwFcbLg==", "license": "Apache-2.0", "dependencies": { "tslib": ">=2.8.1" @@ -4604,25 +4604,25 @@ "@angular/core": ">=16.0.0", "@angular/forms": ">=16.0.0", "@angular/router": ">=16.0.0", - "@maskito/angular": "^3.4.0", - "@maskito/core": "^3.4.0", - "@maskito/kit": "^3.4.0", - "@maskito/phone": "^3.4.0", + "@maskito/angular": "^3.5.0", + "@maskito/core": "^3.5.0", + "@maskito/kit": "^3.5.0", + "@maskito/phone": "^3.5.0", "@ng-web-apis/common": "^4.11.1", "@ng-web-apis/intersection-observer": "^4.11.1", "@ng-web-apis/mutation-observer": "^4.11.1", "@ng-web-apis/resize-observer": "^4.11.1", - "@taiga-ui/cdk": "^4.28.0", - "@taiga-ui/core": "^4.28.0", - "@taiga-ui/i18n": "^4.28.0", - "@taiga-ui/polymorpheus": "^4.8.0", + "@taiga-ui/cdk": "^4.30.0", + "@taiga-ui/core": "^4.30.0", + "@taiga-ui/i18n": "^4.30.0", + "@taiga-ui/polymorpheus": "^4.9.0", "rxjs": ">=7.0.0" } }, "node_modules/@taiga-ui/layout": { - "version": "4.28.0", - "resolved": "https://registry.npmjs.org/@taiga-ui/layout/-/layout-4.28.0.tgz", - "integrity": "sha512-NlXdEmXGhYvTWeSSpGlT9XS0SU1aQDuFAMFBSDVsZqLPWh2DTnNsxSf1/b6UYMmX5JKXhH/bRVvX97N5L5XZqQ==", + "version": "4.30.0", + "resolved": "https://registry.npmjs.org/@taiga-ui/layout/-/layout-4.30.0.tgz", + "integrity": "sha512-DyIqpmXcv/OP4byt7L1f1iBKPysf3L+sj/dBpkeYvAUUnJnXnJsXav0j57d43VkXPn9lpGqz0gEBtzVDt7xxTw==", "license": "Apache-2.0", "dependencies": { "tslib": ">=2.8.1" @@ -4630,17 +4630,17 @@ "peerDependencies": { "@angular/common": ">=16.0.0", "@angular/core": ">=16.0.0", - "@taiga-ui/cdk": "^4.28.0", - "@taiga-ui/core": "^4.28.0", - "@taiga-ui/kit": "^4.28.0", - "@taiga-ui/polymorpheus": "^4.8.0", + "@taiga-ui/cdk": "^4.30.0", + "@taiga-ui/core": "^4.30.0", + "@taiga-ui/kit": "^4.30.0", + "@taiga-ui/polymorpheus": "^4.9.0", "rxjs": ">=7.0.0" } }, "node_modules/@taiga-ui/legacy": { - "version": "4.28.0", - "resolved": "https://registry.npmjs.org/@taiga-ui/legacy/-/legacy-4.28.0.tgz", - "integrity": "sha512-mWE5w7alYsT8GMBNTfcvrf/sJjh1li2/mTykH/aoWklgYHHmSt6moY4Myi8wKdlRFBzi82eXsvJcUSCwD8Y5ew==", + "version": "4.30.0", + "resolved": "https://registry.npmjs.org/@taiga-ui/legacy/-/legacy-4.30.0.tgz", + "integrity": "sha512-ebFJMddzlsq3TUAWxopn5Qju4REkC4bHzoYYx5OEzPq1VW1zmCvNC+X6usMnluhc9aS50UI8ZB7Xd3N4Zdgtfg==", "license": "Apache-2.0", "dependencies": { "tslib": ">=2.8.1" diff --git a/web/package.json b/web/package.json index 71792c535..f300f6fb2 100644 --- a/web/package.json +++ b/web/package.json @@ -47,17 +47,17 @@ "@noble/hashes": "^1.4.0", "@start9labs/argon2": "^0.2.2", "@start9labs/start-sdk": "file:../sdk/baseDist", - "@taiga-ui/addon-charts": "4.28.0", - "@taiga-ui/addon-commerce": "4.28.0", - "@taiga-ui/addon-mobile": "4.28.0", - "@taiga-ui/addon-table": "4.28.0", - "@taiga-ui/cdk": "4.28.0", - "@taiga-ui/core": "4.28.0", - "@taiga-ui/event-plugins": "4.4.1", - "@taiga-ui/icons": "4.28.0", - "@taiga-ui/kit": "4.28.0", - "@taiga-ui/layout": "4.28.0", - "@taiga-ui/legacy": "4.28.0", + "@taiga-ui/addon-charts": "4.30.0", + "@taiga-ui/addon-commerce": "4.30.0", + "@taiga-ui/addon-mobile": "4.30.0", + "@taiga-ui/addon-table": "4.30.0", + "@taiga-ui/cdk": "4.30.0", + "@taiga-ui/core": "4.30.0", + "@taiga-ui/event-plugins": "4.5.0", + "@taiga-ui/icons": "4.30.0", + "@taiga-ui/kit": "4.30.0", + "@taiga-ui/layout": "4.30.0", + "@taiga-ui/legacy": "4.30.0", "@taiga-ui/polymorpheus": "4.9.0", "@tinkoff/ng-dompurify": "4.0.0", "ansi-to-html": "^0.7.2", diff --git a/web/projects/shared/styles/taiga.scss b/web/projects/shared/styles/taiga.scss index 3edb110f8..68b4b97ca 100644 --- a/web/projects/shared/styles/taiga.scss +++ b/web/projects/shared/styles/taiga.scss @@ -162,20 +162,3 @@ tui-badge-notification { align-self: center !important; } } - -// TODO Remove after Taiga UI update -[tuiTitle] { - h1, - h2, - h3, - h4, - h5, - h6 { - font: inherit; - margin: 0; - } - - [tuiSubtitle] { - margin: 0; - } -} diff --git a/web/projects/ui/src/app/i18n/i18n.providers.ts b/web/projects/ui/src/app/i18n/i18n.providers.ts index 4d44720b9..8239f6edd 100644 --- a/web/projects/ui/src/app/i18n/i18n.providers.ts +++ b/web/projects/ui/src/app/i18n/i18n.providers.ts @@ -1,4 +1,4 @@ -import { signal } from '@angular/core' +import { forwardRef, signal } from '@angular/core' import { tuiCreateToken, tuiProvide } from '@taiga-ui/cdk' import { TuiLanguageName, @@ -34,5 +34,8 @@ export const I18N_PROVIDERS = [ } }, }, - tuiProvide(TuiLanguageSwitcherService, i18nService), + tuiProvide( + TuiLanguageSwitcherService, + forwardRef(() => i18nService), + ), ] diff --git a/web/projects/ui/src/app/routes/portal/components/interfaces/actions.component.ts b/web/projects/ui/src/app/routes/portal/components/interfaces/actions.component.ts index 7f5c97ea7..3c35544b1 100644 --- a/web/projects/ui/src/app/routes/portal/components/interfaces/actions.component.ts +++ b/web/projects/ui/src/app/routes/portal/components/interfaces/actions.component.ts @@ -87,9 +87,7 @@ import { InterfaceComponent } from './interface.component' :host { text-align: right; grid-area: 1 / 2 / 3 / 3; - } - - .desktop { + place-content: center; } .mobile { diff --git a/web/projects/ui/src/app/routes/portal/components/interfaces/interface.component.ts b/web/projects/ui/src/app/routes/portal/components/interfaces/interface.component.ts index 1f051808c..42ff76a6f 100644 --- a/web/projects/ui/src/app/routes/portal/components/interfaces/interface.component.ts +++ b/web/projects/ui/src/app/routes/portal/components/interfaces/interface.component.ts @@ -19,14 +19,6 @@ import { MappedServiceInterface } from './interface.utils' flex-direction: column; gap: 1rem; } - - section { - padding-block-end: 1rem; - } - - :host-context(tui-root:not(._mobile)) section ::ng-deep > header { - background: none; - } `, providers: [tuiButtonOptionsProvider({ size: 'xs' })], changeDetection: ChangeDetectionStrategy.OnPush, diff --git a/web/projects/ui/src/app/routes/portal/components/table.component.ts b/web/projects/ui/src/app/routes/portal/components/table.component.ts index 14dc29dd3..d8b5af656 100644 --- a/web/projects/ui/src/app/routes/portal/components/table.component.ts +++ b/web/projects/ui/src/app/routes/portal/components/table.component.ts @@ -13,6 +13,11 @@ import { ChangeDetectionStrategy, Component, input } from '@angular/core' `, + styles: ` + :host:has(app-placeholder) thead { + display: none; + } + `, host: { class: 'g-table' }, changeDetection: ChangeDetectionStrategy.OnPush, }) diff --git a/web/projects/ui/src/app/routes/portal/components/tabs.component.ts b/web/projects/ui/src/app/routes/portal/components/tabs.component.ts index a704d2e32..a34e8f454 100644 --- a/web/projects/ui/src/app/routes/portal/components/tabs.component.ts +++ b/web/projects/ui/src/app/routes/portal/components/tabs.component.ts @@ -10,9 +10,7 @@ import { RouterLink, RouterLinkActive } from '@angular/router' import { TuiResponsiveDialogService, TuiTabBar } from '@taiga-ui/addon-mobile' import { TuiIcon } from '@taiga-ui/core' import { TuiBadgeNotification } from '@taiga-ui/kit' -import { ABOUT } from 'src/app/routes/portal/components/header/about.component' import { BadgeService } from 'src/app/services/badge.service' -import { RESOURCES } from 'src/app/utils/resources' import { getMenu } from 'src/app/utils/system-utilities' const FILTER = ['/portal/services', '/portal/system', '/portal/marketplace'] @@ -72,20 +70,6 @@ const FILTER = ['/portal/services', '/portal/system', '/portal/marketplace'] } } - - @for (link of resources; track $index) { - - - {{ link.name }} - - - } @@ -138,7 +122,6 @@ export class TabsComponent { index = 3 - readonly resources = RESOURCES readonly menu = getMenu().filter(item => !FILTER.includes(item.routerLink)) readonly badge = toSignal(inject(BadgeService).getCount('/portal/system'), { initialValue: 0, @@ -148,10 +131,6 @@ export class TabsComponent { this.menu.reduce((acc, item) => acc + item.badge(), 0), ) - about() { - this.dialogs.open(ABOUT, { label: 'About this server' }).subscribe() - } - more(content: TemplateRef) { this.dialogs.open(content, { label: 'Start OS' }).subscribe({ complete: () => this.update(), diff --git a/web/projects/ui/src/app/routes/portal/routes/services/dashboard/dashboard.component.ts b/web/projects/ui/src/app/routes/portal/routes/services/dashboard/dashboard.component.ts index d2067a72a..c31e74c38 100644 --- a/web/projects/ui/src/app/routes/portal/routes/services/dashboard/dashboard.component.ts +++ b/web/projects/ui/src/app/routes/portal/routes/services/dashboard/dashboard.component.ts @@ -55,6 +55,10 @@ import { ServicesService } from './services.service' font-size: 1rem; overflow: hidden; } + + :host-context(tui-root._mobile) { + padding: 0; + } `, host: { class: 'g-page' }, imports: [ServiceComponent, ToManifestPipe, TuiTable, TitleDirective], diff --git a/web/projects/ui/src/app/routes/portal/routes/system/routes/acme/acme.component.ts b/web/projects/ui/src/app/routes/portal/routes/system/routes/acme/acme.component.ts index 46a299738..864a8a33b 100644 --- a/web/projects/ui/src/app/routes/portal/routes/system/routes/acme/acme.component.ts +++ b/web/projects/ui/src/app/routes/portal/routes/system/routes/acme/acme.component.ts @@ -2,8 +2,8 @@ import { ChangeDetectionStrategy, Component, inject } from '@angular/core' import { toSignal } from '@angular/core/rxjs-interop' import { ErrorService, LoadingService } from '@start9labs/shared' import { ISB, utils } from '@start9labs/start-sdk' -import { TuiButton, TuiLoader, TuiTitle } from '@taiga-ui/core' -import { TuiCell } from '@taiga-ui/layout' +import { TuiButton, TuiLink, TuiLoader, TuiTitle } from '@taiga-ui/core' +import { TuiCell, TuiHeader } from '@taiga-ui/layout' import { PatchDB } from 'patch-db-client' import { map } from 'rxjs' import { FormComponent } from 'src/app/routes/portal/components/form.component' @@ -12,11 +12,28 @@ import { FormDialogService } from 'src/app/services/form-dialog.service' import { DataModel } from 'src/app/services/patch-db/data-model' import { knownACME, toAcmeName } from 'src/app/utils/acme' import { configBuilderToSpec } from 'src/app/utils/configBuilderToSpec' -import { AcmeInfoComponent } from './info.component' @Component({ template: ` - +
+
+

ACME

+

+ Add ACME providers in order to generate SSL (https) certificates for + clearnet access. + +

+
+
Saved Providers @@ -62,9 +79,14 @@ import { AcmeInfoComponent } from './info.component' }
`, + styles: ` + :host { + max-width: 40rem; + } + `, changeDetection: ChangeDetectionStrategy.OnPush, standalone: true, - imports: [TuiButton, TuiLoader, TuiCell, TuiTitle, AcmeInfoComponent], + imports: [TuiButton, TuiLoader, TuiCell, TuiTitle, TuiHeader, TuiLink], }) export default class SystemAcmeComponent { private readonly formDialog = inject(FormDialogService) diff --git a/web/projects/ui/src/app/routes/portal/routes/system/routes/acme/info.component.ts b/web/projects/ui/src/app/routes/portal/routes/system/routes/acme/info.component.ts deleted file mode 100644 index 74047f79a..000000000 --- a/web/projects/ui/src/app/routes/portal/routes/system/routes/acme/info.component.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { ChangeDetectionStrategy, Component } from '@angular/core' -import { TuiLink, TuiNotification } from '@taiga-ui/core' - -@Component({ - selector: 'acme-info', - template: ` - - Register with one or more ACME providers such as Let's Encrypt in order to - generate SSL (https) certificates on-demand for clearnet hosting. - - - `, - changeDetection: ChangeDetectionStrategy.OnPush, - standalone: true, - imports: [TuiNotification, TuiLink], -}) -export class AcmeInfoComponent {} diff --git a/web/projects/ui/src/app/routes/portal/routes/system/routes/backups/backups.component.ts b/web/projects/ui/src/app/routes/portal/routes/system/routes/backups/backups.component.ts index 267bb08c8..e095d7947 100644 --- a/web/projects/ui/src/app/routes/portal/routes/system/routes/backups/backups.component.ts +++ b/web/projects/ui/src/app/routes/portal/routes/system/routes/backups/backups.component.ts @@ -1,21 +1,32 @@ -import { AsyncPipe } from '@angular/common' +import { AsyncPipe, DatePipe } from '@angular/common' import { ChangeDetectionStrategy, Component, inject, OnInit, } from '@angular/core' +import { toSignal } from '@angular/core/rxjs-interop' import { ActivatedRoute, RouterLink } from '@angular/router' import { UnitConversionPipesModule } from '@start9labs/shared' import { TuiResponsiveDialogService } from '@taiga-ui/addon-mobile' -import { TuiButton, TuiLink, TuiLoader } from '@taiga-ui/core' -import { BACKUP } from 'src/app/routes/portal/routes/system/routes/backups/backup.component' +import { TuiMapperPipe } from '@taiga-ui/cdk' +import { + TuiButton, + TuiLink, + TuiLoader, + TuiNotification, + TuiTitle, +} from '@taiga-ui/core' +import { TuiHeader } from '@taiga-ui/layout' +import { PatchDB } from 'patch-db-client' import { CifsBackupTarget, DiskBackupTarget, } from 'src/app/services/api/api.types' import { EOSService } from 'src/app/services/eos.service' +import { DataModel } from 'src/app/services/patch-db/data-model' import { TitleDirective } from 'src/app/services/title.service' +import { BACKUP } from './backup.component' import { BackupService, MappedBackupTarget } from './backup.service' import { BackupNetworkComponent } from './network.component' import { BackupPhysicalComponent } from './physical.component' @@ -29,6 +40,53 @@ import { BACKUP_RESTORE } from './restore.component' {{ type === 'create' ? 'Create Backup' : 'Restore Backup' }} +
+
+

{{ type === 'create' ? 'Create Backup' : 'Restore Backup' }}

+

+ @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. + + } @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. + + } +

+
+
+ + @if (type === 'create' && server(); as s) { + +
+ Last Backup +
+ {{ s.lastBackup ? (s.lastBackup | date: 'medium') : 'never' }} +
+
+
+ } + @if (type === 'create' && (eos.backingUp$ | async)) {
} @else { @@ -40,8 +98,7 @@ import { BACKUP_RESTORE } from './restore.component' /> } @else {
- {{ text }} - a folder on another computer that is connected to the same network as + A folder on another computer that is connected to the same network as your Start9 server. View the
- {{ text }} - a physical drive that is plugged directly into your Start9 Server. - View the - + A physical drive that is plugged directly into your Start9 Server.
} } @@ -71,15 +118,20 @@ import { BACKUP_RESTORE } from './restore.component' changeDetection: ChangeDetectionStrategy.OnPush, standalone: true, imports: [ + AsyncPipe, + DatePipe, RouterLink, TuiButton, TuiLoader, TuiLink, + TuiHeader, + TuiTitle, + TuiNotification, + TuiMapperPipe, TitleDirective, UnitConversionPipesModule, BackupNetworkComponent, BackupPhysicalComponent, - AsyncPipe, BackupProgressComponent, ], }) @@ -88,11 +140,25 @@ export default class SystemBackupComponent implements OnInit { readonly type = inject(ActivatedRoute).snapshot.data['type'] readonly service = inject(BackupService) readonly eos = inject(EOSService) + readonly server = toSignal( + inject>(PatchDB).watch$('serverInfo'), + ) - get text() { - return this.type === 'create' - ? 'Backup server to' - : 'Restore your services from' + readonly toAppearance = (lastBackup: string | null) => { + if (!lastBackup) return 'negative' + + const currentDate = new Date().valueOf() + const backupDate = new Date(lastBackup).valueOf() + const diff = currentDate - backupDate + const week = 604800000 + + if (diff <= week) { + return 'positive' + } else if (diff > week && diff <= week * 2) { + return 'warning' + } else { + return 'negative' + } } ngOnInit() { diff --git a/web/projects/ui/src/app/routes/portal/routes/system/routes/backups/network.component.ts b/web/projects/ui/src/app/routes/portal/routes/system/routes/backups/network.component.ts index 8eb06bbf8..850fef900 100644 --- a/web/projects/ui/src/app/routes/portal/routes/system/routes/backups/network.component.ts +++ b/web/projects/ui/src/app/routes/portal/routes/system/routes/backups/network.component.ts @@ -14,6 +14,7 @@ import { TuiCell } from '@taiga-ui/layout' import { filter } from 'rxjs' import { FormComponent } from 'src/app/routes/portal/components/form.component' import { PlaceholderComponent } from 'src/app/routes/portal/components/placeholder.component' +import { TableComponent } from 'src/app/routes/portal/components/table.component' import { CifsBackupTarget, RR } from 'src/app/services/api/api.types' import { ApiService } from 'src/app/services/api/embassy-api.service' import { FormDialogService } from 'src/app/services/form-dialog.service' @@ -37,69 +38,132 @@ const ERROR = - @for (target of service.cifs(); track $index) { - - - - } @empty { - No network folders - } + + @if (target.entry.mountable) { + + } @else { + + + Unable to connect + + } + + {{ target.entry.path.split('/').pop() }} + {{ target.entry.hostname }} + {{ target.entry.path }} + + + + + + } @empty { + + + + No network folders + + + + } + `, styles: ` + @import '@taiga-ui/core/styles/taiga-ui-local'; + + tr { + cursor: pointer; + @include transition(background); + + @media ($tui-mouse) { + &:hover { + background: var(--tui-background-neutral-1-hover); + } + } + } + + td:first-child { + width: 13rem; + } + [tuiButton] { margin-inline-start: auto; } + + span { + display: flex; + align-items: center; + gap: 0.25rem; + } + + :host-context(tui-root._mobile) { + tr { + grid-template-columns: min-content 1fr 4rem; + white-space: nowrap; + } + + td { + grid-column: span 2; + + &:first-child { + font-size: 0; + width: auto; + grid-area: 1 / 2; + place-content: center; + margin: 0 0.5rem; + } + + &:last-child { + grid-area: 1 / 3 / 4 / 3; + align-self: center; + justify-self: end; + } + } + + .name { + color: var(--tui-text-primary); + font: var(--tui-font-text-m); + font-weight: bold; + grid-column: 1; + max-width: 12rem; + } + } `, host: { class: 'g-card' }, changeDetection: ChangeDetectionStrategy.OnPush, imports: [ TuiButton, - TuiCell, TuiIcon, - TuiTitle, TuiTooltip, PlaceholderComponent, BackupStatusComponent, + TableComponent, ], }) export class BackupNetworkComponent { diff --git a/web/projects/ui/src/app/routes/portal/routes/system/routes/backups/physical.component.ts b/web/projects/ui/src/app/routes/portal/routes/system/routes/backups/physical.component.ts index 46c78ec2b..86a1424f4 100644 --- a/web/projects/ui/src/app/routes/portal/routes/system/routes/backups/physical.component.ts +++ b/web/projects/ui/src/app/routes/portal/routes/system/routes/backups/physical.component.ts @@ -6,16 +6,10 @@ import { } from '@angular/core' import { ActivatedRoute } from '@angular/router' import { UnitConversionPipesModule } from '@start9labs/shared' -import { - TuiAlertService, - TuiButton, - TuiIcon, - TuiNotification, - TuiTitle, -} from '@taiga-ui/core' +import { TuiAlertService, TuiButton, TuiIcon } from '@taiga-ui/core' import { TuiTooltip } from '@taiga-ui/kit' -import { TuiCell } from '@taiga-ui/layout' import { PlaceholderComponent } from 'src/app/routes/portal/components/placeholder.component' +import { TableComponent } from 'src/app/routes/portal/components/table.component' import { DiskBackupTarget } from 'src/app/services/api/api.types' import { BackupService, MappedBackupTarget } from './backup.service' import { BackupStatusComponent } from './status.component' @@ -30,58 +24,97 @@ import { BackupStatusComponent } from './status.component' - - Warning. Do not use this option if you are using a Raspberry Pi with an - external SSD. The Raspberry Pi does not support more than one external - drive without additional power and can cause data corruption. - - - @for (target of service.drives(); track $index) { - -
-
Send Test Email
+ +
+

Send Test Email

+
- Firstname Lastname <email@example.com> + Name Lastname <email@example.com>
- + } `, styles: ` :host { - display: grid !important; - grid-template-columns: 1fr 1fr; - align-items: start; + max-width: 40rem; } - :host-context(tui-root._mobile) { - grid-template-columns: 1fr; + form header, + form footer { + margin: 1rem 0; + display: flex; + gap: 1rem; + } + + footer { + justify-content: flex-end; } `, changeDetection: ChangeDetectionStrategy.OnPush, @@ -91,7 +120,9 @@ import { EmailInfoComponent } from './info.component' FormModule, TuiButton, TuiInputModule, - EmailInfoComponent, + TuiHeader, + TuiTitle, + TuiLink, RouterLink, TitleDirective, ], diff --git a/web/projects/ui/src/app/routes/portal/routes/system/routes/email/info.component.ts b/web/projects/ui/src/app/routes/portal/routes/system/routes/email/info.component.ts deleted file mode 100644 index f1bd31420..000000000 --- a/web/projects/ui/src/app/routes/portal/routes/system/routes/email/info.component.ts +++ /dev/null @@ -1,33 +0,0 @@ -import { ChangeDetectionStrategy, Component } from '@angular/core' -import { TuiLink, TuiNotification } from '@taiga-ui/core' - -@Component({ - selector: 'email-info', - template: ` - - Adding SMTP credentials to StartOS enables StartOS and some services to - send you emails. - - - `, - styles: ` - :host { - grid-column: span 2; - } - - :host-context(tui-root._mobile) { - grid-column: 1; - } - `, - changeDetection: ChangeDetectionStrategy.OnPush, - standalone: true, - imports: [TuiNotification, TuiLink], -}) -export class EmailInfoComponent {} diff --git a/web/projects/ui/src/app/routes/portal/routes/system/routes/interfaces/interfaces.component.ts b/web/projects/ui/src/app/routes/portal/routes/system/routes/interfaces/interfaces.component.ts index fc288c6bd..31bb6727e 100644 --- a/web/projects/ui/src/app/routes/portal/routes/system/routes/interfaces/interfaces.component.ts +++ b/web/projects/ui/src/app/routes/portal/routes/system/routes/interfaces/interfaces.component.ts @@ -2,7 +2,8 @@ import { ChangeDetectionStrategy, Component, inject } from '@angular/core' import { toSignal } from '@angular/core/rxjs-interop' import { RouterLink } from '@angular/router' import { T } from '@start9labs/start-sdk' -import { TuiButton } from '@taiga-ui/core' +import { TuiButton, TuiTitle } from '@taiga-ui/core' +import { TuiHeader } from '@taiga-ui/layout' import { PatchDB } from 'patch-db-client' import { map } from 'rxjs' import { InterfaceComponent } from 'src/app/routes/portal/components/interfaces/interface.component' @@ -34,13 +35,29 @@ const iface: T.ServiceInterface = { Back Web Addresses +
+
+

User Interface Addresses

+

+ View and manage private and public addresses for accessing your + StartOS UI +

+
+
@if (ui(); as ui) { } `, changeDetection: ChangeDetectionStrategy.OnPush, standalone: true, - imports: [InterfaceComponent, RouterLink, TuiButton, TitleDirective], + imports: [ + InterfaceComponent, + RouterLink, + TuiButton, + TitleDirective, + TuiHeader, + TuiTitle, + ], }) export default class StartOsUiComponent { private readonly config = inject(ConfigService) diff --git a/web/projects/ui/src/app/routes/portal/routes/system/routes/password/password.component.ts b/web/projects/ui/src/app/routes/portal/routes/system/routes/password/password.component.ts index 388d4a586..216f0891d 100644 --- a/web/projects/ui/src/app/routes/portal/routes/system/routes/password/password.component.ts +++ b/web/projects/ui/src/app/routes/portal/routes/system/routes/password/password.component.ts @@ -10,6 +10,7 @@ import { TuiNotification, TuiTitle, } from '@taiga-ui/core' +import { TuiHeader } from '@taiga-ui/layout' import { PatchDB } from 'patch-db-client' import { from } from 'rxjs' import { FormComponent } from 'src/app/routes/portal/components/form.component' @@ -23,22 +24,23 @@ import { getServerInfo } from 'src/app/utils/get-server-info' template: ` Back - Password Reset + Change Password - -
- Warning -
- You will still need your current password to decrypt existing backups! -
-
-
-
-
Change Master Password
- @if (spec(); as spec) { - - } -
+
+
+

Change Password

+

+ Change your StartOS master password. + + You will still need your current password to decrypt existing + backups! + +

+
+
+ @if (spec(); as spec) { + + } `, styles: ` :host { @@ -46,6 +48,7 @@ import { getServerInfo } from 'src/app/utils/get-server-info' ::ng-deep footer { background: transparent !important; + margin: 0; } } @@ -56,11 +59,11 @@ import { getServerInfo } from 'src/app/utils/get-server-info' changeDetection: ChangeDetectionStrategy.OnPush, standalone: true, imports: [ - TuiNotification, - TuiTitle, - FormComponent, RouterLink, + TuiHeader, + TuiTitle, TuiButton, + FormComponent, TitleDirective, ], }) diff --git a/web/projects/ui/src/app/routes/portal/routes/system/routes/sessions/sessions.component.ts b/web/projects/ui/src/app/routes/portal/routes/system/routes/sessions/sessions.component.ts index 55de658e6..10f93fc88 100644 --- a/web/projects/ui/src/app/routes/portal/routes/system/routes/sessions/sessions.component.ts +++ b/web/projects/ui/src/app/routes/portal/routes/system/routes/sessions/sessions.component.ts @@ -1,10 +1,11 @@ import { RouterLink } from '@angular/router' import { TuiTable } from '@taiga-ui/addon-table' import { TuiLet } from '@taiga-ui/cdk' -import { TuiButton } from '@taiga-ui/core' +import { TuiButton, TuiTitle } from '@taiga-ui/core' import { CommonModule } from '@angular/common' import { ChangeDetectionStrategy, Component, inject } from '@angular/core' import { ErrorService, LoadingService } from '@start9labs/shared' +import { TuiHeader } from '@taiga-ui/layout' import { from, map, merge, Observable, Subject } from 'rxjs' import { ApiService } from 'src/app/services/api/embassy-api.service' import { Session } from 'src/app/services/api/api.types' @@ -17,14 +18,18 @@ import { SSHTableComponent } from './table.component' Back Active Sessions +
+
+

Active Sessions

+

+ A session is a device that is currently logged into StartOS. For best + security, terminate sessions you do not recognize or no longer use. +

+
+
Current session
-
+
@@ -43,7 +48,7 @@ import { SSHTableComponent } from './table.component' } -
+
`, changeDetection: ChangeDetectionStrategy.OnPush, @@ -56,6 +61,8 @@ import { SSHTableComponent } from './table.component' RouterLink, TitleDirective, TuiTable, + TuiHeader, + TuiTitle, ], }) export default class SystemSessionsComponent { diff --git a/web/projects/ui/src/app/routes/portal/routes/system/routes/sessions/table.component.ts b/web/projects/ui/src/app/routes/portal/routes/system/routes/sessions/table.component.ts index f6ab49941..18617f4e7 100644 --- a/web/projects/ui/src/app/routes/portal/routes/system/routes/sessions/table.component.ts +++ b/web/projects/ui/src/app/routes/portal/routes/system/routes/sessions/table.component.ts @@ -6,53 +6,32 @@ import { OnChanges, } from '@angular/core' import { FormsModule } from '@angular/forms' -import { TuiTable } from '@taiga-ui/addon-table' import { TuiIcon } from '@taiga-ui/core' import { TuiCheckbox, TuiFade, TuiSkeleton } from '@taiga-ui/kit' import { BehaviorSubject } from 'rxjs' +import { TableComponent } from 'src/app/routes/portal/components/table.component' import { Session } from 'src/app/services/api/api.types' import { PlatformInfoPipe } from './platform-info.pipe' @Component({ - selector: 'table[sessions]', + selector: '[sessions]', template: ` - - - - @if (!single) { - - } - User Agent - - Platform - Last Active - - - + @for (session of sessions; track $index) { - @if (session.metadata.platforms | platformInfo; as info) { } @empty { @if (sessions) { - + } @else { @for (item of single ? [''] : ['', '']; track $index) { - + } } } - +
- @if (!single) { - - } - {{ session.userAgent }} + + @@ -64,16 +43,16 @@ import { PlatformInfoPipe } from './platform-info.pipe'
No sessions
No sessions
Loading
Loading
`, styles: [ ` @@ -81,16 +60,37 @@ import { PlatformInfoPipe } from './platform-info.pipe' td { position: relative; + width: 25%; + + &[colspan] { + grid-column: span 2; + } + + &:first-child { + width: 50%; + } } input { position: absolute; top: 50%; - left: 0.5rem; + left: 0.75rem; transform: translateY(-50%); } + .platform { + white-space: nowrap; + } + :host-context(tui-root._mobile) { + tr { + grid-template-columns: 2.5rem 1fr; + + &:has(:checked) .platform { + color: var(--tui-text-action); + } + } + input { @include fullsize(); z-index: 1; @@ -98,8 +98,12 @@ import { PlatformInfoPipe } from './platform-info.pipe' transform: none; } - td:first-child { - padding: 0 0.25rem !important; + td { + width: 100%; + + &:first-child { + padding: 0 !important; + } } .agent { @@ -108,11 +112,9 @@ import { PlatformInfoPipe } from './platform-info.pipe' } .platform { - font-weight: bold; - display: flex; - align-items: center; - gap: 0.5rem; - padding: 0; + font-size: 0; + grid-area: 1 / 1 / 3 / 1; + place-content: center; } .date { @@ -131,7 +133,7 @@ import { PlatformInfoPipe } from './platform-info.pipe' TuiCheckbox, TuiFade, TuiSkeleton, - TuiTable, + TableComponent, ], }) export class SSHTableComponent implements OnChanges { @@ -143,26 +145,10 @@ export class SSHTableComponent implements OnChanges { @Input() single = false - get all(): boolean | null { - if (!this.sessions?.length || !this.selected$.value.length) { - return false - } - - if (this.sessions?.length === this.selected$.value.length) { - return true - } - - return null - } - ngOnChanges() { this.selected$.next([]) } - onAll(selected: boolean) { - this.selected$.next((selected && this.sessions) || []) - } - onToggle(session: T) { const selected = this.selected$.value diff --git a/web/projects/ui/src/app/routes/portal/routes/system/routes/ssh/info.component.ts b/web/projects/ui/src/app/routes/portal/routes/system/routes/ssh/info.component.ts deleted file mode 100644 index f68231cf2..000000000 --- a/web/projects/ui/src/app/routes/portal/routes/system/routes/ssh/info.component.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { ChangeDetectionStrategy, Component } from '@angular/core' -import { TuiLink, TuiNotification } from '@taiga-ui/core' - -@Component({ - selector: 'ssh-info', - template: ` - - Adding domains to StartOS enables you to access your server and service - interfaces over clearnet. - - View instructions - - - `, - changeDetection: ChangeDetectionStrategy.OnPush, - standalone: true, - imports: [TuiNotification, TuiLink], -}) -export class SSHInfoComponent {} diff --git a/web/projects/ui/src/app/routes/portal/routes/system/routes/ssh/ssh.component.ts b/web/projects/ui/src/app/routes/portal/routes/system/routes/ssh/ssh.component.ts index da9c30072..5d2e4e832 100644 --- a/web/projects/ui/src/app/routes/portal/routes/system/routes/ssh/ssh.component.ts +++ b/web/projects/ui/src/app/routes/portal/routes/system/routes/ssh/ssh.component.ts @@ -1,13 +1,13 @@ import { RouterLink } from '@angular/router' import { TuiTable } from '@taiga-ui/addon-table' -import { TuiButton } from '@taiga-ui/core' +import { TuiButton, TuiLink, TuiTitle } from '@taiga-ui/core' import { CommonModule } from '@angular/common' import { ChangeDetectionStrategy, Component, inject } from '@angular/core' import { ErrorService } from '@start9labs/shared' +import { TuiHeader } from '@taiga-ui/layout' import { catchError, defer, of } from 'rxjs' import { ApiService } from 'src/app/services/api/embassy-api.service' import { TitleDirective } from 'src/app/services/title.service' -import { SSHInfoComponent } from './info.component' import { SSHTableComponent } from './table.component' @Component({ @@ -16,7 +16,24 @@ import { SSHTableComponent } from './table.component' Back SSH - +
+
+

SSH

+

+ Manage your SSH keys to access your server from the command line + +

+
+
Saved Keys @@ -30,7 +47,7 @@ import { SSHTableComponent } from './table.component' Add Key
-
+
`, changeDetection: ChangeDetectionStrategy.OnPush, @@ -39,10 +56,12 @@ import { SSHTableComponent } from './table.component' CommonModule, TuiButton, SSHTableComponent, - SSHInfoComponent, RouterLink, TitleDirective, TuiTable, + TuiHeader, + TuiTitle, + TuiLink, ], }) export default class SystemSSHComponent { diff --git a/web/projects/ui/src/app/routes/portal/routes/system/routes/ssh/table.component.ts b/web/projects/ui/src/app/routes/portal/routes/system/routes/ssh/table.component.ts index 496be4fb2..5124ae8da 100644 --- a/web/projects/ui/src/app/routes/portal/routes/system/routes/ssh/table.component.ts +++ b/web/projects/ui/src/app/routes/portal/routes/system/routes/ssh/table.component.ts @@ -7,32 +7,25 @@ import { Input, } from '@angular/core' import { ErrorService, LoadingService } from '@start9labs/shared' -import { TuiTable } from '@taiga-ui/addon-table' -import { TuiDialogOptions, TuiDialogService, TuiButton } from '@taiga-ui/core' +import { TuiButton, TuiDialogOptions, TuiDialogService } from '@taiga-ui/core' import { + TUI_CONFIRM, TuiConfirmData, TuiFade, - TUI_CONFIRM, TuiSkeleton, } from '@taiga-ui/kit' import { filter, take } from 'rxjs' +import { TableComponent } from 'src/app/routes/portal/components/table.component' import { PROMPT } from 'src/app/routes/portal/modals/prompt.component' import { SSHKey } from 'src/app/services/api/api.types' import { ApiService } from 'src/app/services/api/embassy-api.service' @Component({ - selector: 'table[keys]', + selector: '[keys]', template: ` - - - Hostname - Created At - Algorithm - Fingerprint - - - - + @for (key of keys; track $index) { @@ -62,7 +55,7 @@ import { ApiService } from 'src/app/services/api/embassy-api.service' } } } - +
{{ key.hostname }}
`, styles: ` :host-context(tui-root._mobile) { @@ -109,7 +102,7 @@ import { ApiService } from 'src/app/services/api/embassy-api.service' `, standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, - imports: [CommonModule, TuiButton, TuiFade, TuiSkeleton, TuiTable], + imports: [CommonModule, TuiButton, TuiFade, TuiSkeleton, TableComponent], }) export class SSHTableComponent { private readonly loader = inject(LoadingService) diff --git a/web/projects/ui/src/app/routes/portal/routes/system/routes/wifi/info.component.ts b/web/projects/ui/src/app/routes/portal/routes/system/routes/wifi/info.component.ts deleted file mode 100644 index 55fdac670..000000000 --- a/web/projects/ui/src/app/routes/portal/routes/system/routes/wifi/info.component.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { ChangeDetectionStrategy, Component } from '@angular/core' -import { TuiLink, TuiNotification } from '@taiga-ui/core' - -@Component({ - selector: 'wifi-info', - template: ` - - Adding WiFi credentials to StartOS allows you to remove the Ethernet cable - and move the device anywhere you want. StartOS will automatically connect - to available networks. - - - `, - changeDetection: ChangeDetectionStrategy.OnPush, - standalone: true, - imports: [TuiNotification, TuiLink], -}) -export class WifiInfoComponent {} diff --git a/web/projects/ui/src/app/routes/portal/routes/system/routes/wifi/table.component.ts b/web/projects/ui/src/app/routes/portal/routes/system/routes/wifi/table.component.ts index 4b759ac9e..a907027bd 100644 --- a/web/projects/ui/src/app/routes/portal/routes/system/routes/wifi/table.component.ts +++ b/web/projects/ui/src/app/routes/portal/routes/system/routes/wifi/table.component.ts @@ -24,27 +24,26 @@ import SystemWifiComponent from './wifi.component' template: ` @for (network of wifi; track $index) { @if (network.ssid) { -
+ - } @if (network.connected !== undefined) { @@ -63,14 +62,22 @@ import SystemWifiComponent from './wifi.component' } @else { } -
+ } } `, styles: ` :host { align-items: stretch; - white-space: nowrap; + padding: 0.5rem !important; + } + + [tuiCell] { + padding-inline: 1rem !important; + + &:disabled > * { + opacity: 1; + } } tui-icon { diff --git a/web/projects/ui/src/app/routes/portal/routes/system/routes/wifi/wifi.component.ts b/web/projects/ui/src/app/routes/portal/routes/system/routes/wifi/wifi.component.ts index f4fadffed..59545a616 100644 --- a/web/projects/ui/src/app/routes/portal/routes/system/routes/wifi/wifi.component.ts +++ b/web/projects/ui/src/app/routes/portal/routes/system/routes/wifi/wifi.component.ts @@ -13,10 +13,12 @@ import { TuiAppearance, TuiButton, TuiDialogOptions, + TuiLink, TuiLoader, + TuiTitle, } from '@taiga-ui/core' import { TuiSwitch } from '@taiga-ui/kit' -import { TuiCardLarge } from '@taiga-ui/layout' +import { TuiCardLarge, TuiHeader } from '@taiga-ui/layout' import { PatchDB } from 'patch-db-client' import { catchError, defer, map, merge, Observable, of, Subject } from 'rxjs' import { @@ -28,7 +30,6 @@ import { ApiService } from 'src/app/services/api/embassy-api.service' import { FormDialogService } from 'src/app/services/form-dialog.service' import { DataModel } from 'src/app/services/patch-db/data-model' import { TitleDirective } from 'src/app/services/title.service' -import { WifiInfoComponent } from './info.component' import { WifiTableComponent } from './table.component' import { parseWifi, WifiData, WiFiForm } from './utils' import { wifiSpec } from './wifi.const' @@ -39,7 +40,26 @@ import { wifiSpec } from './wifi.const' Back WiFi - +
+
+

WiFi

+

+ Adding WiFi credentials to StartOS allows you to remove the Ethernet + cable and move the device anywhere you want. StartOS will + automatically connect to available networks. + +

+
+
@if (status()?.interface) {
@@ -60,7 +80,6 @@ import { wifiSpec } from './wifi.const' tuiCardLarge="compact" tuiAppearance="neutral" [wifi]="data.known" - [style.padding-block.rem]="0.5" > } @if (data.available.length) { @@ -69,11 +88,10 @@ import { wifiSpec } from './wifi.const' tuiCardLarge="compact" tuiAppearance="neutral" [wifi]="data.available" - [style.padding-block.rem]="0.5" > }

- +

} @else { @@ -88,6 +106,11 @@ import { wifiSpec } from './wifi.const' } `, + styles: ` + :host { + max-width: 40rem; + } + `, changeDetection: ChangeDetectionStrategy.OnPush, standalone: true, imports: [ @@ -97,11 +120,13 @@ import { wifiSpec } from './wifi.const' TuiCardLarge, TuiLoader, TuiAppearance, - WifiInfoComponent, WifiTableComponent, TitleDirective, RouterLink, PlaceholderComponent, + TuiHeader, + TuiTitle, + TuiLink, ], }) export default class SystemWifiComponent { diff --git a/web/projects/ui/src/app/routes/portal/routes/system/system.component.ts b/web/projects/ui/src/app/routes/portal/routes/system/system.component.ts index c73cc768f..0e3e6c01e 100644 --- a/web/projects/ui/src/app/routes/portal/routes/system/system.component.ts +++ b/web/projects/ui/src/app/routes/portal/routes/system/system.component.ts @@ -11,7 +11,7 @@ import { SYSTEM_MENU } from './system.const' @Component({ template: ` - {{ 'system.outlet.general' | i18n }} + {{ 'system.outlet.system' | i18n }}