>((acc, pkg) => {
+ const { id, title } = getManifest(pkg)
+ return {
+ ...acc,
+ [id]: title,
+ }
+ }, {}),
+ ),
),
)
diff --git a/web/projects/ui/src/app/routes/portal/components/interfaces/clearnet.component.ts b/web/projects/ui/src/app/routes/portal/components/interfaces/clearnet.component.ts
index 95b5dc791..58b7718ac 100644
--- a/web/projects/ui/src/app/routes/portal/components/interfaces/clearnet.component.ts
+++ b/web/projects/ui/src/app/routes/portal/components/interfaces/clearnet.component.ts
@@ -91,7 +91,7 @@ type ClearnetForm = {
|
{{ interface.value().addSsl ? (address.acme | acme) : '-' }}
|
- {{ address.url | mask }} |
+ {{ address.url | mask }} |
{{ 'Delete' | i18n }}
@@ -131,6 +130,19 @@ type ClearnetForm = {
}
`,
+ styles: `
+ :host-context(tui-root._mobile) {
+ td {
+ font-weight: bold;
+ color: var(--tui-text-primary);
+
+ &:first-child {
+ font-weight: normal;
+ color: var(--tui-text-secondary);
+ }
+ }
+ }
+ `,
host: { class: 'g-card' },
imports: [
TuiButton,
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 477265c4b..bbec56f15 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
@@ -6,6 +6,7 @@ import { i18nKey, i18nPipe } from '@start9labs/shared'
template: `
+
@for (header of appTable(); track $index) {
| {{ header | i18n }} |
}
diff --git a/web/projects/ui/src/app/routes/portal/routes/notifications/notifications.component.ts b/web/projects/ui/src/app/routes/portal/routes/notifications/notifications.component.ts
index 721c028dd..9ba5b9634 100644
--- a/web/projects/ui/src/app/routes/portal/routes/notifications/notifications.component.ts
+++ b/web/projects/ui/src/app/routes/portal/routes/notifications/notifications.component.ts
@@ -1,3 +1,4 @@
+import { NgTemplateOutlet } from '@angular/common'
import {
ChangeDetectionStrategy,
Component,
@@ -16,44 +17,71 @@ import { NotificationsTableComponent } from './table.component'
@Component({
template: `
- {{ 'Notifications' | i18n }}
-
-
-
-
-
-
-
-
+
+ {{ 'Notifications' | i18n }}
+
+
+
+
+ {{ 'Notifications' | i18n }}
+
+
+
+
+
-
-
+
+ `,
+ styles: `
+ :host {
+ padding: 1rem;
+ }
+
+ :host-context(tui-root._mobile) {
+ header {
+ display: none;
+ }
+
+ section {
+ padding-block: 0;
+ }
+ }
`,
host: { class: 'g-page' },
changeDetection: ChangeDetectionStrategy.OnPush,
@@ -64,6 +92,7 @@ import { NotificationsTableComponent } from './table.component'
NotificationsTableComponent,
TitleDirective,
i18nPipe,
+ NgTemplateOutlet,
],
})
export default class NotificationsComponent implements OnInit {
@@ -75,8 +104,6 @@ export default class NotificationsComponent implements OnInit {
readonly errorService = inject(ErrorService)
readonly notifications = signal(undefined)
- open = false
-
ngOnInit() {
this.route.queryParams.subscribe(params => {
this.router.navigate([], { relativeTo: this.route, queryParams: {} })
@@ -100,8 +127,6 @@ export default class NotificationsComponent implements OnInit {
current: ServerNotifications = [],
toUpdate: ServerNotifications = [],
) {
- this.open = false
-
this.notifications.set(
current.map(c => ({
...c,
@@ -116,8 +141,6 @@ export default class NotificationsComponent implements OnInit {
current: ServerNotifications = [],
toUpdate: ServerNotifications = [],
) {
- this.open = false
-
this.notifications.set(
current.map(c => ({
...c,
@@ -132,8 +155,6 @@ export default class NotificationsComponent implements OnInit {
current: ServerNotifications = [],
toDelete: ServerNotifications = [],
) {
- this.open = false
-
this.notifications.set(
current.filter(c => !toDelete.some(n => n.id === c.id)),
)
diff --git a/web/projects/ui/src/app/routes/portal/routes/services/routes/interface.component.ts b/web/projects/ui/src/app/routes/portal/routes/services/routes/interface.component.ts
index 5ad9dfeef..753d2245d 100644
--- a/web/projects/ui/src/app/routes/portal/routes/services/routes/interface.component.ts
+++ b/web/projects/ui/src/app/routes/portal/routes/services/routes/interface.component.ts
@@ -10,7 +10,7 @@ import { RouterLink } from '@angular/router'
import { getPkgId, i18nPipe } from '@start9labs/shared'
import { T } from '@start9labs/start-sdk'
import { TuiItem } from '@taiga-ui/cdk'
-import { TuiButton, TuiLink } from '@taiga-ui/core'
+import { TuiButton, TuiLink, TuiTitle } from '@taiga-ui/core'
import { TuiBadge, TuiBreadcrumbs } from '@taiga-ui/kit'
import { TuiHeader } from '@taiga-ui/layout'
import { PatchDB } from 'patch-db-client'
@@ -41,7 +41,7 @@ import { TitleDirective } from 'src/app/services/title.service'
@if (interface(); as value) {
-
+
{{ value.name }}
@@ -60,7 +60,8 @@ import { TitleDirective } from 'src/app/services/title.service'
}
`,
styles: `
- :host-context(tui-root._mobile) tui-breadcrumbs {
+ :host-context(tui-root._mobile) tui-breadcrumbs,
+ :host-context(tui-root._mobile) h3 {
display: none;
}
@@ -68,8 +69,6 @@ import { TitleDirective } from 'src/app/services/title.service'
display: flex;
align-items: center;
gap: 0.5rem;
- margin: 1rem 0 0.5rem 0;
- font-size: 2.4rem;
tui-badge {
text-transform: uppercase;
@@ -91,6 +90,7 @@ import { TitleDirective } from 'src/app/services/title.service'
i18nPipe,
TuiBadge,
TuiHeader,
+ TuiTitle,
],
})
export default class ServiceInterfaceRoute {
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 5afca8fc2..ef1b61114 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
@@ -35,7 +35,7 @@ const ERROR =
{{ 'Network Folders' | i18n }}
-
+
{{ 'Open New' | i18n }}
@@ -100,11 +100,11 @@ const ERROR =
@use '@taiga-ui/core/styles/taiga-ui-local' as taiga;
tr {
- cursor: pointer;
@include taiga.transition(background);
@media (taiga.$tui-mouse) {
- &:hover {
+ &:not(:has(app-placeholder)):hover {
+ cursor: pointer;
background: var(--tui-background-neutral-1-hover);
}
}
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 829749dab..dc4713d90 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
@@ -66,11 +66,11 @@ import { BackupStatusComponent } from './status.component'
@use '@taiga-ui/core/styles/taiga-ui-local' as taiga;
tr {
- cursor: pointer;
@include taiga.transition(background);
@media (taiga.$tui-mouse) {
- &:hover {
+ &:not(:has(app-placeholder)):hover {
+ cursor: pointer;
background: var(--tui-background-neutral-1-hover);
}
}
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 37f8b6483..4798b601e 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
@@ -46,7 +46,7 @@ import { SessionsTableComponent } from './table.component'
size="xs"
appearance="primary-destructive"
[style.margin-inline-start]="'auto'"
- [disabled]="!(sessions()?.selected$ | async)?.length"
+ [disabled]="!sessions()?.selected()?.length"
(click)="terminate(others || [])"
>
{{ 'Terminate selected' | i18n }}
@@ -104,13 +104,16 @@ export default class SystemSessionsComponent {
)
async terminate(all: readonly SessionWithId[]) {
- const ids = this.sessions()?.selected$.value.map(s => s.id) || []
+ const ids =
+ this.sessions()
+ ?.selected()
+ .map(s => s.id) || []
const loader = this.loader.open('Terminating sessions').subscribe()
try {
await this.api.killSessions({ ids })
this.local$.next(all.filter(s => !ids.includes(s.id)))
- this.sessions()?.selected$.next([])
+ this.sessions()?.selected.set([])
} catch (e: any) {
this.errorService.handleError(e)
} finally {
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 b784ab027..abb2d6832 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
@@ -2,8 +2,11 @@ import { CommonModule } from '@angular/common'
import {
ChangeDetectionStrategy,
Component,
+ computed,
+ input,
Input,
OnChanges,
+ signal,
} from '@angular/core'
import { FormsModule } from '@angular/forms'
import { TuiIcon } from '@taiga-ui/core'
@@ -17,17 +20,36 @@ import { i18nPipe } from '@start9labs/shared'
@Component({
selector: '[sessions]',
template: `
-
- @for (session of sessions; track $index) {
+
+ @if (!single()) {
+ |
+
+ {{ 'User Agent' | i18n }}
+ |
+ }
+ @for (session of sessions(); track $index) {
- |
+ |
|
} @empty {
- @if (sessions) {
+ @if (sessions()) {
| {{ 'No sessions' | i18n }} |
} @else {
- @for (item of single ? [''] : ['', '']; track $index) {
+ @for (item of single() ? [''] : ['', '']; track $index) {
|
{{ 'Loading' | i18n }}
@@ -144,25 +166,25 @@ import { i18nPipe } from '@start9labs/shared'
],
})
export class SessionsTableComponent implements OnChanges {
- readonly selected$ = new BehaviorSubject([])
+ readonly sessions = input(null)
+ readonly single = input(false)
- @Input()
- sessions: readonly T[] | null = null
-
- @Input()
- single = false
+ readonly selected = signal([])
+ readonly all = computed(
+ () =>
+ !!this.selected()?.length &&
+ (this.selected().length === this.sessions()?.length || null),
+ )
ngOnChanges() {
- this.selected$.next([])
+ this.selected.set([])
}
onToggle(session: T) {
- const selected = this.selected$.value
-
- if (selected.includes(session)) {
- this.selected$.next(selected.filter(s => s !== session))
+ if (this.selected().includes(session)) {
+ this.selected.update(selected => selected.filter(s => s !== session))
} else {
- this.selected$.next([...selected, session])
+ this.selected.update(selected => [...selected, session])
}
}
}
diff --git a/web/projects/ui/src/app/routes/portal/routes/system/routes/startos-ui/startos-ui.component.ts b/web/projects/ui/src/app/routes/portal/routes/system/routes/startos-ui/startos-ui.component.ts
index 81bf3ddf7..a056a82d5 100644
--- a/web/projects/ui/src/app/routes/portal/routes/system/routes/startos-ui/startos-ui.component.ts
+++ b/web/projects/ui/src/app/routes/portal/routes/system/routes/startos-ui/startos-ui.component.ts
@@ -29,7 +29,7 @@ import { TitleDirective } from 'src/app/services/title.service'
-
+
{{ iface.name }}
@@ -41,20 +41,6 @@ import { TitleDirective } from 'src/app/services/title.service'
}
`,
- styles: `
- h3 {
- display: flex;
- align-items: center;
- gap: 0.5rem;
- margin: 1rem 0 0.5rem 0;
- font-size: 2.4rem;
-
- tui-badge {
- text-transform: uppercase;
- font-weight: bold;
- }
- }
- `,
host: { class: 'g-subpage' },
changeDetection: ChangeDetectionStrategy.OnPush,
imports: [
@@ -63,6 +49,7 @@ import { TitleDirective } from 'src/app/services/title.service'
TuiButton,
TitleDirective,
TuiHeader,
+ TuiTitle,
InterfaceStatusComponent,
i18nPipe,
],
diff --git a/web/projects/ui/src/styles.scss b/web/projects/ui/src/styles.scss
index 051cb699a..9ef046937 100644
--- a/web/projects/ui/src/styles.scss
+++ b/web/projects/ui/src/styles.scss
@@ -87,12 +87,7 @@ hr {
padding: 0.5rem;
text-transform: capitalize;
box-shadow: 1px 0 var(--tui-border-normal);
- backdrop-filter: blur(1rem);
- background-color: color-mix(
- in hsl,
- var(--tui-background-base) 90%,
- transparent
- );
+ background: var(--tui-background-base);
}
.g-card {
| |