Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

NAS-132774 / 25.04 / Refactor some inputs to signals #11112

Open
wants to merge 7 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,6 @@
[disabled]="isLoading"
(click)="onExport()"
>
{{ 'Export As {fileType}' | translate:{ fileType: fileType.toUpperCase()} }}
{{ 'Export As {fileType}' | translate:{ fileType: fileType().toUpperCase()} }}
</button>
</div>
41 changes: 21 additions & 20 deletions src/app/modules/buttons/export-button/export-button.component.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import {
ChangeDetectionStrategy, ChangeDetectorRef, Component, Input,
ChangeDetectionStrategy, ChangeDetectorRef, Component, input,
} from '@angular/core';
import { toSignal } from '@angular/core/rxjs-interop';
import { MatButton } from '@angular/material/button';
Expand Down Expand Up @@ -41,16 +41,17 @@ import { selectIsHaLicensed } from 'app/store/ha-info/ha-info.selectors';
],
})
export class ExportButtonComponent<T, M extends ApiJobMethod> {
@Input() jobMethod: M;
@Input() searchQuery: SearchQuery<T>;
@Input() defaultFilters: QueryFilters<T>;
@Input() sorting: TableSort<T>;
@Input() filename = 'data';
@Input() fileType = 'csv';
@Input() fileMimeType = 'text/csv';
@Input() addReportNameArgument = false;
@Input() controllerType: ControllerType;
@Input() downloadMethod?: keyof ApiCallDirectory;
readonly jobMethod = input.required<M>();
readonly searchQuery = input<SearchQuery<T>>();
readonly defaultFilters = input<QueryFilters<T>>();
readonly sorting = input<TableSort<T>>();
readonly filename = input('data');
readonly fileType = input('csv');
readonly fileMimeType = input('text/csv');
readonly addReportNameArgument = input(false);
// TODO: Does not belong to generic export button component.
readonly controllerType = input<ControllerType>();
readonly downloadMethod = input<keyof ApiCallDirectory>();

isLoading = false;

Expand All @@ -67,9 +68,9 @@ export class ExportButtonComponent<T, M extends ApiJobMethod> {

onExport(): void {
this.isLoading = true;
this.api.job(this.jobMethod, this.getExportParams(
this.getQueryFilters(this.searchQuery),
this.getQueryOptions(this.sorting),
this.api.job(this.jobMethod(), this.getExportParams(
this.getQueryFilters(this.searchQuery()),
this.getQueryOptions(this.sorting()),
)).pipe(
switchMap((job) => {
this.cdr.markForCheck();
Expand All @@ -83,15 +84,15 @@ export class ExportButtonComponent<T, M extends ApiJobMethod> {

const url = job.result as string;
const customArguments = {} as { report_name?: string };
const downloadMethod = this.downloadMethod || this.jobMethod;
const downloadMethod = this.downloadMethod() || this.jobMethod();

if (this.addReportNameArgument) {
if (this.addReportNameArgument()) {
customArguments.report_name = url;
}

return this.api.call('core.download', [downloadMethod, [customArguments], url]);
}),
switchMap(([, url]) => this.download.downloadUrl(url, `${this.filename}.${this.fileType}`, this.fileMimeType)),
switchMap(([, url]) => this.download.downloadUrl(url, `${this.filename()}.${this.fileType()}`, this.fileMimeType())),
catchError((error) => {
this.isLoading = false;
this.cdr.markForCheck();
Expand All @@ -113,15 +114,15 @@ export class ExportButtonComponent<T, M extends ApiJobMethod> {
'query-filters': queryFilters,
'query-options': queryOptions,
export_format: ExportFormat.Csv,
...(this.isHaLicensed() && this.controllerType && {
remote_controller: this.controllerType === ControllerType.Standby,
...(this.isHaLicensed() && this.controllerType() && {
remote_controller: this.controllerType() === ControllerType.Standby,
}),
}] as ApiJobParams<M>;
}

private getQueryFilters(searchQuery: SearchQuery<T>): QueryFilters<T> {
if (searchQuery) {
return (searchQuery as AdvancedSearchQuery<T>)?.filters || this.defaultFilters;
return (searchQuery as AdvancedSearchQuery<T>)?.filters || this.defaultFilters();
}

return [];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@
mat-button
type="button"
class="oauth-button"
[ixTest]="testId"
[disabled]="disabled"
[color]="isLoggedIn ? 'accent' : 'primary'"
[ixTest]="testId()"
[disabled]="disabled()"
[color]="isLoggedIn() ? 'accent' : 'primary'"
(click)="onOauthClicked()"
>
{{ buttonText | translate }}
Expand Down
25 changes: 13 additions & 12 deletions src/app/modules/buttons/oauth-button/oauth-button.component.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import {
ChangeDetectionStrategy, ChangeDetectorRef, Component, Inject, Input, OnDestroy, output,
ChangeDetectionStrategy, ChangeDetectorRef, Component, Inject, input, OnDestroy, output,
} from '@angular/core';
import { MatButton } from '@angular/material/button';
import { TranslateService, TranslateModule } from '@ngx-translate/core';
Expand All @@ -25,11 +25,12 @@ import { OauthProviderData } from 'app/pages/credentials/backup-credentials/clou
],
})
export class OauthButtonComponent implements OnDestroy {
@Input() oauthType: OauthButtonType;
@Input() isLoggedIn = false;
@Input() disabled = false;
@Input() oauthUrl: string;
@Input() testId: string;
readonly oauthType = input<OauthButtonType>();
readonly isLoggedIn = input(false);
readonly disabled = input(false);
readonly oauthUrl = input<string>();
// TODO: Figure out in another way.
readonly testId = input<string>();

readonly loggedIn = output<unknown>();

Expand All @@ -39,21 +40,21 @@ export class OauthButtonComponent implements OnDestroy {
};

get buttonText(): string {
switch (this.oauthType) {
switch (this.oauthType()) {
case OauthButtonType.Jira:
if (this.isLoggedIn) {
if (this.isLoggedIn()) {
return this.translate.instant('Logged In To Jira');
}
return this.translate.instant('Login To Jira To Submit');

case OauthButtonType.Provider:
if (this.isLoggedIn) {
if (this.isLoggedIn()) {
return this.translate.instant('Logged In To Provider');
}
return this.translate.instant('Log In To Provider');

case OauthButtonType.Gmail:
if (this.isLoggedIn) {
if (this.isLoggedIn()) {
return this.translate.instant('Logged In To Gmail');
}
return this.translate.instant('Log In To Gmail');
Expand All @@ -75,7 +76,7 @@ export class OauthButtonComponent implements OnDestroy {
}

onOauthClicked(): void {
switch (this.oauthType) {
switch (this.oauthType()) {
case OauthButtonType.Jira:
this.onLoginWithJira();
break;
Expand Down Expand Up @@ -144,7 +145,7 @@ export class OauthButtonComponent implements OnDestroy {
): void {
this.window.removeEventListener('message', authFn, false);
this.window.open(
this.oauthUrl + encodeURIComponent(this.window.location.toString()),
this.oauthUrl() + encodeURIComponent(this.window.location.toString()),
'_blank',
'width=640,height=480',
);
Expand Down
4 changes: 2 additions & 2 deletions src/app/modules/dates/pipes/ix-date/ix-date.component.html
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<span
class="date-spanner"
[matTooltip]="'Browser time: {time}' | translate: { time: (date | formatDateTime) }"
[matTooltip]="'Browser time: {time}' | translate: { time: (date() | formatDateTime) }"
[matTooltipDisabled]="!isTimezoneDifference"
>
{{ machineTime | formatDateTime }}
</span>
</span>
11 changes: 7 additions & 4 deletions src/app/modules/dates/pipes/ix-date/ix-date.component.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import { ChangeDetectionStrategy, Component, Input } from '@angular/core';
import {
ChangeDetectionStrategy, Component, input,
} from '@angular/core';
import { MatTooltip } from '@angular/material/tooltip';
import { TranslateModule } from '@ngx-translate/core';
import { utcToZonedTime, zonedTimeToUtc } from 'date-fns-tz';
Expand All @@ -18,17 +20,18 @@ import { LocaleService } from 'app/services/locale.service';
})
export class IxDateComponent {
/** Date must be in browser timezone */
@Input() date: number | Date;
readonly date = input<number | Date>();

machineTimezone: string;
defaultTz: string = Intl.DateTimeFormat().resolvedOptions().timeZone;

get machineTime(): Date {
const utc = zonedTimeToUtc(this.date, this.defaultTz);
const utc = zonedTimeToUtc(this.date(), this.defaultTz);
return utcToZonedTime(utc, this.machineTimezone);
}

get isTimezoneDifference(): boolean {
return this.machineTime < this.date || this.machineTime > this.date;
return this.machineTime < this.date() || this.machineTime > this.date();
}

constructor(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
<h1 #errorTitle mat-dialog-title id="err-title" class="err-title">
<ix-icon class="error-warning-icon" name="error"></ix-icon>
{{ title | translate }}
{{ title() | translate }}
</h1>
<div #errorMdContent id="err-md-content">
<div #errorMessageWrapper id="err-message-wrapper"><span [innerHTML]="message"></span></div>
@if (backtrace) {
<div #errorMessageWrapper id="err-message-wrapper"><span [innerHTML]="message()"></span></div>
@if (backtrace()) {
<div
class="more-info"
(click)="toggleOpen()"
Expand All @@ -17,20 +17,20 @@ <h1 #errorTitle mat-dialog-title id="err-title" class="err-title">
<span>{{ 'More info...' | translate }}</span>
</div>
}
@if (backtrace) {
@if (backtrace()) {
<div
#errorBtPanel
id="err-bt-panel"
class="backtrace-panel"
[class.open]="!isCloseMoreInfo"
>
<div #errorBtText class="textarea scrolled-down" id="err-bt-text" readonly matInput>
{{ 'Error' | translate }}: {{ backtrace }}
{{ 'Error' | translate }}: {{ backtrace() }}
</div>
</div>
}
</div>
@if (logs) {
@if (logs()) {
<button
mat-button
class="mat-mdc-button mat-button mat-primary"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { HttpErrorResponse } from '@angular/common/http';
import {
ChangeDetectionStrategy, Component, ElementRef, Input, ViewChild,
ChangeDetectionStrategy, Component, ElementRef, input, ViewChild,
} from '@angular/core';
import { MatButton } from '@angular/material/button';
import { MatDialogTitle } from '@angular/material/dialog';
Expand Down Expand Up @@ -36,11 +36,12 @@ export class ErrorTemplateComponent {
@ViewChild('errorBtPanel') errorBtPanel: ElementRef<HTMLElement>;
@ViewChild('errorBtText') errorBtText: ElementRef<HTMLElement>;

@Input() title: string;
@Input() message: string;
@Input() backtrace: string;
readonly title = input<string>();
readonly message = input<string>();
readonly backtrace = input<string>();
readonly logs = input<Job>();

isCloseMoreInfo = true;
@Input() logs: Job;

constructor(
private api: ApiService,
Expand Down Expand Up @@ -68,13 +69,13 @@ export class ErrorTemplateComponent {
}

downloadLogs(): void {
this.api.call('core.job_download_logs', [this.logs.id, `${this.logs.id}.log`])
this.api.call('core.job_download_logs', [this.logs().id, `${this.logs().id}.log`])
.pipe(this.errorHandler.catchError(), untilDestroyed(this))
.subscribe((url) => {
const mimetype = 'text/plain';
this.download.streamDownloadFile(url, `${this.logs.id}.log`, mimetype).pipe(untilDestroyed(this)).subscribe({
this.download.streamDownloadFile(url, `${this.logs().id}.log`, mimetype).pipe(untilDestroyed(this)).subscribe({
next: (file) => {
this.download.downloadBlob(file, `${this.logs.id}.log`);
this.download.downloadBlob(file, `${this.logs().id}.log`);
},
error: (error: HttpErrorResponse) => {
this.dialogService.error(this.errorHandler.parseHttpError(error));
Expand Down
20 changes: 10 additions & 10 deletions src/app/modules/empty/empty.component.html
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<div
[class]="'empty-page ' + conf.type"
[class.large]="conf?.large"
[class.compact]="conf?.compact"
[class]="'empty-page ' + conf().type"
[class.large]="conf()?.large"
[class.compact]="conf()?.compact"
>
@if (!isLoading) {
<div class="icon-div">
Expand All @@ -15,21 +15,21 @@
}
@if (!isLoading) {
<div>
<h3 class="empty-title">{{ conf.title | translate }}</h3>
@if (conf.message && !conf.compact) {
<h3 class="empty-title">{{ conf().title | translate }}</h3>
@if (conf().message && !conf().compact) {
<p class="empty-message">
{{ conf.message | translate }}
{{ conf().message | translate }}
</p>
}
@if (conf.button && !conf.compact) {
@if (conf().button && !conf().compact) {
<button
*ixRequiresRoles="requiredRoles"
*ixRequiresRoles="requiredRoles()"
class="empty-action"
mat-button
[ixTest]="[conf.button.label]"
[ixTest]="[conf().button.label]"
(click)="doAction()"
>
{{ conf.button.label | translate }}
{{ conf().button.label | translate }}
</button>
}
</div>
Expand Down
23 changes: 13 additions & 10 deletions src/app/modules/empty/empty.component.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import { ChangeDetectionStrategy, Component, Input } from '@angular/core';
import {
ChangeDetectionStrategy, Component, input,
} from '@angular/core';
import { MatButtonModule } from '@angular/material/button';
import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
import { TranslateModule } from '@ngx-translate/core';
Expand Down Expand Up @@ -28,25 +30,26 @@ import { TestDirective } from 'app/modules/test-id/test.directive';
],
})
export class EmptyComponent {
@Input() conf: EmptyConfig;
@Input() requiredRoles: Role[];
readonly conf = input.required<EmptyConfig>();
readonly requiredRoles = input<Role[]>();

doAction(): void {
if (this.conf.button.action) {
this.conf.button.action();
if (this.conf().button.action) {
this.conf().button.action();
}
}

get isLoading(): boolean {
return this.conf.type === EmptyType.Loading;
return this.conf().type === EmptyType.Loading;
}

getIcon(): MarkedIcon {
let icon = iconMarker('ix-truenas-logo');
if (this.conf.icon) {
icon = this.conf.icon;
if (this.conf().icon) {
icon = this.conf().icon;
} else {
switch (this.conf.type) {
const type = this.conf().type;
switch (type) {
case EmptyType.Loading:
icon = iconMarker('ix-truenas-logo');
break;
Expand All @@ -63,7 +66,7 @@ export class EmptyComponent {
icon = iconMarker('mdi-magnify-scan');
break;
default:
assertUnreachable(this.conf.type);
assertUnreachable(type);
}
}
return icon;
Expand Down
Loading
Loading