fix dep error display, show starting if any health check starting, show disabled health check message, remove loader from service list, animated dots, better color (#3025)

* refector addresses to not need gateways array

* fix dep error display, show starting if any health check starting, show disabled health check message, remove loader from service list, animated dots, better color

* fix: fix action results textfields

---------

Co-authored-by: waterplea <alexander@inkin.ru>
This commit is contained in:
Matt Hill
2025-09-17 10:32:20 -06:00
committed by GitHub
parent 1d331d7810
commit 7eecf29449
9 changed files with 60 additions and 99 deletions

View File

@@ -110,6 +110,7 @@ body {
animation-fill-mode: forwards;
text-align: left;
width: 1em;
margin-left: -.3rem;
}
@keyframes ellipsis-dot {

View File

@@ -57,7 +57,7 @@
var(--tui-status-warning) 24%,
transparent
);
--tui-status-info: rgba(128, 89, 229, 1);
--tui-status-info: rgba(53, 96, 240, 1);
--tui-status-info-pale: color-mix(
in hsl,
var(--tui-status-info) 12%,

View File

@@ -166,12 +166,10 @@ export class InterfaceService {
}, [] as AddressWithInfo[])
return {
common: bestAddrs.map(a =>
this.toDisplayAddress(a, gateways, host.publicDomains),
),
common: bestAddrs.map(a => this.toDisplayAddress(a, host.publicDomains)),
uncommon: allAddressesWithInfo
.filter(a => !bestAddrs.includes(a))
.map(a => this.toDisplayAddress(a, gateways, host.publicDomains)),
.map(a => this.toDisplayAddress(a, host.publicDomains)),
}
}
@@ -314,7 +312,6 @@ export class InterfaceService {
private toDisplayAddress(
{ info, url, gateway }: AddressWithInfo,
gateways: GatewayPlus[],
publicDomains: Record<string, T.PublicDomainConfig>,
): DisplayAddress {
let access: DisplayAddress['access']
@@ -360,11 +357,11 @@ export class InterfaceService {
// ** Not Tor **
} else {
const port = info.hostname.sslPort || info.hostname.port
const gateway = gateways.find(g => g.id === info.gateway.id)!
gatewayName = gateway.name
const g = gateway!
gatewayName = g.name
const gatewayLanIpv4 = gateway.lanIpv4[0]
const isWireguard = gateway.ipInfo.deviceType === 'wireguard'
const gatewayLanIpv4 = g.lanIpv4[0]
const isWireguard = g.ipInfo.deviceType === 'wireguard'
const localIdeal = this.i18n.transform('Ideal for local access')
const lanRequired = this.i18n.transform(
@@ -405,9 +402,9 @@ export class InterfaceService {
),
rootCaRequired,
]
if (!gateway.public) {
if (!g.public) {
bullets.push(
`${portForwarding} "${gatewayName}": ${port} -> ${gateway.subnets.find(s => s.isIpv4())?.address}:${port}`,
`${portForwarding} "${gatewayName}": ${port} -> ${g.subnets.find(s => s.isIpv4())?.address}:${port}`,
)
}
} else {
@@ -439,12 +436,12 @@ export class InterfaceService {
if (info.public) {
access = 'public'
bullets = [
`${dnsFor} ${info.hostname.value} ${resolvesTo} ${gateway.ipInfo.wanIp}`,
`${dnsFor} ${info.hostname.value} ${resolvesTo} ${g.ipInfo.wanIp}`,
]
if (!gateway.public) {
if (!g.public) {
bullets.push(
`${portForwarding} "${gatewayName}": ${port} -> ${gateway.subnets.find(s => s.isIpv4())?.address}:${port === 443 ? 5443 : port}`,
`${portForwarding} "${gatewayName}": ${port} -> ${g.subnets.find(s => s.isIpv4())?.address}:${port === 443 ? 5443 : port}`,
)
}

View File

@@ -97,10 +97,8 @@ export class ServiceHealthCheckComponent {
return `${this.i18n.transform('Success')}: ${this.healthCheck.message || 'health check passing'}`
case 'loading':
case 'failure':
return this.healthCheck.message
// disabled
default:
return this.healthCheck.result
case 'disabled':
return this.healthCheck.message || this.healthCheck.result
}
}
}

View File

@@ -6,22 +6,26 @@ import {
} from '@angular/core'
import { i18nKey, i18nPipe } from '@start9labs/shared'
import { tuiPure } from '@taiga-ui/cdk'
import { TuiIcon, TuiLoader } from '@taiga-ui/core'
import { TuiIcon } from '@taiga-ui/core'
import { getProgressText } from 'src/app/routes/portal/routes/services/pipes/install-progress.pipe'
import { PackageDataEntry } from 'src/app/services/patch-db/data-model'
import { renderPkgStatus } from 'src/app/services/pkg-status-rendering.service'
import {
PrimaryRendering,
renderPkgStatus,
} from 'src/app/services/pkg-status-rendering.service'
@Component({
selector: 'td[appStatus]',
template: `
@if (loading) {
<tui-loader size="s" />
} @else {
@if (!healthy) {
<tui-icon icon="@tui.triangle-alert" class="g-warning" />
}
@if (!healthy) {
<tui-icon icon="@tui.triangle-alert" class="g-warning" />
}
<b [style.color]="color">{{ status | i18n }}</b>
@if (showDots) {
<span class="loading-dots g-info"></span>
}
<b [style.color]="color">{{ status | i18n }}{{ dots }}</b>
`,
styles: `
:host {
@@ -37,7 +41,7 @@ import { renderPkgStatus } from 'src/app/services/pkg-status-rendering.service'
}
`,
changeDetection: ChangeDetectionStrategy.OnPush,
imports: [TuiIcon, TuiLoader, i18nPipe],
imports: [TuiIcon, i18nPipe],
})
export class StatusComponent {
@Input()
@@ -58,10 +62,6 @@ export class StatusComponent {
)
}
get loading(): boolean {
return this.color === 'var(--tui-status-info)'
}
@tuiPure
getStatus(pkg: PackageDataEntry) {
return renderPkgStatus(pkg)
@@ -72,35 +72,10 @@ export class StatusComponent {
return `${this.i18n.transform('Installing')}... ${this.i18n.transform(getProgressText(this.pkg.stateInfo.installingInfo.progress.overall))}` as i18nKey
}
switch (this.getStatus(this.pkg).primary) {
case 'running':
return 'Running'
case 'stopped':
return 'Stopped'
case 'taskRequired':
return 'Task Required'
case 'updating':
return 'Updating'
case 'stopping':
return 'Stopping'
case 'starting':
return 'Starting'
case 'backingUp':
return 'Backing Up'
case 'restarting':
return 'Restarting'
case 'removing':
return 'Removing'
case 'restoring':
return 'Restoring'
case 'error':
return 'Error'
default:
return 'Unknown'
}
return PrimaryRendering[this.getStatus(this.pkg).primary].display
}
get dots(): '...' | '' {
get showDots() {
switch (this.getStatus(this.pkg).primary) {
case 'updating':
case 'stopping':
@@ -108,9 +83,9 @@ export class StatusComponent {
case 'backingUp':
case 'restarting':
case 'removing':
return '...'
return true
default:
return ''
return false
}
}

View File

@@ -27,7 +27,6 @@ import { QrCodeComponent } from 'ng-qrcode'
tuiTextfield
[readOnly]="true"
[ngModel]="member.value"
[style.border-inline-end-width.rem]="border"
[type]="member.masked && masked ? 'password' : 'text'"
/>
@if (member.masked) {
@@ -129,16 +128,6 @@ export class ActionSuccessMemberComponent {
masked = true
get border(): number {
let border = 0
if (this.member.masked) border += 2
if (this.member.copyable) border += 2
if (this.member.qr) border += 2
return border
}
show(template: TemplateRef<any>) {
const masked = this.masked

View File

@@ -23,7 +23,6 @@ import { SingleResult } from './types'
tuiTextfield
[readOnly]="true"
[ngModel]="single.value"
[style.border-inline-end-width.rem]="border"
[type]="single.masked && masked ? 'password' : 'text'"
/>
@if (single.masked) {
@@ -105,15 +104,6 @@ export class ActionSuccessSingleComponent {
masked = true
get border(): number {
let border = 0
if (this.single.masked) border += 2
if (this.single.copyable) border += 2
return border
}
copy() {
const el = this.input.nativeElement

View File

@@ -126,13 +126,14 @@ export class DepErrorService {
const expected = currentDep?.versionRange || ''
// incorrect version
if (!this.exver.satisfies(depManifest.version, expected)) {
if (depManifest.satisfies.some(v => !this.exver.satisfies(v, expected))) {
return {
expected,
type: 'incorrectVersion',
received: depManifest.version,
}
if (
!this.exver.satisfies(depManifest.version, expected) &&
!depManifest.satisfies.some(v => this.exver.satisfies(v, expected))
) {
return {
expected,
type: 'incorrectVersion',
received: depManifest.version,
}
}

View File

@@ -25,11 +25,21 @@ export function getInstalledPrimaryStatus({
tasks,
status,
}: T.PackageDataEntry): PrimaryStatus {
return Object.values(tasks).some(
t => t.active && t.task.severity === 'critical',
)
? 'taskRequired'
: status.main
if (
Object.values(tasks).some(t => t.active && t.task.severity === 'critical')
) {
return 'taskRequired'
}
if (
Object.values(status.main === 'running' && status.health)
.filter(h => !!h)
.some(h => h.result === 'starting')
) {
return 'starting'
}
return status.main
}
function getHealthStatus(status: T.MainStatus): T.HealthStatus | null {
@@ -43,14 +53,14 @@ function getHealthStatus(status: T.MainStatus): T.HealthStatus | null {
return 'failure'
}
if (values.some(h => h.result === 'loading')) {
return 'loading'
}
if (values.some(h => h.result === 'starting')) {
return 'starting'
}
if (values.some(h => h.result === 'loading')) {
return 'loading'
}
return 'success'
}