From 6234391229b3f00abfcee311c5a845397fbac002 Mon Sep 17 00:00:00 2001 From: waterplea Date: Mon, 3 Jun 2024 15:10:49 +0500 Subject: [PATCH 1/6] feat: add mobile view for all the tables Signed-off-by: waterplea --- .../system/backups/backups.component.ts | 42 ++-- .../backups/components/physical.component.ts | 119 +++++++---- .../backups/components/targets.component.ts | 155 ++++++++------ .../backups/components/upcoming.component.ts | 139 +++++++----- .../backups/modals/history.component.ts | 174 +++++++++------- .../system/backups/modals/jobs.component.ts | 115 ++++++---- .../backups/modals/targets.component.ts | 87 ++++---- .../system/notifications/item.component.ts | 90 +++++--- .../routes/domains/table.component.ts | 103 ++++++--- .../settings/routes/proxies/menu.component.ts | 123 ----------- .../routes/proxies/proxies.component.ts | 2 +- .../routes/proxies/table.component.ts | 197 +++++++++++++++--- .../routes/sessions/table.component.ts | 104 +++++---- .../settings/routes/ssh/table.component.ts | 105 +++++++--- web/projects/ui/src/styles.scss | 36 ++++ 15 files changed, 964 insertions(+), 627 deletions(-) delete mode 100644 web/projects/ui/src/app/routes/portal/routes/system/settings/routes/proxies/menu.component.ts diff --git a/web/projects/ui/src/app/routes/portal/routes/system/backups/backups.component.ts b/web/projects/ui/src/app/routes/portal/routes/system/backups/backups.component.ts index 01f29e551..2778a68c8 100644 --- a/web/projects/ui/src/app/routes/portal/routes/system/backups/backups.component.ts +++ b/web/projects/ui/src/app/routes/portal/routes/system/backups/backups.component.ts @@ -1,44 +1,34 @@ -import { CommonModule } from '@angular/common' import { ChangeDetectionStrategy, Component, inject } from '@angular/core' -import { TuiDialogService, TuiSvgModule } from '@taiga-ui/core' -import { TuiFadeModule } from '@taiga-ui/experimental' -import { BackupsCreateService } from './services/create.service' -import { BackupsRestoreService } from './services/restore.service' +import { TuiDialogService } from '@taiga-ui/core' +import { TuiIconModule } from '@taiga-ui/experimental' import { BackupsUpcomingComponent } from './components/upcoming.component' -import { TARGETS } from './modals/targets.component' import { HISTORY } from './modals/history.component' import { JOBS } from './modals/jobs.component' +import { TARGETS } from './modals/targets.component' +import { BackupsCreateService } from './services/create.service' +import { BackupsRestoreService } from './services/restore.service' @Component({ template: `

Options

- + @for (option of options; track $index) { + + }

Upcoming Jobs

-
-
-
+
`, host: { class: 'g-page' }, changeDetection: ChangeDetectionStrategy.OnPush, standalone: true, - imports: [ - CommonModule, - TuiSvgModule, - TuiFadeModule, - BackupsUpcomingComponent, - ], + imports: [BackupsUpcomingComponent, TuiIconModule], }) export default class BackupsComponent { private readonly dialogs = inject(TuiDialogService) diff --git a/web/projects/ui/src/app/routes/portal/routes/system/backups/components/physical.component.ts b/web/projects/ui/src/app/routes/portal/routes/system/backups/components/physical.component.ts index 5b6357993..3031fb28f 100644 --- a/web/projects/ui/src/app/routes/portal/routes/system/backups/components/physical.component.ts +++ b/web/projects/ui/src/app/routes/portal/routes/system/backups/components/physical.component.ts @@ -25,51 +25,94 @@ import { UnknownDisk } from 'src/app/services/api/api.types' - - - {{ disk.vendor || 'unknown make' }}, - {{ disk.model || 'unknown model' }} - - {{ disk.label }} - {{ disk.capacity | convertBytes }} - {{ disk.used ? (disk.used | convertBytes) : 'Unknown' }} - - - - - + @for (disk of backupsPhysical; track $index) { - -
Loading
+ + {{ disk.vendor || 'unknown make' }}, + {{ disk.model || 'unknown model' }} + + {{ disk.label }} + {{ disk.capacity | convertBytes }} + + {{ disk.used ? (disk.used | convertBytes) : 'Unknown' }} + + + -
- - - - To add a new physical backup target, connect the drive and click - refresh. - - - + } @empty { + @if (backupsPhysical) { + + + To add a new physical backup target, connect the drive and click + refresh. + + + } @else { + +
Loading
+ + } + } `, + styles: ` + :host-context(tui-root._mobile) { + tr { + grid-template-columns: 1fr 1fr; + } + + td:only-child { + grid-column: span 2; + } + + .model { + order: 1; + white-space: nowrap; + color: var(--tui-text-02); + } + + .actions { + order: 2; + padding: 0; + text-align: right; + } + + .title { + order: 3; + grid-column: span 2; + font-weight: bold; + text-transform: uppercase; + } + + .capacity { + order: 4; + + &::before { + content: 'Capacity: '; + } + } + + .used { + order: 5; + text-align: right; + + &::before { + content: 'Used: '; + } + } + } + `, changeDetection: ChangeDetectionStrategy.OnPush, standalone: true, - imports: [ - CommonModule, - TuiForModule, - TuiSvgModule, - TuiButtonModule, - UnitConversionPipesModule, - ], + imports: [TuiButtonModule, UnitConversionPipesModule], }) export class BackupsPhysicalComponent { @Input() diff --git a/web/projects/ui/src/app/routes/portal/routes/system/backups/components/targets.component.ts b/web/projects/ui/src/app/routes/portal/routes/system/backups/components/targets.component.ts index 4fcc27b91..4b34588f6 100644 --- a/web/projects/ui/src/app/routes/portal/routes/system/backups/components/targets.component.ts +++ b/web/projects/ui/src/app/routes/portal/routes/system/backups/components/targets.component.ts @@ -1,4 +1,3 @@ -import { CommonModule } from '@angular/common' import { ChangeDetectionStrategy, Component, @@ -7,13 +6,8 @@ import { Input, Output, } from '@angular/core' -import { TuiForModule } from '@taiga-ui/cdk' -import { TuiButtonModule } from '@taiga-ui/experimental' -import { - TuiDialogOptions, - TuiDialogService, - TuiSvgModule, -} from '@taiga-ui/core' +import { TuiDialogOptions, TuiDialogService } from '@taiga-ui/core' +import { TuiButtonModule, TuiIconModule } from '@taiga-ui/experimental' import { TUI_PROMPT, TuiPromptData } from '@taiga-ui/kit' import { filter, map, Subject, switchMap } from 'rxjs' import { BackupTarget } from 'src/app/services/api/api.types' @@ -32,69 +26,110 @@ import { GetBackupIconPipe } from '../pipes/get-backup-icon.pipe' - - {{ target.name }} - - - {{ target.type | titlecase }} - - - - - {{ target.path }} - - - - - - - - -
Loading
+ @for (target of backupsTargets; track $index) { + + {{ target.name }} + + + {{ target.type }} + + + + + {{ target.path }} + + + -
- - No saved backup targets. - + } @empty { + @if (backupsTargets) { + No saved backup targets. + } @else { + @for (i of ['', '']; track $index) { + +
Loading
+ + } + } + } `, + styles: ` + tui-icon { + font-size: 1rem; + vertical-align: sub; + margin-inline-end: 0.25rem; + } + + :host-context(tui-root._mobile) { + tr { + grid-template-columns: 1.5rem 2fr 1fr; + } + + td:only-child { + grid-column: span 3; + } + + .title { + order: 2; + font-weight: bold; + text-transform: uppercase; + } + + .available { + order: 1; + } + + .actions { + order: 3; + padding: 0; + text-align: right; + } + + .path { + order: 4; + color: var(--tui-text-03); + grid-column: span 2; + overflow: hidden; + text-overflow: ellipsis; + } + + .type { + order: 5; + text-align: right; + text-transform: capitalize; + color: var(--tui-text-02); + } + } + `, changeDetection: ChangeDetectionStrategy.OnPush, standalone: true, - imports: [ - CommonModule, - TuiForModule, - TuiSvgModule, - TuiButtonModule, - GetBackupIconPipe, - ], + imports: [TuiButtonModule, GetBackupIconPipe, TuiIconModule], }) export class BackupsTargetsComponent { private readonly dialogs = inject(TuiDialogService) readonly delete$ = new Subject() - readonly update$ = new Subject() @Input() backupsTargets: readonly BackupTarget[] | null = null diff --git a/web/projects/ui/src/app/routes/portal/routes/system/backups/components/upcoming.component.ts b/web/projects/ui/src/app/routes/portal/routes/system/backups/components/upcoming.component.ts index 55d67b043..cfe2b0630 100644 --- a/web/projects/ui/src/app/routes/portal/routes/system/backups/components/upcoming.component.ts +++ b/web/projects/ui/src/app/routes/portal/routes/system/backups/components/upcoming.component.ts @@ -1,12 +1,12 @@ -import { CommonModule } from '@angular/common' +import { DatePipe } from '@angular/common' import { ChangeDetectionStrategy, Component, inject } from '@angular/core' -import { TuiForModule } from '@taiga-ui/cdk' -import { TuiSvgModule } from '@taiga-ui/core' +import { toSignal } from '@angular/core/rxjs-interop' +import { TuiIconModule } from '@taiga-ui/experimental' +import { CronJob } from 'cron' import { PatchDB } from 'patch-db-client' import { from, map } from 'rxjs' -import { CronJob } from 'cron' -import { DataModel } from 'src/app/services/patch-db/data-model' import { ApiService } from 'src/app/services/api/embassy-api.service' +import { DataModel } from 'src/app/services/patch-db/data-model' import { GetBackupIconPipe } from '../pipes/get-backup-icon.pipe' @Component({ @@ -20,58 +20,97 @@ import { GetBackupIconPipe } from '../pipes/get-backup-icon.pipe' Packages - - - - - Running - - - {{ job.next | date: 'MMM d, y, h:mm a' }} - - - {{ job.name }} - - - {{ job.target.name }} - - Packages: {{ job.packageIds.length }} - - - You have no active or upcoming backup jobs - - - -
Loading
- -
- + @if (current(); as current) { + + @for (job of upcoming(); track $index) { + + + @if (current.id === job.id) { + Running + } @else { + {{ job.next | date: 'MMM d, y, h:mm a' }} + } + + {{ job.name }} + + + {{ job.target.name }} + + Packages: {{ job.packageIds.length }} + + } @empty { + @if (upcoming()) { + + You have no active or upcoming backup jobs + + } @else { + @for (row of ['', '']; track $index) { + +
Loading
+ + } + } + } + + } + `, + styles: ` + :host { + grid-template-columns: 1fr 1fr; + } + + .date, + .name { + grid-column: span 2; + } + + tui-icon { + font-size: 1rem; + vertical-align: sub; + margin-inline-end: 0.25rem; + } + + :host-context(tui-root._mobile) { + .date { + color: var(--tui-text-02); + } + + .name { + text-transform: uppercase; + font-weight: bold; + } + + .packages { + text-align: right; + } + } `, changeDetection: ChangeDetectionStrategy.OnPush, standalone: true, - imports: [CommonModule, TuiForModule, TuiSvgModule, GetBackupIconPipe], + imports: [GetBackupIconPipe, DatePipe, TuiIconModule], }) export class BackupsUpcomingComponent { - readonly current$ = inject(PatchDB) - .watch$('serverInfo', 'statusInfo', 'currentBackup', 'job') - .pipe(map(job => job || {})) + readonly current = toSignal( + inject(PatchDB) + .watch$('serverInfo', 'statusInfo', 'currentBackup', 'job') + .pipe(map(job => job || {})), + ) - readonly upcoming$ = from(inject(ApiService).getBackupJobs({})).pipe( - map(jobs => - jobs - .map(job => { - const nextDate = new CronJob(job.cron, () => {}).nextDate() + readonly upcoming = toSignal( + from(inject(ApiService).getBackupJobs({})).pipe( + map(jobs => + jobs + .map(job => { + const nextDate = new CronJob(job.cron, () => {}).nextDate() - return { - ...job, - next: nextDate.toISO(), - diff: nextDate.diffNow().milliseconds, - } - }) - .sort((a, b) => a.diff - b.diff), + return { + ...job, + next: nextDate.toISO(), + diff: nextDate.diffNow().milliseconds, + } + }) + .sort((a, b) => a.diff - b.diff), + ), ), ) } diff --git a/web/projects/ui/src/app/routes/portal/routes/system/backups/modals/history.component.ts b/web/projects/ui/src/app/routes/portal/routes/system/backups/modals/history.component.ts index a91bb1a7b..d12f19eb8 100644 --- a/web/projects/ui/src/app/routes/portal/routes/system/backups/modals/history.component.ts +++ b/web/projects/ui/src/app/routes/portal/routes/system/backups/modals/history.component.ts @@ -1,27 +1,27 @@ import { CommonModule } from '@angular/common' -import { ChangeDetectionStrategy, Component, inject } from '@angular/core' +import { + ChangeDetectionStrategy, + Component, + inject, + signal, +} from '@angular/core' import { FormsModule } from '@angular/forms' import { ErrorService, LoadingService } from '@start9labs/shared' -import { PolymorpheusComponent } from '@tinkoff/ng-polymorpheus' -import { - ALWAYS_FALSE_HANDLER, - ALWAYS_TRUE_HANDLER, - TuiForModule, -} from '@taiga-ui/cdk' -import { TuiDialogService, TuiLinkModule, TuiSvgModule } from '@taiga-ui/core' -import { TuiButtonModule, TuiFadeModule } from '@taiga-ui/experimental' +import { ALWAYS_FALSE_HANDLER, ALWAYS_TRUE_HANDLER } from '@taiga-ui/cdk' +import { TuiDialogService, TuiLinkModule } from '@taiga-ui/core' +import { TuiButtonModule, TuiIconModule } from '@taiga-ui/experimental' import { TuiCheckboxModule } from '@taiga-ui/kit' +import { PolymorpheusComponent } from '@tinkoff/ng-polymorpheus' import { BehaviorSubject } from 'rxjs' +import { REPORT } from 'src/app/components/report.component' import { BackupRun } from 'src/app/services/api/api.types' import { ApiService } from 'src/app/services/api/embassy-api.service' -import { REPORT } from 'src/app/components/report.component' import { DurationPipe } from '../pipes/duration.pipe' -import { HasErrorPipe } from '../pipes/has-error.pipe' import { GetBackupIconPipe } from '../pipes/get-backup-icon.pipe' +import { HasErrorPipe } from '../pipes/has-error.pipe' @Component({ template: ` -

Past Events

-
- - - - - - - - - - - - - - +
- - Started AtDurationResultJobTarget
+ + + + + + + + + + + + @for (run of runs(); track $index) { + + - - + + - - - - - - - - + } @empty { + @if (runs()) { - - -
+ + Started AtDurationJobResultTarget
{{ run.startedAt | date: 'medium' }}{{ run.startedAt | duration: run.completedAt }} Minutes - - - - + + {{ run.startedAt | duration: run.completedAt }} minutes + {{ run.job.name || 'No job' }} + @if (run.report | hasError) { + + } @else { + + } {{ run.job.name || 'No job' }} - + + {{ run.job.target.name }}
Loading
No backups have been run yet.
-
+ } @else { + @for (row of ['', '']; track $index) { + +
Loading
+ + } + } + } + + + `, + styles: ` + tui-icon { + font-size: 1rem; + vertical-align: sub; + margin-inline-end: 0.25rem; + } + + :host-context(tui-root._mobile) { + tr { + grid-template-columns: 1.75rem 1fr 7rem; + } + + td:only-child { + grid-column: span 3; + } + + .title { + grid-column: span 2; + font-weight: bold; + text-transform: uppercase; + } + + .duration, + .result { + text-align: right; + } + } `, changeDetection: ChangeDetectionStrategy.OnPush, standalone: true, imports: [ CommonModule, FormsModule, - TuiForModule, TuiButtonModule, TuiCheckboxModule, - TuiSvgModule, + TuiIconModule, TuiLinkModule, - TuiFadeModule, DurationPipe, HasErrorPipe, GetBackupIconPipe, @@ -118,9 +134,7 @@ export class BackupsHistoryModal { private readonly errorService = inject(ErrorService) private readonly loader = inject(LoadingService) - readonly loading$ = new BehaviorSubject(true) - - runs: BackupRun[] | null = null + runs = signal(null) selected: boolean[] = [] get all(): boolean | null { @@ -143,13 +157,11 @@ export class BackupsHistoryModal { async ngOnInit() { try { - this.runs = await this.api.getBackupRuns({}) - this.selected = this.runs.map(ALWAYS_FALSE_HANDLER) + this.runs.set(await this.api.getBackupRuns({})) + this.selected = this.runs()?.map(ALWAYS_FALSE_HANDLER) || [] } catch (e: any) { - this.runs = [] + this.runs.set([]) this.errorService.handleError(e) - } finally { - this.loading$.next(false) } } @@ -157,12 +169,12 @@ export class BackupsHistoryModal { const loader = this.loader.open('Deleting...').subscribe() const ids = this.selected .filter(Boolean) - .map((_, i) => this.runs?.[i].id || '') + .map((_, i) => this.runs()?.[i].id || '') try { await this.api.deleteBackupRuns({ ids }) - this.runs = this.runs?.filter(r => !ids.includes(r.id)) || [] - this.selected = this.runs.map(ALWAYS_FALSE_HANDLER) + this.runs.set(this.runs()?.filter(r => !ids.includes(r.id)) || []) + this.selected = this.runs()?.map(ALWAYS_FALSE_HANDLER) || [] } catch (e: any) { this.errorService.handleError(e) } finally { diff --git a/web/projects/ui/src/app/routes/portal/routes/system/backups/modals/jobs.component.ts b/web/projects/ui/src/app/routes/portal/routes/system/backups/modals/jobs.component.ts index 85b3d5a4c..7c7a5bb42 100644 --- a/web/projects/ui/src/app/routes/portal/routes/system/backups/modals/jobs.component.ts +++ b/web/projects/ui/src/app/routes/portal/routes/system/backups/modals/jobs.component.ts @@ -1,22 +1,19 @@ -import { CommonModule } from '@angular/common' import { Component, inject, OnInit } from '@angular/core' import { ErrorService, LoadingService } from '@start9labs/shared' -import { TuiForModule } from '@taiga-ui/cdk' import { TuiDialogOptions, TuiDialogService, TuiNotificationModule, - TuiSvgModule, } from '@taiga-ui/core' -import { TuiButtonModule, TuiFadeModule } from '@taiga-ui/experimental' +import { TuiButtonModule, TuiIconModule } from '@taiga-ui/experimental' import { TUI_PROMPT, TuiPromptData } from '@taiga-ui/kit' import { PolymorpheusComponent } from '@tinkoff/ng-polymorpheus' import { BehaviorSubject, filter } from 'rxjs' import { BackupJob } from 'src/app/services/api/api.types' import { ApiService } from 'src/app/services/api/embassy-api.service' -import { BackupJobBuilder } from '../utils/job-builder' -import { ToHumanCronPipe } from '../pipes/to-human-cron.pipe' import { GetBackupIconPipe } from '../pipes/get-backup-icon.pipe' +import { ToHumanCronPipe } from '../pipes/to-human-cron.pipe' +import { BackupJobBuilder } from '../utils/job-builder' import { EDIT } from './edit.component' @Component({ @@ -39,27 +36,27 @@ import { EDIT } from './edit.component' Create New Job -
- - +
+ + + + + + + + + + + @for (job of jobs; track $index) { - - - - - - - - - - - + - - - + + - - - - - - + } @empty { + @if (jobs) { - - -
NameTargetPackagesSchedule
NameTargetPackagesSchedule
{{ job.name }} - + {{ job.name }} + {{ job.target.name }} Packages: {{ job.packageIds.length }}{{ (job.cron | toHumanCron).message }} + Packages: {{ job.packageIds.length }}{{ (job.cron | toHumanCron).message }}
Loading
No jobs found.
-
+ } @else { + @for (i of ['', '']; track $index) { + +
Loading
+ + } + } + } + + + `, + styles: ` + tui-icon { + font-size: 1rem; + vertical-align: sub; + margin-inline-end: 0.25rem; + } + + :host-context(tui-root._mobile) { + tr { + grid-template-columns: 1fr 1fr; + } + + td:only-child { + grid-column: span 2; + } + + .title { + order: 1; + font-weight: bold; + text-transform: uppercase; + } + + .actions { + order: 2; + padding: 0; + text-align: right; + } + + .target { + order: 3; + } + + .packages { + order: 4; + text-align: right; + } + + .schedule { + order: 5; + color: var(--tui-text-02); + } + } `, standalone: true, imports: [ - CommonModule, - TuiForModule, TuiNotificationModule, TuiButtonModule, - TuiSvgModule, - TuiFadeModule, + TuiIconModule, ToHumanCronPipe, GetBackupIconPipe, ], diff --git a/web/projects/ui/src/app/routes/portal/routes/system/backups/modals/targets.component.ts b/web/projects/ui/src/app/routes/portal/routes/system/backups/modals/targets.component.ts index 0c7aa954d..f06b315a7 100644 --- a/web/projects/ui/src/app/routes/portal/routes/system/backups/modals/targets.component.ts +++ b/web/projects/ui/src/app/routes/portal/routes/system/backups/modals/targets.component.ts @@ -1,21 +1,11 @@ import { CommonModule } from '@angular/common' -import { Component, inject, OnInit } from '@angular/core' +import { Component, inject, OnInit, signal } from '@angular/core' import { ErrorService, LoadingService } from '@start9labs/shared' import { CT } from '@start9labs/start-sdk' import { TuiNotificationModule } from '@taiga-ui/core' -import { TuiButtonModule, TuiFadeModule } from '@taiga-ui/experimental' +import { TuiButtonModule } from '@taiga-ui/experimental' import { PolymorpheusComponent } from '@tinkoff/ng-polymorpheus' -import { BehaviorSubject } from 'rxjs' import { FormComponent } from 'src/app/routes/portal/components/form.component' -import { configBuilderToSpec } from 'src/app/utils/configBuilderToSpec' -import { - cifsSpec, - diskBackupTargetSpec, - dropboxSpec, - googleDriveSpec, - remoteBackupTargetSpec, -} from '../types/target' -import { FormDialogService } from 'src/app/services/form-dialog.service' import { BackupTarget, BackupTargetType, @@ -23,13 +13,21 @@ import { UnknownDisk, } from 'src/app/services/api/api.types' import { ApiService } from 'src/app/services/api/embassy-api.service' -import { BackupConfig } from '../types/backup-config' +import { FormDialogService } from 'src/app/services/form-dialog.service' +import { configBuilderToSpec } from 'src/app/utils/configBuilderToSpec' import { BackupsPhysicalComponent } from '../components/physical.component' import { BackupsTargetsComponent } from '../components/targets.component' +import { BackupConfig } from '../types/backup-config' +import { + cifsSpec, + diskBackupTargetSpec, + dropboxSpec, + googleDriveSpec, + remoteBackupTargetSpec, +} from '../types/target' @Component({ template: ` - Backup targets are physical or virtual locations for storing encrypted backups. They can be physical drives plugged into your server, shared @@ -45,31 +43,32 @@ import { BackupsTargetsComponent } from '../components/targets.component'

Unknown Physical Drives -

-
-
-
+

Saved Targets -

-
-
-
+
`, standalone: true, imports: [ @@ -78,7 +77,6 @@ import { BackupsTargetsComponent } from '../components/targets.component' TuiButtonModule, BackupsPhysicalComponent, BackupsTargetsComponent, - TuiFadeModule, ], }) export class BackupsTargetsModal implements OnInit { @@ -87,25 +85,20 @@ export class BackupsTargetsModal implements OnInit { private readonly formDialog = inject(FormDialogService) private readonly loader = inject(LoadingService) - readonly loading$ = new BehaviorSubject(true) - - targets?: RR.GetBackupTargetsRes + targets = signal(null) ngOnInit() { this.refresh() } async refresh() { - this.loading$.next(true) - this.targets = undefined + this.targets.set(null) try { - this.targets = await this.api.getBackupTargets({}) + this.targets.set(await this.api.getBackupTargets({})) } catch (e: any) { this.errorService.handleError(e) - this.targets = { unknownDisks: [], saved: [] } - } finally { - this.loading$.next(false) + this.targets.set({ unknownDisks: [], saved: [] }) } } @@ -114,7 +107,7 @@ export class BackupsTargetsModal implements OnInit { try { await this.api.removeBackupTarget({ id }) - this.setTargets(this.targets?.saved.filter(a => a.id !== id)) + this.setTargets(this.targets()?.saved.filter(a => a.id !== id)) } catch (e: any) { this.errorService.handleError(e) } finally { @@ -158,8 +151,8 @@ export class BackupsTargetsModal implements OnInit { ...value, }).then(response => { this.setTargets( - this.targets?.saved.concat(response), - this.targets?.unknownDisks.filter(a => a !== disk), + this.targets()?.saved.concat(response), + this.targets()?.unknownDisks.filter(a => a !== disk), ) return true }), @@ -221,10 +214,10 @@ export class BackupsTargetsModal implements OnInit { } private setTargets( - saved: BackupTarget[] = this.targets?.saved || [], - unknownDisks: UnknownDisk[] = this.targets?.unknownDisks || [], + saved: BackupTarget[] = this.targets()?.saved || [], + unknownDisks: UnknownDisk[] = this.targets()?.unknownDisks || [], ) { - this.targets = { unknownDisks, saved } + this.targets.set({ unknownDisks, saved }) } private async getSpec(target: BackupTarget) { diff --git a/web/projects/ui/src/app/routes/portal/routes/system/notifications/item.component.ts b/web/projects/ui/src/app/routes/portal/routes/system/notifications/item.component.ts index 11e7dea59..f5e7e01c9 100644 --- a/web/projects/ui/src/app/routes/portal/routes/system/notifications/item.component.ts +++ b/web/projects/ui/src/app/routes/portal/routes/system/notifications/item.component.ts @@ -8,7 +8,8 @@ import { import { RouterLink } from '@angular/router' import { T } from '@start9labs/start-sdk' import { tuiPure } from '@taiga-ui/cdk' -import { TuiLinkModule, TuiSvgModule } from '@taiga-ui/core' +import { TuiLinkModule } from '@taiga-ui/core' +import { TuiIconModule } from '@taiga-ui/experimental' import { TuiLineClampModule } from '@taiga-ui/kit' import { PatchDB } from 'patch-db-client' import { first, Observable } from 'rxjs' @@ -20,22 +21,24 @@ import { toRouterLink } from 'src/app/utils/to-router-link' @Component({ selector: '[notificationItem]', template: ` - - {{ notificationItem.createdAt | date: 'MMM d, y, h:mm a' }} - - + + + {{ notificationItem.createdAt | date: 'MMM d, y, h:mm a' }} + + + {{ notificationItem.title }} - - - {{ manifest.title }} - - N/A + + @if (manifest$ | async; as manifest) { + + {{ manifest.title }} + + } @else { + N/A + } - + - - + @if (overflow) { + + } + @if (notificationItem.code === 1) { + + } `, changeDetection: ChangeDetectionStrategy.OnPush, @@ -65,21 +64,50 @@ import { toRouterLink } from 'src/app/utils/to-router-link' '[class._new]': '!notificationItem.read', }, styles: ` - :host._new { - background: var(--tui-clear); + :host { + grid-template-columns: 1.75rem 1fr; + + &._new { + background: var(--tui-clear) !important; + } } td { + grid-column: span 2; padding: 0.25rem; vertical-align: top; } + + .checkbox { + grid-column: span 1; + padding-top: 0.4rem; + } + + :host-context(tui-root._mobile) { + .date { + grid-column: span 1; + color: var(--tui-text-02); + } + + .title { + font-weight: bold; + font-size: 1.2em; + display: flex; + align-items: center; + gap: 0.75rem; + } + + .service:not(:has(a)) { + display: none; + } + } `, imports: [ CommonModule, RouterLink, TuiLineClampModule, - TuiSvgModule, TuiLinkModule, + TuiIconModule, ], }) export class NotificationItemComponent { diff --git a/web/projects/ui/src/app/routes/portal/routes/system/settings/routes/domains/table.component.ts b/web/projects/ui/src/app/routes/portal/routes/system/settings/routes/domains/table.component.ts index 8e77f3a77..eee8258b9 100644 --- a/web/projects/ui/src/app/routes/portal/routes/system/settings/routes/domains/table.component.ts +++ b/web/projects/ui/src/app/routes/portal/routes/system/settings/routes/domains/table.component.ts @@ -25,36 +25,83 @@ import { Domain } from 'src/app/services/patch-db/data-model' - - {{ domain.value }} - {{ domain.createdAt | date: 'short' }} - {{ domain.provider }} - {{ getStrategy(domain) }} - - - N/A - - - - - + @for (domain of domains; track $index) { + + {{ domain.value }} + {{ domain.createdAt | date: 'short' }} + {{ domain.provider }} + {{ getStrategy(domain) }} + + @if (domain.usedBy.length; as qty) { + + } @else { + N/A + } + + + + + + } @empty { + No domains + } `, + styles: ` + :host-context(tui-root._mobile) { + tr { + grid-template-columns: 1fr 1fr; + } + + td:only-child { + grid-column: span 2; + } + + .title { + order: 1; + font-weight: bold; + } + + .actions { + order: 2; + padding: 0; + text-align: right; + } + + .provider { + order: 3; + } + + .strategy { + order: 4; + text-align: right; + } + + .date { + order: 5; + color: var(--tui-text-03); + } + + .used { + order: 6; + text-align: right; + + &:not(:has(button)) { + display: none; + } + } + } + `, standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, imports: [CommonModule, TuiButtonModule, TuiLinkModule], diff --git a/web/projects/ui/src/app/routes/portal/routes/system/settings/routes/proxies/menu.component.ts b/web/projects/ui/src/app/routes/portal/routes/system/settings/routes/proxies/menu.component.ts deleted file mode 100644 index 40b20e606..000000000 --- a/web/projects/ui/src/app/routes/portal/routes/system/settings/routes/proxies/menu.component.ts +++ /dev/null @@ -1,123 +0,0 @@ -import { CommonModule } from '@angular/common' -import { - ChangeDetectionStrategy, - Component, - inject, - Input, -} from '@angular/core' -import { ErrorService, LoadingService } from '@start9labs/shared' -import { TuiButtonModule } from '@taiga-ui/experimental' -import { - TuiDataListModule, - TuiDialogOptions, - TuiDialogService, - TuiDropdownModule, - TuiHostedDropdownModule, -} from '@taiga-ui/core' -import { TUI_PROMPT } from '@taiga-ui/kit' -import { filter } from 'rxjs' -import { - FormComponent, - FormContext, -} from 'src/app/routes/portal/components/form.component' -import { Proxy } from 'src/app/services/patch-db/data-model' -import { ApiService } from 'src/app/services/api/embassy-api.service' -import { FormDialogService } from 'src/app/services/form-dialog.service' -import { DELETE_OPTIONS, ProxyUpdate } from './constants' -import { CB } from '@start9labs/start-sdk' - -@Component({ - selector: 'proxies-menu', - template: ` - - - - - - - - - - - - `, - standalone: true, - changeDetection: ChangeDetectionStrategy.OnPush, - imports: [ - CommonModule, - TuiButtonModule, - TuiDataListModule, - TuiDropdownModule, - TuiHostedDropdownModule, - ], -}) -export class ProxiesMenuComponent { - private readonly dialogs = inject(TuiDialogService) - private readonly loader = inject(LoadingService) - private readonly errorService = inject(ErrorService) - private readonly api = inject(ApiService) - private readonly formDialog = inject(FormDialogService) - - @Input({ required: true }) proxy!: Proxy - - delete() { - this.dialogs - .open(TUI_PROMPT, DELETE_OPTIONS) - .pipe(filter(Boolean)) - .subscribe(async () => { - const loader = this.loader.open('Deleting...').subscribe() - - try { - await this.api.deleteProxy({ id: this.proxy.id }) - } catch (e: any) { - this.errorService.handleError(e) - } finally { - loader.unsubscribe() - } - }) - } - - async rename() { - const spec = { name: 'Name', required: { default: this.proxy.name } } - const name = await CB.Value.text(spec).build({} as any) - const options: Partial>> = { - label: `Rename ${this.proxy.name}`, - data: { - spec: { name }, - buttons: [ - { - text: 'Save', - handler: value => this.update(value), - }, - ], - }, - } - - this.formDialog.open(FormComponent, options) - } - - private async update(value: ProxyUpdate): Promise { - const loader = this.loader.open('Saving...').subscribe() - - try { - await this.api.updateProxy(value) - return true - } catch (e: any) { - this.errorService.handleError(e) - return false - } finally { - loader.unsubscribe() - } - } -} diff --git a/web/projects/ui/src/app/routes/portal/routes/system/settings/routes/proxies/proxies.component.ts b/web/projects/ui/src/app/routes/portal/routes/system/settings/routes/proxies/proxies.component.ts index 6d660de2a..fc8912bff 100644 --- a/web/projects/ui/src/app/routes/portal/routes/system/settings/routes/proxies/proxies.component.ts +++ b/web/projects/ui/src/app/routes/portal/routes/system/settings/routes/proxies/proxies.component.ts @@ -24,7 +24,7 @@ import { wireguardSpec, WireguardSpec } from './constants' Add Proxy -
+
`, changeDetection: ChangeDetectionStrategy.OnPush, standalone: true, diff --git a/web/projects/ui/src/app/routes/portal/routes/system/settings/routes/proxies/table.component.ts b/web/projects/ui/src/app/routes/portal/routes/system/settings/routes/proxies/table.component.ts index 418e8e71a..2c03a27a7 100644 --- a/web/projects/ui/src/app/routes/portal/routes/system/settings/routes/proxies/table.component.ts +++ b/web/projects/ui/src/app/routes/portal/routes/system/settings/routes/proxies/table.component.ts @@ -2,15 +2,31 @@ import { CommonModule } from '@angular/common' import { ChangeDetectionStrategy, Component, - EventEmitter, inject, Input, - Output, } from '@angular/core' -import { TuiDialogService, TuiLinkModule } from '@taiga-ui/core' -import { TuiBadgeModule, TuiButtonModule } from '@taiga-ui/experimental' +import { ErrorService, LoadingService } from '@start9labs/shared' +import { CB } from '@start9labs/start-sdk' +import { + TuiDataListModule, + TuiDialogOptions, + TuiDialogService, + TuiLinkModule, +} from '@taiga-ui/core' +import { TuiButtonModule, TuiIconsModule } from '@taiga-ui/experimental' +import { TUI_PROMPT } from '@taiga-ui/kit' +import { filter } from 'rxjs' +import { + FormComponent, + FormContext, +} from 'src/app/routes/portal/components/form.component' +import { + DELETE_OPTIONS, + ProxyUpdate, +} from 'src/app/routes/portal/routes/system/settings/routes/proxies/constants' +import { ApiService } from 'src/app/services/api/embassy-api.service' +import { FormDialogService } from 'src/app/services/form-dialog.service' import { Proxy } from 'src/app/services/patch-db/data-model' -import { ProxiesMenuComponent } from './menu.component' @Component({ selector: 'table[proxies]', @@ -21,46 +37,111 @@ import { ProxiesMenuComponent } from './menu.component' Created Type Used By - + - - {{ proxy.name }} - {{ proxy.createdAt | date: 'short' }} - {{ proxy.type }} - - - N/A - - - + @for (proxy of proxies; track $index) { + + {{ proxy.name }} + {{ proxy.createdAt | date: 'short' }} + {{ proxy.type }} + + @if (getLength(proxy); as length) { + + } @else { + N/A + } + + + + + + + } @empty { + @if (proxies) { + No proxies added + } @else { + +
Loading
+ + } + } `, + styles: ` + :host-context(tui-root._mobile) { + tr { + grid-template-columns: 1fr 1fr; + } + + td:only-child { + grid-column: span 2; + } + + .title { + order: 1; + font-weight: bold; + text-transform: uppercase; + } + + .actions { + order: 2; + padding: 0; + text-align: right; + } + + .date { + order: 3; + grid-column: span 2; + color: var(--tui-text-02); + } + + .type { + order: 4; + } + + .used { + order: 5; + text-align: right; + + &:not(:has(button)) { + display: none; + } + } + } + `, standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, - imports: [ - CommonModule, - TuiButtonModule, - TuiBadgeModule, - TuiLinkModule, - ProxiesMenuComponent, - ], + imports: [CommonModule, TuiLinkModule, TuiIconsModule, TuiButtonModule], }) export class ProxiesTableComponent { private readonly dialogs = inject(TuiDialogService) + private readonly loader = inject(LoadingService) + private readonly errorService = inject(ErrorService) + private readonly api = inject(ApiService) + private readonly formDialog = inject(FormDialogService) @Input() - proxies: readonly Proxy[] = [] - - @Output() - readonly delete = new EventEmitter() + proxies: readonly Proxy[] | null = null getLength({ usedBy }: Proxy) { return usedBy.domains.length + usedBy.services.length @@ -81,4 +162,54 @@ export class ProxiesTableComponent { this.dialogs.open(message, { label: 'Used by', size: 's' }).subscribe() } + + delete({ id }: Proxy) { + this.dialogs + .open(TUI_PROMPT, DELETE_OPTIONS) + .pipe(filter(Boolean)) + .subscribe(async () => { + const loader = this.loader.open('Deleting...').subscribe() + + try { + await this.api.deleteProxy({ id }) + } catch (e: any) { + this.errorService.handleError(e) + } finally { + loader.unsubscribe() + } + }) + } + + async rename(proxy: Proxy) { + const spec = { name: 'Name', required: { default: proxy.name } } + const name = await CB.Value.text(spec).build({} as any) + const options: Partial>> = { + label: `Rename ${proxy.name}`, + data: { + spec: { name }, + buttons: [ + { + text: 'Save', + handler: value => this.update(value), + }, + ], + }, + } + + this.formDialog.open(FormComponent, options) + } + + private async update(value: ProxyUpdate): Promise { + const loader = this.loader.open('Saving...').subscribe() + + try { + await this.api.updateProxy(value) + return true + } catch (e: any) { + this.errorService.handleError(e) + return false + } finally { + loader.unsubscribe() + } + } } diff --git a/web/projects/ui/src/app/routes/portal/routes/system/settings/routes/sessions/table.component.ts b/web/projects/ui/src/app/routes/portal/routes/system/settings/routes/sessions/table.component.ts index 418a88dfe..fd97af5af 100644 --- a/web/projects/ui/src/app/routes/portal/routes/system/settings/routes/sessions/table.component.ts +++ b/web/projects/ui/src/app/routes/portal/routes/system/settings/routes/sessions/table.component.ts @@ -5,17 +5,17 @@ import { Input, OnChanges, } from '@angular/core' +import { FormsModule } from '@angular/forms' import { TuiLinkModule } from '@taiga-ui/core' import { TuiButtonModule, TuiCheckboxModule, + TuiFadeModule, TuiIconModule, } from '@taiga-ui/experimental' import { BehaviorSubject } from 'rxjs' -import { TuiForModule } from '@taiga-ui/cdk' import { Session } from 'src/app/services/api/api.types' import { PlatformInfoPipe } from './platform-info.pipe' -import { FormsModule } from '@angular/forms' @Component({ selector: 'table[sessions]', @@ -23,15 +23,16 @@ import { FormsModule } from '@angular/forms' - + @if (!single) { + + } User Agent Platform @@ -39,31 +40,39 @@ import { FormsModule } from '@angular/forms' - - - - {{ session.userAgent }} - - - - {{ info.name }} - - {{ session.lastActive }} - - - - -
Loading
+ @for (session of sessions; track $index) { + + + @if (!single) { + + } + {{ session.userAgent }} + @if (session.metadata.platforms | platformInfo; as info) { + + + {{ info.name }} + + } + {{ session.lastActive | date: 'medium' }} -
+ } @empty { + @if (sessions) { + No sessions + } @else { + @for (item of single ? [''] : ['', '']; track $index) { + +
Loading
+ + } + } + } `, styles: [ @@ -71,22 +80,41 @@ import { FormsModule } from '@angular/forms' input { position: absolute; top: 50%; - left: 0.5rem; + left: 0.25rem; transform: translateY(-50%); } + + :host-context(tui-root._mobile) { + .agent { + white-space: nowrap; + display: block; + } + + .platform { + font-weight: bold; + display: flex; + align-items: center; + gap: 0.5rem; + padding: 0; + } + + .date { + color: var(--tui-text-02); + } + } `, ], standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, imports: [ CommonModule, - TuiForModule, + FormsModule, + PlatformInfoPipe, TuiButtonModule, TuiLinkModule, - PlatformInfoPipe, TuiIconModule, TuiCheckboxModule, - FormsModule, + TuiFadeModule, ], }) export class SSHTableComponent implements OnChanges { diff --git a/web/projects/ui/src/app/routes/portal/routes/system/settings/routes/ssh/table.component.ts b/web/projects/ui/src/app/routes/portal/routes/system/settings/routes/ssh/table.component.ts index 57faafa82..db0c30072 100644 --- a/web/projects/ui/src/app/routes/portal/routes/system/settings/routes/ssh/table.component.ts +++ b/web/projects/ui/src/app/routes/portal/routes/system/settings/routes/ssh/table.component.ts @@ -6,19 +6,14 @@ import { inject, Input, } from '@angular/core' -import { - TuiDialogOptions, - TuiDialogService, - TuiLinkModule, -} from '@taiga-ui/core' -import { TuiButtonModule } from '@taiga-ui/experimental' +import { ErrorService, LoadingService } from '@start9labs/shared' +import { TuiDialogOptions, TuiDialogService } from '@taiga-ui/core' +import { TuiButtonModule, TuiFadeModule } from '@taiga-ui/experimental' +import { TUI_PROMPT, TuiPromptData } from '@taiga-ui/kit' +import { filter, take } from 'rxjs' import { PROMPT } from 'src/app/routes/portal/modals/prompt.component' import { SSHKey } from 'src/app/services/api/api.types' -import { filter, take } from 'rxjs' -import { ErrorService, LoadingService } from '@start9labs/shared' import { ApiService } from 'src/app/services/api/embassy-api.service' -import { TUI_PROMPT, TuiPromptData } from '@taiga-ui/kit' -import { TuiForModule } from '@taiga-ui/cdk' @Component({ selector: 'table[keys]', @@ -33,34 +28,78 @@ import { TuiForModule } from '@taiga-ui/cdk' - - {{ key.hostname }} - {{ key.createdAt | date: 'medium' }} - {{ key.alg }} - {{ key.fingerprint }} - - - - - - -
Loading
+ @for (key of keys; track $index) { + + {{ key.hostname }} + {{ key.createdAt | date: 'medium' }} + {{ key.alg }} + {{ key.fingerprint }} + + + -
+ } @empty { + @if (keys) { + No keys added + } @else { + @for (i of ['', '']; track $index) { + +
Loading
+ + } + } + } `, + styles: ` + :host-context(tui-root._mobile) { + tr { + grid-template-columns: 2fr 1fr; + } + + td:only-child { + grid-column: span 2; + } + + .title { + order: 1; + font-weight: bold; + text-transform: uppercase; + } + + .actions { + order: 2; + padding: 0; + text-align: right; + } + + .date { + order: 3; + } + + .algorithm { + order: 4; + text-align: right; + } + + .fingerprint { + order: 5; + grid-column: span 2; + color: var(--tui-text-02); + } + } + `, standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, - imports: [CommonModule, TuiForModule, TuiButtonModule, TuiLinkModule], + imports: [CommonModule, TuiButtonModule, TuiFadeModule], }) export class SSHTableComponent { private readonly loader = inject(LoadingService) diff --git a/web/projects/ui/src/styles.scss b/web/projects/ui/src/styles.scss index 98e9d0eef..d9c237e05 100644 --- a/web/projects/ui/src/styles.scss +++ b/web/projects/ui/src/styles.scss @@ -89,6 +89,40 @@ hr { } } +tui-root._mobile .g-table { + min-width: 0; + + thead { + display: none; + } + + tbody { + display: flex; + flex-direction: column; + gap: 1rem; + } + + tr { + display: grid; + border-radius: var(--tui-radius-l); + padding: 0.75rem 1rem; + // TODO: Theme + background: rgba(0, 0, 0, 0.2); + } + + td, + th { + height: auto; + min-height: 2rem; + align-content: center; + box-shadow: none; + + &:not([tuiFade]) { + overflow: hidden; + } + } +} + .g-title { display: flex; align-items: center; @@ -127,6 +161,8 @@ hr { a.g-action, button.g-action { + cursor: pointer; + &:disabled { pointer-events: none; opacity: var(--tui-disabled-opacity); From d85e621bb364629eb33be55a51a5fe08c937c476 Mon Sep 17 00:00:00 2001 From: waterplea Date: Fri, 5 Jul 2024 18:23:18 +0500 Subject: [PATCH 2/6] chore: address comments --- patch-db | 2 +- .../backups/components/upcoming.component.ts | 8 ++- .../backups/modals/history.component.ts | 59 +++++++++++++++---- .../system/notifications/item.component.ts | 18 ++++-- .../system/notifications/table.component.ts | 8 +++ .../routes/domains/table.component.ts | 2 +- .../routes/proxies/table.component.ts | 8 +-- .../routes/sessions/table.component.ts | 13 ++++ .../settings/routes/ssh/table.component.ts | 15 ++--- web/projects/ui/src/styles.scss | 6 ++ 10 files changed, 107 insertions(+), 32 deletions(-) diff --git a/patch-db b/patch-db index 3dc11afd4..7aa53249f 160000 --- a/patch-db +++ b/patch-db @@ -1 +1 @@ -Subproject commit 3dc11afd46d93094ac52ae1fef311a91c4561e8c +Subproject commit 7aa53249f9353162475ea347abac92abcfba5493 diff --git a/web/projects/ui/src/app/routes/portal/routes/system/backups/components/upcoming.component.ts b/web/projects/ui/src/app/routes/portal/routes/system/backups/components/upcoming.component.ts index cfe2b0630..4a56e25af 100644 --- a/web/projects/ui/src/app/routes/portal/routes/system/backups/components/upcoming.component.ts +++ b/web/projects/ui/src/app/routes/portal/routes/system/backups/components/upcoming.component.ts @@ -28,7 +28,7 @@ import { GetBackupIconPipe } from '../pipes/get-backup-icon.pipe' @if (current.id === job.id) { Running } @else { - {{ job.next | date: 'MMM d, y, h:mm a' }} + {{ job.next | date: 'medium' }} } {{ job.name }} @@ -59,7 +59,11 @@ import { GetBackupIconPipe } from '../pipes/get-backup-icon.pipe' grid-template-columns: 1fr 1fr; } - .date, + .date { + order: 1; + grid-column: span 2; + } + .name { grid-column: span 2; } diff --git a/web/projects/ui/src/app/routes/portal/routes/system/backups/modals/history.component.ts b/web/projects/ui/src/app/routes/portal/routes/system/backups/modals/history.component.ts index d12f19eb8..3a51b32ca 100644 --- a/web/projects/ui/src/app/routes/portal/routes/system/backups/modals/history.component.ts +++ b/web/projects/ui/src/app/routes/portal/routes/system/backups/modals/history.component.ts @@ -9,10 +9,12 @@ import { FormsModule } from '@angular/forms' import { ErrorService, LoadingService } from '@start9labs/shared' import { ALWAYS_FALSE_HANDLER, ALWAYS_TRUE_HANDLER } from '@taiga-ui/cdk' import { TuiDialogService, TuiLinkModule } from '@taiga-ui/core' -import { TuiButtonModule, TuiIconModule } from '@taiga-ui/experimental' -import { TuiCheckboxModule } from '@taiga-ui/kit' +import { + TuiButtonModule, + TuiCheckboxModule, + TuiIconModule, +} from '@taiga-ui/experimental' import { PolymorpheusComponent } from '@tinkoff/ng-polymorpheus' -import { BehaviorSubject } from 'rxjs' import { REPORT } from 'src/app/components/report.component' import { BackupRun } from 'src/app/services/api/api.types' import { ApiService } from 'src/app/services/api/embassy-api.service' @@ -37,7 +39,10 @@ import { HasErrorPipe } from '../pipes/has-error.pipe' - @for (run of runs(); track $index) { - - - {{ run.startedAt | date: 'medium' }} + + + + + {{ run.startedAt | date: 'medium' }} {{ run.startedAt | duration: run.completedAt }} minutes @@ -67,7 +79,7 @@ import { HasErrorPipe } from '../pipes/has-error.pipe' } - + {{ run.job.target.name }} @@ -87,27 +99,50 @@ import { HasErrorPipe } from '../pipes/has-error.pipe' `, styles: ` + @import '@taiga-ui/core/styles/taiga-ui-local'; + tui-icon { font-size: 1rem; vertical-align: sub; margin-inline-end: 0.25rem; } + button { + position: relative; + } + + [tuiCheckbox] { + display: block; + } + :host-context(tui-root._mobile) { tr { - grid-template-columns: 1.75rem 1fr 7rem; + grid-template-columns: 1fr 7rem; } td:only-child { - grid-column: span 3; + grid-column: span 2; + } + + .checkbox { + @include fullsize(); + + [tuiCheckbox] { + @include fullsize(); + opacity: 0; + } } .title { - grid-column: span 2; font-weight: bold; text-transform: uppercase; } + .date, + .duration { + color: var(--tui-text-02); + } + .duration, .result { text-align: right; @@ -120,12 +155,12 @@ import { HasErrorPipe } from '../pipes/has-error.pipe' CommonModule, FormsModule, TuiButtonModule, - TuiCheckboxModule, TuiIconModule, TuiLinkModule, DurationPipe, HasErrorPipe, GetBackupIconPipe, + TuiCheckboxModule, ], }) export class BackupsHistoryModal { diff --git a/web/projects/ui/src/app/routes/portal/routes/system/notifications/item.component.ts b/web/projects/ui/src/app/routes/portal/routes/system/notifications/item.component.ts index f5e7e01c9..3d03b0e57 100644 --- a/web/projects/ui/src/app/routes/portal/routes/system/notifications/item.component.ts +++ b/web/projects/ui/src/app/routes/portal/routes/system/notifications/item.component.ts @@ -23,7 +23,7 @@ import { toRouterLink } from 'src/app/utils/to-router-link' template: ` - {{ notificationItem.createdAt | date: 'MMM d, y, h:mm a' }} + {{ notificationItem.createdAt | date: 'medium' }} @@ -64,28 +64,36 @@ import { toRouterLink } from 'src/app/utils/to-router-link' '[class._new]': '!notificationItem.read', }, styles: ` + @import '@taiga-ui/core/styles/taiga-ui-local'; + :host { - grid-template-columns: 1.75rem 1fr; + grid-template-columns: 1fr; &._new { background: var(--tui-clear) !important; } } + button { + position: relative; + } + td { - grid-column: span 2; padding: 0.25rem; vertical-align: top; } .checkbox { - grid-column: span 1; padding-top: 0.4rem; } :host-context(tui-root._mobile) { + .checkbox { + @include fullsize(); + } + .date { - grid-column: span 1; + order: 1; color: var(--tui-text-02); } diff --git a/web/projects/ui/src/app/routes/portal/routes/system/notifications/table.component.ts b/web/projects/ui/src/app/routes/portal/routes/system/notifications/table.component.ts index e4c298f4e..c6b56b197 100644 --- a/web/projects/ui/src/app/routes/portal/routes/system/notifications/table.component.ts +++ b/web/projects/ui/src/app/routes/portal/routes/system/notifications/table.component.ts @@ -66,6 +66,14 @@ import { NotificationItemComponent } from './item.component' } `, + styles: ` + @import '@taiga-ui/core/styles/taiga-ui-local'; + + :host-context(tui-root._mobile) input { + @include fullsize(); + opacity: 0; + } + `, changeDetection: ChangeDetectionStrategy.OnPush, standalone: true, imports: [ diff --git a/web/projects/ui/src/app/routes/portal/routes/system/settings/routes/domains/table.component.ts b/web/projects/ui/src/app/routes/portal/routes/system/settings/routes/domains/table.component.ts index eee8258b9..c7be3c518 100644 --- a/web/projects/ui/src/app/routes/portal/routes/system/settings/routes/domains/table.component.ts +++ b/web/projects/ui/src/app/routes/portal/routes/system/settings/routes/domains/table.component.ts @@ -28,7 +28,7 @@ import { Domain } from 'src/app/services/patch-db/data-model' @for (domain of domains; track $index) { {{ domain.value }} - {{ domain.createdAt | date: 'short' }} + {{ domain.createdAt | date: 'medium' }} {{ domain.provider }} {{ getStrategy(domain) }} diff --git a/web/projects/ui/src/app/routes/portal/routes/system/settings/routes/proxies/table.component.ts b/web/projects/ui/src/app/routes/portal/routes/system/settings/routes/proxies/table.component.ts index 2c03a27a7..8f5166e38 100644 --- a/web/projects/ui/src/app/routes/portal/routes/system/settings/routes/proxies/table.component.ts +++ b/web/projects/ui/src/app/routes/portal/routes/system/settings/routes/proxies/table.component.ts @@ -44,7 +44,7 @@ import { Proxy } from 'src/app/services/patch-db/data-model' @for (proxy of proxies; track $index) { {{ proxy.name }} - {{ proxy.createdAt | date: 'short' }} + {{ proxy.createdAt | date: 'medium' }} {{ proxy.type }} @if (getLength(proxy); as length) { @@ -110,17 +110,17 @@ import { Proxy } from 'src/app/services/patch-db/data-model' } .date { - order: 3; + order: 5; grid-column: span 2; color: var(--tui-text-02); } .type { - order: 4; + order: 3; } .used { - order: 5; + order: 4; text-align: right; &:not(:has(button)) { diff --git a/web/projects/ui/src/app/routes/portal/routes/system/settings/routes/sessions/table.component.ts b/web/projects/ui/src/app/routes/portal/routes/system/settings/routes/sessions/table.component.ts index fd97af5af..e6b085877 100644 --- a/web/projects/ui/src/app/routes/portal/routes/system/settings/routes/sessions/table.component.ts +++ b/web/projects/ui/src/app/routes/portal/routes/system/settings/routes/sessions/table.component.ts @@ -77,6 +77,8 @@ import { PlatformInfoPipe } from './platform-info.pipe' `, styles: [ ` + @import '@taiga-ui/core/styles/taiga-ui-local'; + input { position: absolute; top: 50%; @@ -85,6 +87,17 @@ import { PlatformInfoPipe } from './platform-info.pipe' } :host-context(tui-root._mobile) { + input { + @include fullsize(); + z-index: 1; + opacity: 0; + transform: none; + } + + td:first-child { + padding: 0 0.25rem !important; + } + .agent { white-space: nowrap; display: block; diff --git a/web/projects/ui/src/app/routes/portal/routes/system/settings/routes/ssh/table.component.ts b/web/projects/ui/src/app/routes/portal/routes/system/settings/routes/ssh/table.component.ts index db0c30072..ef131258f 100644 --- a/web/projects/ui/src/app/routes/portal/routes/system/settings/routes/ssh/table.component.ts +++ b/web/projects/ui/src/app/routes/portal/routes/system/settings/routes/ssh/table.component.ts @@ -81,18 +81,19 @@ import { ApiService } from 'src/app/services/api/embassy-api.service' text-align: right; } - .date { + .fingerprint { order: 3; + grid-column: span 2; + } + + .date { + order: 4; + color: var(--tui-text-02); } .algorithm { - order: 4; - text-align: right; - } - - .fingerprint { order: 5; - grid-column: span 2; + text-align: right; color: var(--tui-text-02); } } diff --git a/web/projects/ui/src/styles.scss b/web/projects/ui/src/styles.scss index d9c237e05..cff302e26 100644 --- a/web/projects/ui/src/styles.scss +++ b/web/projects/ui/src/styles.scss @@ -103,6 +103,7 @@ tui-root._mobile .g-table { } tr { + position: relative; display: grid; border-radius: var(--tui-radius-l); padding: 0.75rem 1rem; @@ -110,8 +111,13 @@ tui-root._mobile .g-table { background: rgba(0, 0, 0, 0.2); } + tr:has(:checked) { + box-shadow: inset 0 0 0 0.125rem var(--tui-primary); + } + td, th { + position: static; height: auto; min-height: 2rem; align-content: center; From fd7c7ea6b73f86235d048fe214e00aa8cd76b716 Mon Sep 17 00:00:00 2001 From: waterplea Date: Fri, 5 Jul 2024 18:27:40 +0500 Subject: [PATCH 3/6] chore: compact cards --- web/projects/ui/src/styles.scss | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/web/projects/ui/src/styles.scss b/web/projects/ui/src/styles.scss index cff302e26..60bf54d4d 100644 --- a/web/projects/ui/src/styles.scss +++ b/web/projects/ui/src/styles.scss @@ -99,14 +99,14 @@ tui-root._mobile .g-table { tbody { display: flex; flex-direction: column; - gap: 1rem; + gap: 0.5rem; } tr { position: relative; display: grid; border-radius: var(--tui-radius-l); - padding: 0.75rem 1rem; + padding: 0.375rem 0.5rem; // TODO: Theme background: rgba(0, 0, 0, 0.2); } @@ -119,7 +119,7 @@ tui-root._mobile .g-table { th { position: static; height: auto; - min-height: 2rem; + min-height: 1.5rem; align-content: center; box-shadow: none; From 6aba166c823b99c5dfb807ead19a2462f722a5aa Mon Sep 17 00:00:00 2001 From: waterplea Date: Tue, 9 Jul 2024 13:45:04 +0500 Subject: [PATCH 4/6] chore: address comments --- .../backups/modals/history.component.ts | 1 + .../settings/routes/ssh/table.component.ts | 8 +++-- web/projects/ui/src/index.html | 34 +++++++++++++++++-- web/projects/ui/src/styles.scss | 1 + 4 files changed, 40 insertions(+), 4 deletions(-) diff --git a/web/projects/ui/src/app/routes/portal/routes/system/backups/modals/history.component.ts b/web/projects/ui/src/app/routes/portal/routes/system/backups/modals/history.component.ts index 3a51b32ca..3a4e01c90 100644 --- a/web/projects/ui/src/app/routes/portal/routes/system/backups/modals/history.component.ts +++ b/web/projects/ui/src/app/routes/portal/routes/system/backups/modals/history.component.ts @@ -140,6 +140,7 @@ import { HasErrorPipe } from '../pipes/has-error.pipe' .date, .duration { + order: 1; color: var(--tui-text-02); } diff --git a/web/projects/ui/src/app/routes/portal/routes/system/settings/routes/ssh/table.component.ts b/web/projects/ui/src/app/routes/portal/routes/system/settings/routes/ssh/table.component.ts index ef131258f..45475981d 100644 --- a/web/projects/ui/src/app/routes/portal/routes/system/settings/routes/ssh/table.component.ts +++ b/web/projects/ui/src/app/routes/portal/routes/system/settings/routes/ssh/table.component.ts @@ -62,7 +62,7 @@ import { ApiService } from 'src/app/services/api/embassy-api.service' styles: ` :host-context(tui-root._mobile) { tr { - grid-template-columns: 2fr 1fr; + grid-template-columns: 3fr 2fr; } td:only-child { @@ -94,7 +94,11 @@ import { ApiService } from 'src/app/services/api/embassy-api.service' .algorithm { order: 5; text-align: right; - color: var(--tui-text-02); + + &::before { + content: 'Algorithm: '; + color: var(--tui-text-02); + } } } `, diff --git a/web/projects/ui/src/index.html b/web/projects/ui/src/index.html index 0248b7462..dd037db64 100644 --- a/web/projects/ui/src/index.html +++ b/web/projects/ui/src/index.html @@ -1,4 +1,4 @@ - + @@ -21,10 +21,40 @@ /> + - + + Start OS +

Loading

+ +
diff --git a/web/projects/ui/src/styles.scss b/web/projects/ui/src/styles.scss index 60bf54d4d..61c06c070 100644 --- a/web/projects/ui/src/styles.scss +++ b/web/projects/ui/src/styles.scss @@ -42,6 +42,7 @@ hr { tui-root._mobile & { // For tui-tab-bar height: calc(100vh - 3.875rem - var(--tui-height-l)); + padding: 1rem; } } From 956c8a8e03d565659dcd93d771fdada36026a374 Mon Sep 17 00:00:00 2001 From: waterplea Date: Wed, 10 Jul 2024 10:35:21 +0500 Subject: [PATCH 5/6] chore: more comments --- index.html | 2 +- web/projects/shared/styles/shared.scss | 4 +-- web/projects/shared/styles/taiga.scss | 4 +-- .../form/form-array/form-array.component.scss | 2 +- .../portal/routes/dashboard/ui.component.ts | 2 +- .../backups/components/targets.component.ts | 32 ++++++++++-------- .../routes/system/metrics/cpu.component.ts | 4 +-- .../system/metrics/metrics.component.ts | 2 +- .../routes/domains/table.component.ts | 33 ++++++++++--------- .../routes/proxies/table.component.ts | 10 +----- web/projects/ui/src/styles.scss | 1 + 11 files changed, 48 insertions(+), 48 deletions(-) diff --git a/index.html b/index.html index aa68e3a04..2163e5527 100644 --- a/index.html +++ b/index.html @@ -16,7 +16,7 @@ .header-title span { position: relative; } - .header-title span:before { + .header-title span::before { content: ""; position: absolute; top: 100%; diff --git a/web/projects/shared/styles/shared.scss b/web/projects/shared/styles/shared.scss index 4d66c1dff..1d38a6f02 100644 --- a/web/projects/shared/styles/shared.scss +++ b/web/projects/shared/styles/shared.scss @@ -214,7 +214,7 @@ body { user-select: text; } -.loading-dots:after { +.loading-dots::after { content: '...'; overflow: hidden; display: inline-block; @@ -303,4 +303,4 @@ h5, h6, hr { margin: 0; -} \ No newline at end of file +} diff --git a/web/projects/shared/styles/taiga.scss b/web/projects/shared/styles/taiga.scss index cb7087b33..ed33a88ac 100644 --- a/web/projects/shared/styles/taiga.scss +++ b/web/projects/shared/styles/taiga.scss @@ -140,7 +140,7 @@ background: transparent !important; } - tui-root._mobile &:after { + tui-root._mobile &::after { display: none; } } @@ -149,7 +149,7 @@ tui-dialog { transform: translate3d(0, 0, 0); } -tui-opt-group[data-label^='⚠️']:before { +tui-opt-group[data-label^='⚠️']::before { color: var(--tui-warning-fill); } diff --git a/web/projects/ui/src/app/routes/portal/components/form/form-array/form-array.component.scss b/web/projects/ui/src/app/routes/portal/components/form/form-array/form-array.component.scss index 9b6415ff7..95b5185cc 100644 --- a/web/projects/ui/src/app/routes/portal/components/form/form-array/form-array.component.scss +++ b/web/projects/ui/src/app/routes/portal/components/form/form-array/form-array.component.scss @@ -26,7 +26,7 @@ opacity: 0; } - &:after { + &::after { @include transition(opacity); content: ''; diff --git a/web/projects/ui/src/app/routes/portal/routes/dashboard/ui.component.ts b/web/projects/ui/src/app/routes/portal/routes/dashboard/ui.component.ts index 0bdd31da2..490856eba 100644 --- a/web/projects/ui/src/app/routes/portal/routes/dashboard/ui.component.ts +++ b/web/projects/ui/src/app/routes/portal/routes/dashboard/ui.component.ts @@ -52,7 +52,7 @@ import { PackageDataEntry } from 'src/app/services/patch-db/data-model' } `, styles: ` - :host-context(tui-root._mobile) *:before { + :host-context(tui-root._mobile) *::before { font-size: 1.5rem !important; } `, diff --git a/web/projects/ui/src/app/routes/portal/routes/system/backups/components/targets.component.ts b/web/projects/ui/src/app/routes/portal/routes/system/backups/components/targets.component.ts index 4b34588f6..1c51e0cf5 100644 --- a/web/projects/ui/src/app/routes/portal/routes/system/backups/components/targets.component.ts +++ b/web/projects/ui/src/app/routes/portal/routes/system/backups/components/targets.component.ts @@ -90,36 +90,40 @@ import { GetBackupIconPipe } from '../pipes/get-backup-icon.pipe' grid-column: span 3; } - .title { + .type { + order: 1; + text-transform: capitalize; + color: var(--tui-text-02); + grid-column: span 3; + + tui-icon { + display: none; + } + } + + .available { order: 2; + } + + .title { + order: 3; font-weight: bold; text-transform: uppercase; } - .available { - order: 1; - } - .actions { - order: 3; + order: 4; padding: 0; text-align: right; } .path { - order: 4; + order: 5; color: var(--tui-text-03); grid-column: span 2; overflow: hidden; text-overflow: ellipsis; } - - .type { - order: 5; - text-align: right; - text-transform: capitalize; - color: var(--tui-text-02); - } } `, changeDetection: ChangeDetectionStrategy.OnPush, diff --git a/web/projects/ui/src/app/routes/portal/routes/system/metrics/cpu.component.ts b/web/projects/ui/src/app/routes/portal/routes/system/metrics/cpu.component.ts index 0b3ef27ae..6ed092ace 100644 --- a/web/projects/ui/src/app/routes/portal/routes/system/metrics/cpu.component.ts +++ b/web/projects/ui/src/app/routes/portal/routes/system/metrics/cpu.component.ts @@ -45,7 +45,7 @@ import { ChangeDetectionStrategy, Component, Input } from '@angular/core' top: 50%; left: 50%; - &:before { + &::before { content: ''; position: absolute; inset: -0.5rem; @@ -53,7 +53,7 @@ import { ChangeDetectionStrategy, Component, Input } from '@angular/core' border-radius: 100%; } - &:after { + &::after { content: ''; position: absolute; width: 0.25rem; diff --git a/web/projects/ui/src/app/routes/portal/routes/system/metrics/metrics.component.ts b/web/projects/ui/src/app/routes/portal/routes/system/metrics/metrics.component.ts index 95c4d8de0..7fd01f420 100644 --- a/web/projects/ui/src/app/routes/portal/routes/system/metrics/metrics.component.ts +++ b/web/projects/ui/src/app/routes/portal/routes/system/metrics/metrics.component.ts @@ -131,7 +131,7 @@ import { TimeService } from 'src/app/services/time.service' color: var(--tui-text-01); padding-top: 0.4rem; - &:after { + &::after { content: attr(data-unit); font-size: 0.5rem; font-weight: normal; diff --git a/web/projects/ui/src/app/routes/portal/routes/system/settings/routes/domains/table.component.ts b/web/projects/ui/src/app/routes/portal/routes/system/settings/routes/domains/table.component.ts index c7be3c518..c3ad80b6a 100644 --- a/web/projects/ui/src/app/routes/portal/routes/system/settings/routes/domains/table.component.ts +++ b/web/projects/ui/src/app/routes/portal/routes/system/settings/routes/domains/table.component.ts @@ -17,7 +17,6 @@ import { Domain } from 'src/app/services/patch-db/data-model' Domain - Added DDNS Provider Network Strategy Used By @@ -28,13 +27,12 @@ import { Domain } from 'src/app/services/patch-db/data-model' @for (domain of domains; track $index) { {{ domain.value }} - {{ domain.createdAt | date: 'medium' }} {{ domain.provider }} {{ getStrategy(domain) }} - @if (domain.usedBy.length; as qty) { + @if (domain.usedBy.length + 1; as qty) { } @else { N/A @@ -60,7 +58,7 @@ import { Domain } from 'src/app/services/patch-db/data-model' styles: ` :host-context(tui-root._mobile) { tr { - grid-template-columns: 1fr 1fr; + grid-template-columns: 2fr 1fr; } td:only-child { @@ -78,22 +76,27 @@ import { Domain } from 'src/app/services/patch-db/data-model' text-align: right; } - .provider { - order: 3; - } - .strategy { - order: 4; - text-align: right; + order: 3; + grid-column: span 2; + + &::before { + content: 'Strategy: '; + color: var(--tui-text-02); + } } - .date { - order: 5; - color: var(--tui-text-03); + .provider { + order: 4; + + &::before { + content: 'DDNS: '; + color: var(--tui-text-02); + } } .used { - order: 6; + order: 5; text-align: right; &:not(:has(button)) { diff --git a/web/projects/ui/src/app/routes/portal/routes/system/settings/routes/proxies/table.component.ts b/web/projects/ui/src/app/routes/portal/routes/system/settings/routes/proxies/table.component.ts index 8f5166e38..9d67be0fc 100644 --- a/web/projects/ui/src/app/routes/portal/routes/system/settings/routes/proxies/table.component.ts +++ b/web/projects/ui/src/app/routes/portal/routes/system/settings/routes/proxies/table.component.ts @@ -34,7 +34,6 @@ import { Proxy } from 'src/app/services/patch-db/data-model' Name - Created Type Used By @@ -44,12 +43,11 @@ import { Proxy } from 'src/app/services/patch-db/data-model' @for (proxy of proxies; track $index) { {{ proxy.name }} - {{ proxy.createdAt | date: 'medium' }} {{ proxy.type }} @if (getLength(proxy); as length) { } @else { N/A @@ -109,12 +107,6 @@ import { Proxy } from 'src/app/services/patch-db/data-model' text-align: right; } - .date { - order: 5; - grid-column: span 2; - color: var(--tui-text-02); - } - .type { order: 3; } diff --git a/web/projects/ui/src/styles.scss b/web/projects/ui/src/styles.scss index 61c06c070..a121c8d45 100644 --- a/web/projects/ui/src/styles.scss +++ b/web/projects/ui/src/styles.scss @@ -78,6 +78,7 @@ hr { height: 2rem; padding: 0 0.25rem; box-shadow: inset 0 -1px var(--tui-clear); + text-overflow: ellipsis; } th { From 398eb13a7f14b1c802524335abcb3f893b1e7829 Mon Sep 17 00:00:00 2001 From: waterplea Date: Wed, 10 Jul 2024 16:01:57 +0500 Subject: [PATCH 6/6] chore: remove test --- .../routes/system/settings/routes/domains/table.component.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/projects/ui/src/app/routes/portal/routes/system/settings/routes/domains/table.component.ts b/web/projects/ui/src/app/routes/portal/routes/system/settings/routes/domains/table.component.ts index c3ad80b6a..c6a0a70d9 100644 --- a/web/projects/ui/src/app/routes/portal/routes/system/settings/routes/domains/table.component.ts +++ b/web/projects/ui/src/app/routes/portal/routes/system/settings/routes/domains/table.component.ts @@ -30,7 +30,7 @@ import { Domain } from 'src/app/services/patch-db/data-model' {{ domain.provider }} {{ getStrategy(domain) }} - @if (domain.usedBy.length + 1; as qty) { + @if (domain.usedBy.length; as qty) {