diff --git a/src/app/modules/lists/dual-listbox/dual-listbox.component.html b/src/app/modules/lists/dual-listbox/dual-listbox.component.html index 217e6060872..05cbc345ad9 100644 --- a/src/app/modules/lists/dual-listbox/dual-listbox.component.html +++ b/src/app/modules/lists/dual-listbox/dual-listbox.component.html @@ -38,6 +38,7 @@ type="button" color="primary" ixTest="move-selected-right" + matTooltipPosition="right" [attr.aria-label]="'Move selected items to the right side list' | translate" [matTooltip]="'Move selected items to the right side list' | translate" [disabled]="available.pick.length === 0" @@ -51,6 +52,7 @@ type="button" color="primary" ixTest="move-all-right" + matTooltipPosition="right" [disabled]="isAllSelected(available)" [attr.aria-label]="'Move all items to the right side list' | translate" [matTooltip]="'Move all items to the right side list' | translate" @@ -64,6 +66,7 @@ type="button" color="primary" ixTest="move-selected-left" + matTooltipPosition="right" [attr.aria-label]="'Move selected items to the left side list' | translate" [matTooltip]="'Move selected items to the left side list' | translate" [disabled]="confirmed.pick.length === 0" @@ -77,6 +80,7 @@ type="button" color="primary" ixTest="move-all-left" + matTooltipPosition="right" [attr.aria-label]="'Move all items to the left side list' | translate" [matTooltip]="'Move all items to the left side list' | translate" [disabled]="isAllSelected(confirmed)" diff --git a/src/app/pages/credentials/groups/group-members/group-members.component.html b/src/app/pages/credentials/groups/group-members/group-members.component.html index 6d62e57f479..1f5181d168c 100644 --- a/src/app/pages/credentials/groups/group-members/group-members.component.html +++ b/src/app/pages/credentials/groups/group-members/group-members.component.html @@ -1,11 +1,11 @@ - @if (isFormLoading) { + @if (isLoading()) { } - @if (group?.group) { + @if (group()?.group) { - {{ 'Manage members of {name} group' | translate: { name: group.group } }} + {{ 'Manage members of {name} group' | translate: { name: group().group } }} @if (requiredRoles?.length && !(hasRequiredRoles | async)) { } @@ -13,7 +13,7 @@ } - @if (users.length) { + @if (users().length) { } @@ -34,7 +34,7 @@ mat-button color="primary" ixTest="save" - [disabled]="isFormLoading" + [disabled]="isLoading()" (click)="onSubmit()" > {{ 'Save' | translate }} diff --git a/src/app/pages/credentials/groups/group-members/group-members.component.spec.ts b/src/app/pages/credentials/groups/group-members/group-members.component.spec.ts index abc1dcb943d..50d6608aece 100644 --- a/src/app/pages/credentials/groups/group-members/group-members.component.spec.ts +++ b/src/app/pages/credentials/groups/group-members/group-members.component.spec.ts @@ -54,6 +54,10 @@ describe('GroupMembersComponent', () => { api = spectator.inject(ApiService); }); + it('loads local users to show in available users', () => { + expect(spectator.inject(ApiService).call).toHaveBeenCalledWith('user.query', [[['local', '=', true]]]); + }); + it('shows current group values when form is being edited', async () => { const userList = await loader.getHarness(MatListHarness.with({ selector: '[aria-label="All Users"]' })); const memberList = await loader.getHarness(MatListHarness.with({ selector: '[aria-label="Group Members"]' })); @@ -63,7 +67,6 @@ describe('GroupMembersComponent', () => { expect(await userList.getItems()).toHaveLength(1); expect(await memberList.getItems()).toHaveLength(1); - expect(api.call).toHaveBeenCalledWith('user.query'); expect(api.call).toHaveBeenCalledWith('group.query', [[['id', '=', 1]]]); }); diff --git a/src/app/pages/credentials/groups/group-members/group-members.component.ts b/src/app/pages/credentials/groups/group-members/group-members.component.ts index 44c044e59ab..963bf90a7bd 100644 --- a/src/app/pages/credentials/groups/group-members/group-members.component.ts +++ b/src/app/pages/credentials/groups/group-members/group-members.component.ts @@ -1,6 +1,6 @@ import { AsyncPipe } from '@angular/common'; import { - ChangeDetectionStrategy, ChangeDetectorRef, Component, OnInit, + ChangeDetectionStrategy, Component, OnInit, signal, } from '@angular/core'; import { MatButton } from '@angular/material/button'; import { @@ -11,7 +11,7 @@ import { MatProgressBar } from '@angular/material/progress-bar'; import { ActivatedRoute, Router } from '@angular/router'; import { UntilDestroy, untilDestroyed } from '@ngneat/until-destroy'; import { TranslateModule } from '@ngx-translate/core'; -import { Observable } from 'rxjs'; +import { forkJoin, Observable } from 'rxjs'; import { switchMap } from 'rxjs/operators'; import { RequiresRolesDirective } from 'app/directives/requires-roles/requires-roles.directive'; import { Role } from 'app/enums/role.enum'; @@ -56,12 +56,11 @@ import { ErrorHandlerService } from 'app/services/error-handler.service'; export class GroupMembersComponent implements OnInit { protected readonly requiredRoles = [Role.AccountWrite]; protected readonly iconMarker = iconMarker; + protected selectedMembers: User[] = []; + protected readonly users = signal([]); - selectedMembers: User[] = []; - users: User[] = []; - - isFormLoading = false; - group: Group; + protected readonly isLoading = signal(false); + protected readonly group = signal(null); get hasRequiredRoles(): Observable { return this.authService.hasRole(this.requiredRoles); @@ -73,27 +72,22 @@ export class GroupMembersComponent implements OnInit { private router: Router, private dialog: DialogService, private errorHandler: ErrorHandlerService, - private cdr: ChangeDetectorRef, private authService: AuthService, ) {} ngOnInit(): void { - this.isFormLoading = true; + this.isLoading.set(true); this.activatedRoute.params.pipe( - switchMap((params) => { - return this.api.call('group.query', [[['id', '=', parseInt(params.pk as string)]]]); - }), - switchMap((groups) => { - this.group = groups[0]; - this.cdr.markForCheck(); - return this.api.call('user.query'); - }), + switchMap((params) => forkJoin([ + this.api.call('group.query', [[['id', '=', parseInt(params.pk as string)]]]), + this.api.call('user.query', [[['local', '=', true]]]), + ])), untilDestroyed(this), - ).subscribe((users) => { - this.users = users; - this.selectedMembers = users.filter((user) => this.group.users.includes(user.id)); - this.isFormLoading = false; - this.cdr.markForCheck(); + ).subscribe(([groups, users]) => { + this.group.set(groups[0]); + this.users.set(users); + this.selectedMembers = users.filter((user) => this.group().users.includes(user.id)); + this.isLoading.set(false); }); } @@ -102,20 +96,18 @@ export class GroupMembersComponent implements OnInit { } onSubmit(): void { - this.isFormLoading = true; - this.cdr.markForCheck(); + this.isLoading.set(true); const userIds = this.selectedMembers.map((user) => user.id); - this.api.call('group.update', [this.group.id, { users: userIds }]).pipe( + this.api.call('group.update', [this.group().id, { users: userIds }]).pipe( untilDestroyed(this), ).subscribe({ next: () => { - this.isFormLoading = false; + this.isLoading.set(false); this.router.navigate(['/', 'credentials', 'groups']); }, error: (error) => { - this.isFormLoading = false; - this.cdr.markForCheck(); + this.isLoading.set(false); this.dialog.error(this.errorHandler.parseError(error)); }, }); diff --git a/src/app/pages/data-protection/cloud-backup/cloud-backup-form/cloud-backup-form.component.ts b/src/app/pages/data-protection/cloud-backup/cloud-backup-form/cloud-backup-form.component.ts index 234a85d0ac5..8d3a485621e 100644 --- a/src/app/pages/data-protection/cloud-backup/cloud-backup-form/cloud-backup-form.component.ts +++ b/src/app/pages/data-protection/cloud-backup/cloud-backup-form/cloud-backup-form.component.ts @@ -118,7 +118,7 @@ export class CloudBackupFormComponent implements OnInit { editingTask: CloudBackup; bucketOptions$ = of([]); - transferSettings$ = this.ws.call('cloud_backup.transfer_setting_choices').pipe( + transferSettings$ = this.api.call('cloud_backup.transfer_setting_choices').pipe( map((availableSettings) => { const allOptions = mapToOptions(cloudsyncTransferSettingLabels, this.translate); return allOptions.filter((option) => availableSettings.includes(option.value as CloudsyncTransferSetting)); diff --git a/src/assets/i18n/zh-hans.json b/src/assets/i18n/zh-hans.json index d8eab98333c..1c05c69d96a 100644 --- a/src/assets/i18n/zh-hans.json +++ b/src/assets/i18n/zh-hans.json @@ -94,7 +94,6 @@ "No proxies added.": "", "Non expiring": "", "Not Installed": "", - "Now/Restart": "", "OS": "", "Override Admin Email": "", "Performance Optimization": "", diff --git a/src/assets/icons/sprite-config.json b/src/assets/icons/sprite-config.json index 55cfdca26f4..5c456b2dd65 100644 --- a/src/assets/icons/sprite-config.json +++ b/src/assets/icons/sprite-config.json @@ -1,3 +1,3 @@ { - "iconUrl": "assets/icons/sprite.svg?v=889c0f9a7a" + "iconUrl": "assets/icons/sprite.svg?v=c8ca8bd44c" } \ No newline at end of file diff --git a/src/assets/icons/sprite.svg b/src/assets/icons/sprite.svg index 3e316f8bc19..fcb13e331c8 100644 --- a/src/assets/icons/sprite.svg +++ b/src/assets/icons/sprite.svg @@ -1 +1 @@ - + \ No newline at end of file