Browse Source

refactor(admin-ui): Extract ChannelAssignmentControl component

Michael Bromley 6 years ago
parent
commit
9f60330cab

+ 2 - 2
packages/admin-ui/src/app/core/components/channel-switcher/channel-switcher.component.html

@@ -2,7 +2,7 @@
     <vdr-dropdown *ngIf="1 < channels.length">
     <vdr-dropdown *ngIf="1 < channels.length">
         <button class="btn btn-link active-channel" vdrDropdownTrigger>
         <button class="btn btn-link active-channel" vdrDropdownTrigger>
             <vdr-channel-badge [channelCode]="activeChannelCode$ | async"></vdr-channel-badge>
             <vdr-channel-badge [channelCode]="activeChannelCode$ | async"></vdr-channel-badge>
-            <span class="active-channel">{{ activeChannelLabel$ | async | translate }}</span>
+            <span class="active-channel">{{ activeChannelCode$ | async | channelCodeToLabel | translate }}</span>
             <span class="trigger"><clr-icon shape="caret down"></clr-icon></span>
             <span class="trigger"><clr-icon shape="caret down"></clr-icon></span>
         </button>
         </button>
         <vdr-dropdown-menu vdrPosition="bottom-right">
         <vdr-dropdown-menu vdrPosition="bottom-right">
@@ -13,7 +13,7 @@
                 (click)="setActiveChannel(channel.id)"
                 (click)="setActiveChannel(channel.id)"
             >
             >
                 <vdr-channel-badge [channelCode]="channel.code"></vdr-channel-badge>
                 <vdr-channel-badge [channelCode]="channel.code"></vdr-channel-badge>
-                {{ getChannelLabel(channel.code) | translate }}
+                {{ channel.code | channelCodeToLabel | translate }}
             </button>
             </button>
         </vdr-dropdown-menu>
         </vdr-dropdown-menu>
     </vdr-dropdown>
     </vdr-dropdown>

+ 0 - 14
packages/admin-ui/src/app/core/components/channel-switcher/channel-switcher.component.ts

