diff --git a/frontend/projects/ui/src/app/apps/portal/routes/service/components/interfaces.component.ts b/frontend/projects/ui/src/app/apps/portal/routes/service/components/interfaces.component.ts
new file mode 100644
index 000000000..f70261748
--- /dev/null
+++ b/frontend/projects/ui/src/app/apps/portal/routes/service/components/interfaces.component.ts
@@ -0,0 +1,42 @@
+import { NgForOf } from '@angular/common'
+import { ChangeDetectionStrategy, Component, Input } from '@angular/core'
+import { PackageDataEntry } from 'src/app/services/patch-db/data-model'
+import {
+ PackageStatus,
+ PrimaryStatus,
+} from 'src/app/services/pkg-status-rendering.service'
+import { InterfaceInfoPipe } from '../pipes/interface-info.pipe'
+import { ToStatusPipe } from '../pipes/to-status.pipe'
+import { ServiceInterfaceComponent } from './interface.component'
+import { RouterLink } from '@angular/router'
+
+@Component({
+ selector: 'service-interfaces',
+ template: `
+
+ `,
+ changeDetection: ChangeDetectionStrategy.OnPush,
+ standalone: true,
+ imports: [
+ NgForOf,
+ RouterLink,
+ InterfaceInfoPipe,
+ ServiceInterfaceComponent,
+ ToStatusPipe,
+ ],
+})
+export class ServiceInterfacesComponent {
+ @Input({ required: true })
+ service!: PackageDataEntry
+
+ isRunning({ primary }: PackageStatus): boolean {
+ return primary === PrimaryStatus.Running
+ }
+}
diff --git a/frontend/projects/ui/src/app/apps/portal/routes/service/components/menu-item.component.ts b/frontend/projects/ui/src/app/apps/portal/routes/service/components/menu-item.component.ts
new file mode 100644
index 000000000..6f6503f6f
--- /dev/null
+++ b/frontend/projects/ui/src/app/apps/portal/routes/service/components/menu-item.component.ts
@@ -0,0 +1,25 @@
+import { ChangeDetectionStrategy, Component, Input } from '@angular/core'
+import { TuiSvgModule } from '@taiga-ui/core'
+import { ServiceMenu } from '../pipes/to-menu.pipe'
+
+@Component({
+ selector: '[serviceMenuItem]',
+ template: `
+
+ `,
+ changeDetection: ChangeDetectionStrategy.OnPush,
+ standalone: true,
+ imports: [TuiSvgModule],
+})
+export class ServiceMenuItemComponent {
+ @Input({ required: true, alias: 'serviceMenuItem' })
+ menu!: ServiceMenu
+}
diff --git a/frontend/projects/ui/src/app/apps/portal/routes/service/components/menu.component.ts b/frontend/projects/ui/src/app/apps/portal/routes/service/components/menu.component.ts
index a227d4a6f..cc0fef09e 100644
--- a/frontend/projects/ui/src/app/apps/portal/routes/service/components/menu.component.ts
+++ b/frontend/projects/ui/src/app/apps/portal/routes/service/components/menu.component.ts
@@ -1,25 +1,46 @@
+import { CommonModule } from '@angular/common'
import { ChangeDetectionStrategy, Component, Input } from '@angular/core'
-import { TuiSvgModule } from '@taiga-ui/core'
-import { ServiceMenu } from '../pipes/to-menu.pipe'
+import { PackageDataEntry } from 'src/app/services/patch-db/data-model'
+import { ToMenuPipe } from '../pipes/to-menu.pipe'
+import { ServiceMenuItemComponent } from './menu-item.component'
@Component({
- selector: '[serviceMenu]',
+ selector: 'service-menu',
template: `
-
-
{{ menu.name }}
-
- {{ menu.description }}
-
+
Menu
+
-
+
`,
changeDetection: ChangeDetectionStrategy.OnPush,
standalone: true,
- imports: [TuiSvgModule],
+ imports: [CommonModule, ToMenuPipe, ServiceMenuItemComponent],
})
export class ServiceMenuComponent {
- @Input({ required: true, alias: 'serviceMenu' })
- menu!: ServiceMenu
+ @Input({ required: true })
+ service!: PackageDataEntry
+
+ get color(): string {
+ return this.service.installed?.outboundProxy
+ ? 'var(--tui-success-fill)'
+ : 'var(--tui-warning-fill)'
+ }
+
+ get proxy(): string {
+ switch (this.service.installed?.outboundProxy) {
+ case 'primary':
+ return 'System Primary'
+ case 'mirror':
+ return 'Mirror P2P'
+ default:
+ return this.service.installed?.outboundProxy?.proxyId || 'None'
+ }
+ }
}
diff --git a/frontend/projects/ui/src/app/apps/portal/routes/service/components/status.component.ts b/frontend/projects/ui/src/app/apps/portal/routes/service/components/status.component.ts
index 59eb44e8b..a636fbc68 100644
--- a/frontend/projects/ui/src/app/apps/portal/routes/service/components/status.component.ts
+++ b/frontend/projects/ui/src/app/apps/portal/routes/service/components/status.component.ts
@@ -24,6 +24,15 @@ import { InstallProgressPipeModule } from 'src/app/common/install-progress/insta
`,
+ styles: [
+ `
+ :host {
+ font-size: x-large;
+ margin: 1em 0;
+ display: block;
+ }
+ `,
+ ],
changeDetection: ChangeDetectionStrategy.OnPush,
standalone: true,
imports: [CommonModule, InstallProgressPipeModule],
diff --git a/frontend/projects/ui/src/app/apps/portal/routes/service/pipes/interface-info.pipe.ts b/frontend/projects/ui/src/app/apps/portal/routes/service/pipes/interface-info.pipe.ts
index 8ab557b2b..cc1d793dc 100644
--- a/frontend/projects/ui/src/app/apps/portal/routes/service/pipes/interface-info.pipe.ts
+++ b/frontend/projects/ui/src/app/apps/portal/routes/service/pipes/interface-info.pipe.ts
@@ -1,18 +1,15 @@
-import { inject, Pipe, PipeTransform } from '@angular/core'
-import { TuiDialogService } from '@taiga-ui/core'
-import { PolymorpheusComponent } from '@tinkoff/ng-polymorpheus'
+import { Pipe, PipeTransform } from '@angular/core'
import {
InterfaceInfo,
PackageDataEntry,
} from 'src/app/services/patch-db/data-model'
-import { ServiceInterfaceModal } from '../modals/interface.component'
export interface ExtendedInterfaceInfo extends InterfaceInfo {
id: string
icon: string
color: string
typeDetail: string
- action: () => void
+ routerLink: string
}
@Pipe({
@@ -20,12 +17,7 @@ export interface ExtendedInterfaceInfo extends InterfaceInfo {
standalone: true,
})
export class InterfaceInfoPipe implements PipeTransform {
- private readonly dialogs = inject(TuiDialogService)
-
- transform({
- manifest,
- installed,
- }: PackageDataEntry): ExtendedInterfaceInfo[] {
+ transform({ installed }: PackageDataEntry): ExtendedInterfaceInfo[] {
return Object.entries(installed!.interfaceInfo).map(([id, val]) => {
let color: string
let icon: string
@@ -60,17 +52,7 @@ export class InterfaceInfoPipe implements PipeTransform {
color,
icon,
typeDetail,
- action: () =>
- this.dialogs
- .open(new PolymorpheusComponent(ServiceInterfaceModal), {
- label: val.name,
- size: 'l',
- data: {
- packageId: manifest.id,
- interfaceId: id,
- },
- })
- .subscribe(),
+ routerLink: `./interface/${id}`,
}
})
}
diff --git a/frontend/projects/ui/src/app/apps/portal/routes/service/pipes/to-dependencies.pipe.ts b/frontend/projects/ui/src/app/apps/portal/routes/service/pipes/to-dependencies.pipe.ts
index c0951d6d6..9e575e9ba 100644
--- a/frontend/projects/ui/src/app/apps/portal/routes/service/pipes/to-dependencies.pipe.ts
+++ b/frontend/projects/ui/src/app/apps/portal/routes/service/pipes/to-dependencies.pipe.ts
@@ -10,6 +10,8 @@ import { FormDialogService } from 'src/app/services/form-dialog.service'
import { ServiceConfigModal } from '../modals/config.component'
import { DependencyInfo } from '../types/dependency-info'
import { PackageConfigData } from '../types/package-config-data'
+import { NavigationService } from '../../../services/navigation.service'
+import { toRouterLink } from '../../../utils/to-router-link'
@Pipe({
name: 'toDependencies',
@@ -19,23 +21,32 @@ export class ToDependenciesPipe implements PipeTransform {
constructor(
private readonly router: Router,
private readonly formDialog: FormDialogService,
+ private readonly navigation: NavigationService,
) {}
- transform(pkg: PackageDataEntry): DependencyInfo[] {
- if (!pkg.installed) return []
+ transform(pkg: PackageDataEntry): DependencyInfo[] | null {
+ if (!pkg.installed) return null
- return Object.keys(pkg.installed['current-dependencies'])
- .filter(depId => !!pkg.manifest.dependencies[depId])
+ const deps = Object.keys(pkg.installed['current-dependencies'])
+ .filter(depId => pkg.manifest.dependencies[depId])
.map(depId => this.setDepValues(pkg, depId))
+
+ return deps.length ? deps : null
}
- private setDepValues(pkg: PackageDataEntry, depId: string): DependencyInfo {
+ private setDepValues(pkg: PackageDataEntry, id: string): DependencyInfo {
+ const error = pkg.installed!.status['dependency-errors'][id]
+ const depInfo = pkg.installed!['dependency-info'][id]
+ const version = pkg.manifest.dependencies[id].version
+ const title = depInfo?.title || id
+ const icon = depInfo?.icon || ''
+
let errorText = ''
let actionText = 'View'
- let action = (): unknown =>
- this.router.navigate([`portal`, `service`, depId])
-
- const error = pkg.installed!.status['dependency-errors'][depId]
+ let action = () => {
+ this.navigation.addTab({ icon, title, routerLink: toRouterLink(id) })
+ this.router.navigate([`portal`, `service`, id])
+ }
if (error) {
// health checks failed
@@ -45,12 +56,12 @@ export class ToDependenciesPipe implements PipeTransform {
} else if (error.type === DependencyErrorType.NotInstalled) {
errorText = 'Not installed'
actionText = 'Install'
- action = () => this.fixDep(pkg, 'install', depId)
+ action = () => this.fixDep(pkg, 'install', id)
// incorrect version
} else if (error.type === DependencyErrorType.IncorrectVersion) {
errorText = 'Incorrect version'
actionText = 'Update'
- action = () => this.fixDep(pkg, 'update', depId)
+ action = () => this.fixDep(pkg, 'update', id)
// not running
} else if (error.type === DependencyErrorType.NotRunning) {
errorText = 'Not running'
@@ -59,24 +70,14 @@ export class ToDependenciesPipe implements PipeTransform {
} else if (error.type === DependencyErrorType.ConfigUnsatisfied) {
errorText = 'Config not satisfied'
actionText = 'Auto config'
- action = () => this.fixDep(pkg, 'configure', depId)
+ action = () => this.fixDep(pkg, 'configure', id)
} else if (error.type === DependencyErrorType.Transitive) {
errorText = 'Dependency has a dependency issue'
}
errorText = `${errorText}. ${pkg.manifest.title} will not work as expected.`
}
- const depInfo = pkg.installed!['dependency-info'][depId]
-
- return {
- id: depId,
- version: pkg.manifest.dependencies[depId].version,
- title: depInfo?.title || depId,
- icon: depInfo?.icon || '',
- errorText,
- actionText,
- action,
- }
+ return { id, icon, title, version, errorText, actionText, action }
}
async fixDep(
diff --git a/frontend/projects/ui/src/app/apps/portal/routes/service/pipes/to-menu.pipe.ts b/frontend/projects/ui/src/app/apps/portal/routes/service/pipes/to-menu.pipe.ts
index 133269fad..b1110eb5d 100644
--- a/frontend/projects/ui/src/app/apps/portal/routes/service/pipes/to-menu.pipe.ts
+++ b/frontend/projects/ui/src/app/apps/portal/routes/service/pipes/to-menu.pipe.ts
@@ -14,9 +14,7 @@ import { FormDialogService } from 'src/app/services/form-dialog.service'
import { ProxyService } from 'src/app/services/proxy.service'
import { PackageConfigData } from '../types/package-config-data'
import { ServiceConfigModal } from '../modals/config.component'
-import { ServiceLogsModal } from '../modals/logs.component'
import { ServiceCredentialsModal } from '../modals/credentials.component'
-import { ServiceActionsModal } from '../modals/actions.component'
export interface ServiceMenu {
icon: string
@@ -29,7 +27,7 @@ export interface ServiceMenu {
name: 'toMenu',
standalone: true,
})
-export class ToMenusPipe implements PipeTransform {
+export class ToMenuPipe implements PipeTransform {
private readonly api = inject(ApiService)
private readonly dialogs = inject(TuiDialogService)
private readonly formDialog = inject(FormDialogService)
@@ -69,11 +67,9 @@ export class ToMenusPipe implements PipeTransform {
name: 'Actions',
description: `Uninstall and other commands specific to ${manifest.title}`,
action: () =>
- this.showDialog(
- `${manifest.title} credentials`,
- manifest.id,
- ServiceActionsModal,
- ),
+ this.router.navigate(['actions'], {
+ relativeTo: this.route,
+ }),
},
{
icon: 'tuiIconShieldLarge',
@@ -86,11 +82,9 @@ export class ToMenusPipe implements PipeTransform {
name: 'Logs',
description: `Raw, unfiltered logs`,
action: () =>
- this.showDialog(
- `${manifest.title} logs`,
- manifest.id,
- ServiceLogsModal,
- ),
+ this.router.navigate(['logs'], {
+ relativeTo: this.route,
+ }),
},
url
? {
@@ -99,7 +93,6 @@ export class ToMenusPipe implements PipeTransform {
description: `View ${manifest.title} on the Marketplace`,
action: () =>
this.router.navigate(['marketplace', manifest.id], {
- relativeTo: this.route,
queryParams: { url },
}),
}
diff --git a/frontend/projects/ui/src/app/apps/portal/routes/service/modals/actions.component.ts b/frontend/projects/ui/src/app/apps/portal/routes/service/routes/actions.component.ts
similarity index 82%
rename from frontend/projects/ui/src/app/apps/portal/routes/service/modals/actions.component.ts
rename to frontend/projects/ui/src/app/apps/portal/routes/service/routes/actions.component.ts
index cae649863..2529d8535 100644
--- a/frontend/projects/ui/src/app/apps/portal/routes/service/modals/actions.component.ts
+++ b/frontend/projects/ui/src/app/apps/portal/routes/service/routes/actions.component.ts
@@ -1,18 +1,16 @@
import { CommonModule } from '@angular/common'
-import { ChangeDetectionStrategy, Component, Inject } from '@angular/core'
-import { Router } from '@angular/router'
+import { ChangeDetectionStrategy, Component, inject } from '@angular/core'
+import { ActivatedRoute, Router } from '@angular/router'
import {
isEmptyObject,
WithId,
ErrorService,
LoadingService,
+ getPkgId,
} from '@start9labs/shared'
-import { TuiDialogContext, TuiDialogService } from '@taiga-ui/core'
+import { TuiDialogService } from '@taiga-ui/core'
import { TUI_PROMPT } from '@taiga-ui/kit'
-import {
- POLYMORPHEUS_CONTEXT,
- PolymorpheusComponent,
-} from '@tinkoff/ng-polymorpheus'
+import { PolymorpheusComponent } from '@tinkoff/ng-polymorpheus'
import { PatchDB } from 'patch-db-client'
import { filter, switchMap, timer } from 'rxjs'
import { ApiService } from 'src/app/services/api/embassy-api.service'
@@ -27,7 +25,11 @@ import { FormDialogService } from 'src/app/services/form-dialog.service'
import { FormPage } from 'src/app/apps/ui/modals/form/form.page'
import { ServiceActionComponent } from '../components/action.component'
import { ServiceActionSuccessComponent } from '../components/action-success.component'
+import { DesktopService } from '../../../services/desktop.service'
import { GroupActionsPipe } from '../pipes/group-actions.pipe'
+import { updateTab } from '../utils/update-tab'
+import { NavigationService } from '../../../services/navigation.service'
+import { toRouterLink } from '../../../utils/to-router-link'
@Component({
template: `
@@ -41,7 +43,9 @@ import { GroupActionsPipe } from '../pipes/group-actions.pipe'
>
- Actions for {{ pkg.manifest.title }}
+
+ Actions for {{ pkg.manifest.title }}
+