@@ -17,7 +17,6 @@ import { LocalStorageService } from '../../providers/local-storage/local-storage
 })
 })
 export class ChannelSwitcherComponent implements OnInit {
 export class ChannelSwitcherComponent implements OnInit {
     channels$: Observable<CurrentUserChannel[]>;
     channels$: Observable<CurrentUserChannel[]>;
-    activeChannelLabel$: Observable<string>;
     activeChannelCode$: Observable<string>;
     activeChannelCode$: Observable<string>;
     constructor(private dataService: DataService, private localStorageService: LocalStorageService) {}
     constructor(private dataService: DataService, private localStorageService: LocalStorageService) {}
 
 
@@ -27,22 +26,9 @@ export class ChannelSwitcherComponent implements OnInit {
             .userStatus()
             .userStatus()
             .mapStream(data => data.userStatus.channels.find(c => c.id === data.userStatus.activeChannelId))
             .mapStream(data => data.userStatus.channels.find(c => c.id === data.userStatus.activeChannelId))
             .pipe(filter(notNullOrUndefined));
             .pipe(filter(notNullOrUndefined));
-        this.activeChannelLabel$ = activeChannel$.pipe(map(channel => this.getChannelLabel(channel.code)));
         this.activeChannelCode$ = activeChannel$.pipe(map(channel => channel.code));
         this.activeChannelCode$ = activeChannel$.pipe(map(channel => channel.code));
     }
     }
 
 
-    getChannelLabel(channelCode: string): string {
-        if (this.isDefaultChannel(channelCode)) {
-            return _('common.default-channel');
-        } else {
-            return channelCode;
-        }
-    }
-
-    isDefaultChannel(channelCode: string): boolean {
-        return channelCode === DEFAULT_CHANNEL_CODE;
-    }
-
     setActiveChannel(channelId: string) {
     setActiveChannel(channelId: string) {
         this.dataService.client.setActiveChannel(channelId).subscribe(({ setActiveChannel }) => {
         this.dataService.client.setActiveChannel(channelId).subscribe(({ setActiveChannel }) => {
             const activeChannel = setActiveChannel.channels.find(c => c.id === channelId);
             const activeChannel = setActiveChannel.channels.find(c => c.id === channelId);

+ 4 - 1
packages/admin-ui/src/app/settings/components/channel-list/channel-list.component.html

@@ -12,7 +12,10 @@
     <vdr-dt-column>{{ 'common.code' | translate }}</vdr-dt-column>
     <vdr-dt-column>{{ 'common.code' | translate }}</vdr-dt-column>
     <vdr-dt-column></vdr-dt-column>
     <vdr-dt-column></vdr-dt-column>
     <ng-template let-channel="item">
     <ng-template let-channel="item">
-        <td class="left align-middle">{{ channel.code }}</td>
+        <td class="left align-middle">
+            <vdr-channel-badge [channelCode]="channel.code"></vdr-channel-badge>
+            {{ channel.code | channelCodeToLabel | translate }}
+        </td>
         <td class="right align-middle">
         <td class="right align-middle">
             <vdr-table-row-action
             <vdr-table-row-action
                 iconShape="edit"
                 iconShape="edit"

+ 15 - 11
packages/admin-ui/src/app/settings/components/role-detail/role-detail.component.html

@@ -6,7 +6,7 @@
         <vdr-action-bar-items locationId="role-detail"></vdr-action-bar-items>
         <vdr-action-bar-items locationId="role-detail"></vdr-action-bar-items>
         <button
         <button
             class="btn btn-primary"
             class="btn btn-primary"
-            *ngIf="(isNew$ | async); else updateButton"
+            *ngIf="isNew$ | async; else updateButton"
             (click)="create()"
             (click)="create()"
             [disabled]="detailForm.invalid || detailForm.pristine"
             [disabled]="detailForm.invalid || detailForm.pristine"
         >
         >
@@ -35,19 +35,23 @@
             (input)="updateCode($event.target.value)"
             (input)="updateCode($event.target.value)"
         />
         />
     </vdr-form-field>
     </vdr-form-field>
-    <vdr-form-field [label]="'common.code' | translate" for="code" [readOnlyToggle]="'UpdateAdministrator' | hasPermission">
-        <input id="code" type="text" formControlName="code" [readonly]="!('UpdateAdministrator' | hasPermission)" />
+    <vdr-form-field
+        [label]="'common.code' | translate"
+        for="code"
+        [readOnlyToggle]="'UpdateAdministrator' | hasPermission"
+    >
+        <input
+            id="code"
+            type="text"
+            formControlName="code"
+            [readonly]="!('UpdateAdministrator' | hasPermission)"
+        />
     </vdr-form-field>
     </vdr-form-field>
     <vdr-form-field [label]="'settings.channel' | translate">
     <vdr-form-field [label]="'settings.channel' | translate">
-        <ng-select
-            [items]="channels$ | async"
-            bindLabel="code"
-            bindValue="id"
-            appendTo="body"
-            [addTag]="false"
-            [multiple]="true"
+        <vdr-channel-assignment-control
             formControlName="channelIds"
             formControlName="channelIds"
-        ></ng-select>
+            [vdrDisabled]="!('UpdateAdministrator' | hasPermission)"
+        ></vdr-channel-assignment-control>
     </vdr-form-field>
     </vdr-form-field>
     <label>{{ 'settings.permissions' | translate }}</label>
     <label>{{ 'settings.permissions' | translate }}</label>
     <vdr-permission-grid
     <vdr-permission-grid

+ 1 - 12
packages/admin-ui/src/app/settings/components/role-detail/role-detail.component.ts

@@ -2,13 +2,12 @@ import { ChangeDetectionStrategy, ChangeDetectorRef, Component, OnDestroy, OnIni
 import { FormBuilder, FormGroup, Validators } from '@angular/forms';
 import { FormBuilder, FormGroup, Validators } from '@angular/forms';
 import { ActivatedRoute, Router } from '@angular/router';
 import { ActivatedRoute, Router } from '@angular/router';
 import { Observable } from 'rxjs';
 import { Observable } from 'rxjs';
-import { map, mergeMap, take, tap } from 'rxjs/operators';
+import { mergeMap, take } from 'rxjs/operators';
 import { normalizeString } from 'shared/normalize-string';
 import { normalizeString } from 'shared/normalize-string';
 
 
 import { BaseDetailComponent } from '../../../common/base-detail.component';
 import { BaseDetailComponent } from '../../../common/base-detail.component';
 import {
 import {
     CreateRoleInput,
     CreateRoleInput,
-    CurrentUserChannel,
     LanguageCode,
     LanguageCode,
     Permission,
     Permission,
     Role,
     Role,
@@ -27,7 +26,6 @@ import { ServerConfigService } from '../../../data/server-config';
 })
 })
 export class RoleDetailComponent extends BaseDetailComponent<Role> implements OnInit, OnDestroy {
 export class RoleDetailComponent extends BaseDetailComponent<Role> implements OnInit, OnDestroy {
     role$: Observable<Role>;
     role$: Observable<Role>;
-    channels$: Observable<CurrentUserChannel[]>;
     detailForm: FormGroup;
     detailForm: FormGroup;
     permissions: { [K in Permission]: boolean };
     permissions: { [K in Permission]: boolean };
     permissionsChanged = false;
     permissionsChanged = false;
@@ -55,15 +53,6 @@ export class RoleDetailComponent extends BaseDetailComponent<Role> implements On
     ngOnInit() {
     ngOnInit() {
         this.init();
         this.init();
         this.role$ = this.entity$;
         this.role$ = this.entity$;
-        this.channels$ = this.dataService.client.userStatus().single$.pipe(
-            map(data => (data.userStatus ? data.userStatus.channels : [])),
-            tap(channels => {
-                const channelIdControl = this.detailForm.get('channelId');
-                if (channelIdControl) {
-                    channelIdControl.patchValue(channels[0].id);
-                }
-            }),
-        );
     }
     }
 
 
     ngOnDestroy(): void {
     ngOnDestroy(): void {

+ 24 - 0
packages/admin-ui/src/app/shared/components/channel-assignment-control/channel-assignment-control.component.html

@@ -0,0 +1,24 @@
+<ng-select
+    [items]="channels$ | async"
+    bindLabel="code"
+    bindValue="id"
+    appendTo="body"
+    [addTag]="false"
+    [multiple]="true"
+    [ngModel]="value"
+    [clearable]="false"
+    [disabled]="disabled"
+    (focus)="focussed()"
+    (change)="valueChanged($event)"
+>
+    <ng-template ng-label-tmp let-item="item" let-clear="clear">
+        <span aria-hidden="true" class="ng-value-icon left" (click)="clear(item)"> × </span>
+        <vdr-channel-badge [channelCode]="item.code"></vdr-channel-badge>
+        <span class="channel-label">{{ item.code | channelCodeToLabel | translate }}</span>
+    </ng-template>
+    <ng-template ng-option-tmp let-item="item">
+        <vdr-channel-badge [channelCode]="item.code"></vdr-channel-badge>
+        {{ item.code | channelCodeToLabel | translate }}
+    </ng-template>
+</ng-select>
+

+ 11 - 0
packages/admin-ui/src/app/shared/components/channel-assignment-control/channel-assignment-control.component.scss

@@ -0,0 +1,11 @@
+@import "variables";
+
+::ng-deep .ng-value, ::ng-deep .ng-option {
+    > vdr-channel-badge {
+        margin-bottom: -1px;
+    }
+}
+
+.channel-label {
+    margin-right: 6px;
+}

+ 62 - 0
packages/admin-ui/src/app/shared/components/channel-assignment-control/channel-assignment-control.component.ts

@@ -0,0 +1,62 @@
+import { ChangeDetectionStrategy, Component, OnInit } from '@angular/core';
+import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';
+import { Observable } from 'rxjs';
+import { map } from 'rxjs/operators';
+
+import { CurrentUserChannel } from '../../../common/generated-types';
+import { DataService } from '../../../data/providers/data.service';
+
+@Component({
+    selector: 'vdr-channel-assignment-control',
+    templateUrl: './channel-assignment-control.component.html',
+    styleUrls: ['./channel-assignment-control.component.scss'],
+    changeDetection: ChangeDetectionStrategy.OnPush,
+    providers: [
+        {
+            provide: NG_VALUE_ACCESSOR,
+            useExisting: ChannelAssignmentControlComponent,
+            multi: true,
+        },
+    ],
+})
+export class ChannelAssignmentControlComponent implements OnInit, ControlValueAccessor {
+    channels$: Observable<CurrentUserChannel[]>;
+    value: string[] = [];
+    disabled = false;
+    private onChange: (value: any) => void;
+    private onTouched: () => void;
+
+    constructor(private dataService: DataService) {}
+
+    ngOnInit() {
+        this.channels$ = this.dataService.client
+            .userStatus()
+            .single$.pipe(map(data => (data.userStatus ? data.userStatus.channels : [])));
+    }
+
+    registerOnChange(fn: any): void {
+        this.onChange = fn;
+    }
+
+    registerOnTouched(fn: any): void {
+        this.onTouched = fn;
+    }
+
+    setDisabledState(isDisabled: boolean): void {
+        this.disabled = isDisabled;
+    }
+
+    writeValue(obj: unknown): void {
+        if (Array.isArray(obj)) {
+            this.value = obj;
+        }
+    }
+
+    focussed() {
+        this.onTouched();
+    }
+
+    valueChanged(channels: CurrentUserChannel[]) {
+        this.onChange(channels.map(c => c.id));
+    }
+}

+ 17 - 0
packages/admin-ui/src/app/shared/pipes/channel-label.pipe.ts

@@ -0,0 +1,17 @@
+import { Pipe, PipeTransform } from '@angular/core';
+import { DEFAULT_CHANNEL_CODE } from 'shared/shared-constants';
+
+import { _ } from '../../core/providers/i18n/mark-for-extraction';
+
+@Pipe({
+    name: 'channelCodeToLabel',
+})
+export class ChannelLabelPipe implements PipeTransform {
+    transform(value: any, ...args: any[]): any {
+        if (value === DEFAULT_CHANNEL_CODE) {
+            return _('common.default-channel');
+        } else {
+            return value;
+        }
+    }
+}

+ 4 - 0
packages/admin-ui/src/app/shared/shared-declarations.ts

@@ -8,7 +8,11 @@ export { AssetFileInputComponent } from './components/asset-file-input/asset-fil
 export { AssetGalleryComponent } from './components/asset-gallery/asset-gallery.component';
 export { AssetGalleryComponent } from './components/asset-gallery/asset-gallery.component';
 export { AssetPickerDialogComponent } from './components/asset-picker-dialog/asset-picker-dialog.component';
 export { AssetPickerDialogComponent } from './components/asset-picker-dialog/asset-picker-dialog.component';
 export { PercentageSuffixInputComponent } from './components/affixed-input/percentage-suffix-input.component';
 export { PercentageSuffixInputComponent } from './components/affixed-input/percentage-suffix-input.component';
+export {
+    ChannelAssignmentControlComponent,
+} from './components/channel-assignment-control/channel-assignment-control.component';
 export { ChannelBadgeComponent } from './components/channel-badge/channel-badge.component';
 export { ChannelBadgeComponent } from './components/channel-badge/channel-badge.component';
+export { ChannelLabelPipe } from './pipes/channel-label.pipe';
 export { ChipComponent } from './components/chip/chip.component';
 export { ChipComponent } from './components/chip/chip.component';
 export { ConfigurableInputComponent } from './components/configurable-input/configurable-input.component';
 export { ConfigurableInputComponent } from './components/configurable-input/configurable-input.component';
 export { CurrencyInputComponent } from './components/currency-input/currency-input.component';
 export { CurrencyInputComponent } from './components/currency-input/currency-input.component';

+ 4 - 0
packages/admin-ui/src/app/shared/shared.module.ts

@@ -24,7 +24,9 @@ import {
     AssetFileInputComponent,
     AssetFileInputComponent,
     AssetGalleryComponent,
     AssetGalleryComponent,
     AssetPickerDialogComponent,
     AssetPickerDialogComponent,
+    ChannelAssignmentControlComponent,
     ChannelBadgeComponent,
     ChannelBadgeComponent,
+    ChannelLabelPipe,
     ChipComponent,
     ChipComponent,
     ConfigurableInputComponent,
     ConfigurableInputComponent,
     CurrencyInputComponent,
     CurrencyInputComponent,
@@ -132,6 +134,8 @@ const DECLARATIONS = [
     EntityInfoComponent,
     EntityInfoComponent,
     DatetimePickerComponent,
     DatetimePickerComponent,
     ChannelBadgeComponent,
     ChannelBadgeComponent,
+    ChannelAssignmentControlComponent,
+    ChannelLabelPipe,
 ];
 ];
 
 
 @NgModule({
 @NgModule({