Przeglądaj źródła

feat: Implement Channel API & admin-ui list/detail

Michael Bromley 7 lat temu
rodzic
commit
ef68c26796
24 zmienionych plików z 774 dodań i 97 usunięć
  1. 7 0
      admin-ui/src/app/core/components/main-nav/main-nav.component.html
  2. 53 0
      admin-ui/src/app/data/definitions/settings-definitions.ts
  3. 4 0
      admin-ui/src/app/data/providers/data.service.mock.ts
  4. 32 0
      admin-ui/src/app/data/providers/settings-data.service.ts
  5. 38 0
      admin-ui/src/app/settings/components/channel-detail/channel-detail.component.html
  6. 0 0
      admin-ui/src/app/settings/components/channel-detail/channel-detail.component.scss
  7. 128 0
      admin-ui/src/app/settings/components/channel-detail/channel-detail.component.ts
  8. 24 0
      admin-ui/src/app/settings/components/channel-list/channel-list.component.html
  9. 0 0
      admin-ui/src/app/settings/components/channel-list/channel-list.component.scss
  10. 19 0
      admin-ui/src/app/settings/components/channel-list/channel-list.component.ts
  11. 27 0
      admin-ui/src/app/settings/providers/routing/channel-resolver.ts
  12. 13 1
      admin-ui/src/app/settings/settings.module.ts
  13. 27 1
      admin-ui/src/app/settings/settings.routes.ts
  14. 6 1
      admin-ui/src/i18n-messages/en.json
  15. 0 0
      schema.json
  16. 1 1
      server/mock-data/data-sources/countries.json
  17. 70 23
      server/mock-data/mock-data.service.ts
  18. 5 5
      server/mock-data/populate.ts
  19. 29 4
      server/src/api/resolvers/channel.resolver.ts
  20. 10 1
      server/src/api/types/channel.api.graphql
  21. 8 1
      server/src/entity/channel/channel.entity.ts
  22. 20 0
      server/src/entity/channel/channel.graphql
  23. 52 6
      server/src/service/providers/channel.service.ts
  24. 201 53
      shared/generated-types.ts

+ 7 - 0
admin-ui/src/app/core/components/main-nav/main-nav.component.html

@@ -68,6 +68,13 @@
                         <clr-icon shape="administrator" size="20"></clr-icon>{{ 'nav.administrators' | translate }}
                     </a>
                 </li>
+                <li>
+                    <a class="nav-link"
+                       [routerLink]="['/settings', 'channels']"
+                       routerLinkActive="active">
+                        <clr-icon shape="layers" size="20"></clr-icon>{{ 'nav.channels' | translate }}
+                    </a>
+                </li>
                 <li>
                     <a class="nav-link"
                        [routerLink]="['/settings', 'roles']"

+ 53 - 0
admin-ui/src/app/data/definitions/settings-definitions.ts

@@ -215,3 +215,56 @@ export const UPDATE_TAX_RATE = gql`
     }
     ${TAX_RATE_FRAGMENT}
 `;
+
+export const CHANNEL_FRAGMENT = gql`
+    fragment Channel on Channel {
+        id
+        code
+        token
+        defaultLanguageCode
+        defaultShippingZone {
+            id
+            name
+        }
+        defaultTaxZone {
+            id
+            name
+        }
+    }
+`;
+
+export const GET_CHANNELS = gql`
+    query GetChannels {
+        channels {
+            ...Channel
+        }
+    }
+    ${CHANNEL_FRAGMENT}
+`;
+
+export const GET_CHANNEL = gql`
+    query GetChannel($id: ID!) {
+        channel(id: $id) {
+            ...Channel
+        }
+    }
+    ${CHANNEL_FRAGMENT}
+`;
+
+export const CREATE_CHANNEL = gql`
+    mutation CreateChannel($input: CreateChannelInput!) {
+        createChannel(input: $input) {
+            ...Channel
+        }
+    }
+    ${CHANNEL_FRAGMENT}
+`;
+
+export const UPDATE_CHANNEL = gql`
+    mutation UpdateChannel($input: UpdateChannelInput!) {
+        updateChannel(input: $input) {
+            ...Channel
+        }
+    }
+    ${CHANNEL_FRAGMENT}
+`;

+ 4 - 0
admin-ui/src/app/data/providers/data.service.mock.ts

@@ -107,5 +107,9 @@ export class MockDataService implements DataServiceMock {
         getTaxRate: spyQueryResult('getTaxRate'),
         createTaxRate: spyObservable('createTaxRate'),
         updateTaxRate: spyObservable('updateTaxRate'),
+        getChannels: spyQueryResult('getChannels'),
+        getChannel: spyQueryResult('getChannel'),
+        createChannel: spyObservable('createChannel'),
+        updateChannel: spyObservable('updateChannel'),
     };
 }

+ 32 - 0
admin-ui/src/app/data/providers/settings-data.service.ts

@@ -1,5 +1,7 @@
 import {
     AddMembersToZone,
+    CreateChannel,
+    CreateChannelInput,
     CreateCountry,
     CreateCountryInput,
     CreateTaxCategory,
@@ -8,6 +10,8 @@ import {
     CreateTaxRateInput,
     CreateZone,
     CreateZoneInput,
+    GetChannel,
+    GetChannels,
     GetCountry,
     GetCountryList,
     GetTaxCategories,
@@ -17,6 +21,8 @@ import {
     GetZone,
     GetZones,
     RemoveMembersFromZone,
+    UpdateChannel,
+    UpdateChannelInput,
     UpdateCountry,
     UpdateCountryInput,
     UpdateTaxCategory,
@@ -29,10 +35,13 @@ import {
 
 import {
     ADD_MEMBERS_TO_ZONE,
+    CREATE_CHANNEL,
     CREATE_COUNTRY,
     CREATE_TAX_CATEGORY,
     CREATE_TAX_RATE,
     CREATE_ZONE,
+    GET_CHANNEL,
+    GET_CHANNELS,
     GET_COUNTRY,
     GET_COUNTRY_LIST,
     GET_TAX_CATEGORIES,
@@ -41,6 +50,7 @@ import {
     GET_TAX_RATE_LIST,
     GET_ZONES,
     REMOVE_MEMBERS_FROM_ZONE,
+    UPDATE_CHANNEL,
     UPDATE_COUNTRY,
     UPDATE_TAX_CATEGORY,
     UPDATE_TAX_RATE,
@@ -171,4 +181,26 @@ export class SettingsDataService {
             input,
         });
     }
+
+    getChannels() {
+        return this.baseDataService.query<GetChannels.Query>(GET_CHANNELS);
+    }
+
+    getChannel(id: string) {
+        return this.baseDataService.query<GetChannel.Query, GetChannel.Variables>(GET_CHANNEL, {
+            id,
+        });
+    }
+
+    createChannel(input: CreateChannelInput) {
+        return this.baseDataService.mutate<CreateChannel.Mutation, CreateChannel.Variables>(CREATE_CHANNEL, {
+            input,
+        });
+    }
+
+    updateChannel(input: UpdateChannelInput) {
+        return this.baseDataService.mutate<UpdateChannel.Mutation, UpdateChannel.Variables>(UPDATE_CHANNEL, {
+            input,
+        });
+    }
 }

+ 38 - 0
admin-ui/src/app/settings/components/channel-detail/channel-detail.component.html

@@ -0,0 +1,38 @@
+<vdr-action-bar>
+    <vdr-ab-left>
+    </vdr-ab-left>
+
+    <vdr-ab-right>
+        <button class="btn btn-primary"
+                *ngIf="isNew$ | async; else updateButton"
+                (click)="create()"
+                [disabled]="!saveButtonEnabled()">{{ 'common.create' | translate }}</button>
+        <ng-template #updateButton>
+            <button class="btn btn-primary"
+                    (click)="save()"
+                    [disabled]="!saveButtonEnabled()">{{ 'common.update' | translate }}</button>
+        </ng-template>
+    </vdr-ab-right>
+</vdr-action-bar>
+
+<form class="form" [formGroup]="channelForm" >
+    <section class="form-block">
+        <vdr-form-field [label]="'common.code' | translate" for="code">
+            <input id="code" type="text" formControlName="code">
+        </vdr-form-field>
+        <vdr-form-field [label]="'settings.default-tax-zone' | translate" for="defaultTaxZoneId">
+            <select clrSelect name="defaultTaxZoneId"
+                    formControlName="defaultTaxZoneId">
+                <option *ngFor="let zone of zones$ | async"
+                        [value]="zone.id">{{ zone.name }}</option>
+            </select>
+        </vdr-form-field>
+        <vdr-form-field [label]="'settings.default-shipping-zone' | translate" for="defaultShippingZoneId">
+            <select clrSelect name="defaultShippingZoneId"
+                    formControlName="defaultShippingZoneId">
+                <option *ngFor="let zone of zones$ | async"
+                        [value]="zone.id">{{ zone.name }}</option>
+            </select>
+        </vdr-form-field>
+    </section>
+</form>

+ 0 - 0
admin-ui/src/app/settings/components/channel-detail/channel-detail.component.scss


+ 128 - 0
admin-ui/src/app/settings/components/channel-detail/channel-detail.component.ts

@@ -0,0 +1,128 @@
+import { ChangeDetectionStrategy, ChangeDetectorRef, Component, OnDestroy, OnInit } from '@angular/core';
+import { FormBuilder, FormGroup, Validators } from '@angular/forms';
+import { ActivatedRoute, Router } from '@angular/router';
+import { Observable } from 'rxjs';
+import { mergeMap, take } from 'rxjs/operators';
+import { Channel, CreateChannelInput, LanguageCode, UpdateChannelInput, Zone } from 'shared/generated-types';
+
+import { BaseDetailComponent } from '../../../common/base-detail.component';
+import { _ } from '../../../core/providers/i18n/mark-for-extraction';
+import { NotificationService } from '../../../core/providers/notification/notification.service';
+import { DataService } from '../../../data/providers/data.service';
+import { ServerConfigService } from '../../../data/server-config';
+
+@Component({
+    selector: 'vdr-channel-detail',
+    templateUrl: './channel-detail.component.html',
+    styleUrls: ['./channel-detail.component.scss'],
+    changeDetection: ChangeDetectionStrategy.OnPush,
+})
+export class ChannelDetailComponent extends BaseDetailComponent<Channel.Fragment>
+    implements OnInit, OnDestroy {
+    zones$: Observable<Zone.Fragment[]>;
+    channelForm: FormGroup;
+
+    constructor(
+        router: Router,
+        route: ActivatedRoute,
+        serverConfigService: ServerConfigService,
+        private changeDetector: ChangeDetectorRef,
+        private dataService: DataService,
+        private formBuilder: FormBuilder,
+        private notificationService: NotificationService,
+    ) {
+        super(route, router, serverConfigService);
+        this.channelForm = this.formBuilder.group({
+            code: ['', Validators.required],
+            token: ['', Validators.required],
+            defaultShippingZoneId: [''],
+            defaultTaxZoneId: [''],
+        });
+    }
+
+    ngOnInit() {
+        this.init();
+        this.zones$ = this.dataService.settings.getZones().mapSingle(data => data.zones);
+    }
+
+    ngOnDestroy() {
+        this.destroy();
+    }
+
+    saveButtonEnabled(): boolean {
+        return this.channelForm.dirty && this.channelForm.valid;
+    }
+
+    create() {
+        if (!this.channelForm.dirty) {
+            return;
+        }
+        const formValue = this.channelForm.value;
+        const input = {
+            code: formValue.code,
+            defaultShippingZoneId: formValue.defaultShippingZoneId,
+            defaultTaxZoneId: formValue.defaultTaxZoneId,
+        } as CreateChannelInput;
+        this.dataService.settings.createChannel(input).subscribe(
+            data => {
+                this.notificationService.success(_('common.notify-create-success'), {
+                    entity: 'Channel',
+                });
+                this.channelForm.markAsPristine();
+                this.changeDetector.markForCheck();
+                this.router.navigate(['../', data.createChannel.id], { relativeTo: this.route });
+            },
+            err => {
+                this.notificationService.error(_('common.notify-create-error'), {
+                    entity: 'Channel',
+                });
+            },
+        );
+    }
+
+    save() {
+        if (!this.channelForm.dirty) {
+            return;
+        }
+        const formValue = this.channelForm.value;
+        this.entity$
+            .pipe(
+                take(1),
+                mergeMap(channel => {
+                    const input = {
+                        id: channel.id,
+                        code: formValue.code,
+                        defaultShippingZoneId: formValue.defaultShippingZoneId,
+                        defaultTaxZoneId: formValue.defaultTaxZoneId,
+                    } as UpdateChannelInput;
+                    return this.dataService.settings.updateChannel(input);
+                }),
+            )
+            .subscribe(
+                data => {
+                    this.notificationService.success(_('common.notify-update-success'), {
+                        entity: 'Channel',
+                    });
+                    this.channelForm.markAsPristine();
+                    this.changeDetector.markForCheck();
+                },
+                err => {
+                    this.notificationService.error(_('common.notify-update-error'), {
+                        entity: 'Channel',
+                    });
+                },
+            );
+    }
+
+    /**
+     * Update the form values when the entity changes.
+     */
+    protected setFormValues(entity: Channel.Fragment, languageCode: LanguageCode): void {
+        this.channelForm.patchValue({
+            code: entity.code,
+            token: entity.token,
+            defaultShippingZoneId: entity.defaultShippingZone ? entity.defaultShippingZone.id : '',
+            defaultTaxZoneId: entity.defaultTaxZone ? entity.defaultTaxZone.id : '',
+        });
+    }
+}

+ 24 - 0
admin-ui/src/app/settings/components/channel-list/channel-list.component.html

@@ -0,0 +1,24 @@
+<vdr-action-bar>
+    <vdr-ab-right>
+        <a class="btn btn-primary" [routerLink]="['./create']">
+            <clr-icon shape="plus"></clr-icon>
+            {{ 'settings.create-new-channel' | translate }}
+        </a>
+    </vdr-ab-right>
+</vdr-action-bar>
+
+<vdr-data-table [items]="channels$ | async">
+    <vdr-dt-column>{{ 'common.ID' | translate }}</vdr-dt-column>
+    <vdr-dt-column>{{ 'common.code' | translate }}</vdr-dt-column>
+    <vdr-dt-column></vdr-dt-column>
+    <ng-template let-channel="item">
+        <td class="left">{{ channel.id }}</td>
+        <td class="left">{{ channel.code }}</td>
+        <td class="right">
+            <vdr-table-row-action iconShape="edit"
+                                  [label]="'common.edit' | translate"
+                                  [linkTo]="['./', channel.id]">
+            </vdr-table-row-action>
+        </td>
+    </ng-template>
+</vdr-data-table>

+ 0 - 0
admin-ui/src/app/settings/components/channel-list/channel-list.component.scss


+ 19 - 0
admin-ui/src/app/settings/components/channel-list/channel-list.component.ts

@@ -0,0 +1,19 @@
+import { ChangeDetectionStrategy, Component } from '@angular/core';
+import { Observable } from 'rxjs';
+import { Channel } from 'shared/generated-types';
+
+import { DataService } from '../../../data/providers/data.service';
+
+@Component({
+    selector: 'vdr-channel-list',
+    templateUrl: './channel-list.component.html',
+    styleUrls: ['./channel-list.component.scss'],
+    changeDetection: ChangeDetectionStrategy.OnPush,
+})
+export class ChannelListComponent {
+    channels$: Observable<Channel.Fragment[]>;
+
+    constructor(private dataService: DataService) {
+        this.channels$ = this.dataService.settings.getChannels().mapStream(data => data.channels);
+    }
+}

+ 27 - 0
admin-ui/src/app/settings/providers/routing/channel-resolver.ts

@@ -0,0 +1,27 @@
+import { Injectable } from '@angular/core';
+import { Channel } from 'shared/generated-types';
+
+import { BaseEntityResolver } from '../../../common/base-entity-resolver';
+import { getDefaultLanguage } from '../../../common/utilities/get-default-language';
+import { DataService } from '../../../data/providers/data.service';
+
+/**
+ * Resolves the id from the path into a Customer entity.
+ */
+@Injectable()
+export class ChannelResolver extends BaseEntityResolver<Channel.Fragment> {
+    constructor(private dataService: DataService) {
+        super(
+            {
+                __typename: 'Channel',
+                id: '',
+                code: '',
+                token: '',
+                defaultLanguageCode: getDefaultLanguage(),
+                defaultShippingZone: {} as any,
+                defaultTaxZone: {} as any,
+            },
+            id => this.dataService.settings.getChannel(id).mapStream(data => data.channel),
+        );
+    }
+}

+ 13 - 1
admin-ui/src/app/settings/settings.module.ts

@@ -5,6 +5,8 @@ import { SharedModule } from '../shared/shared.module';
 
 import { AdminDetailComponent } from './components/admin-detail/admin-detail.component';
 import { AdministratorListComponent } from './components/administrator-list/administrator-list.component';
+import { ChannelDetailComponent } from './components/channel-detail/channel-detail.component';
+import { ChannelListComponent } from './components/channel-list/channel-list.component';
 import { CountryDetailComponent } from './components/country-detail/country-detail.component';
 import { CountryListComponent } from './components/country-list/country-list.component';
 import { PermissionGridComponent } from './components/permission-grid/permission-grid.component';
@@ -16,6 +18,7 @@ import { TaxRateDetailComponent } from './components/tax-rate-detail/tax-rate-de
 import { TaxRateListComponent } from './components/tax-rate-list/tax-rate-list.component';
 import { ZoneSelectorDialogComponent } from './components/zone-selector-dialog/zone-selector-dialog.component';
 import { AdministratorResolver } from './providers/routing/administrator-resolver';
+import { ChannelResolver } from './providers/routing/channel-resolver';
 import { CountryResolver } from './providers/routing/country-resolver';
 import { RoleResolver } from './providers/routing/role-resolver';
 import { TaxCategoryResolver } from './providers/routing/tax-category-resolver';
@@ -37,8 +40,17 @@ import { settingsRoutes } from './settings.routes';
         ZoneSelectorDialogComponent,
         TaxRateListComponent,
         TaxRateDetailComponent,
+        ChannelListComponent,
+        ChannelDetailComponent,
     ],
     entryComponents: [ZoneSelectorDialogComponent],
-    providers: [TaxCategoryResolver, AdministratorResolver, RoleResolver, CountryResolver, TaxRateResolver],
+    providers: [
+        TaxCategoryResolver,
+        AdministratorResolver,
+        RoleResolver,
+        CountryResolver,
+        TaxRateResolver,
+        ChannelResolver,
+    ],
 })
 export class SettingsModule {}

+ 27 - 1
admin-ui/src/app/settings/settings.routes.ts

@@ -1,5 +1,5 @@
 import { Route } from '@angular/router';
-import { Administrator, Country, GetCountry, Role, TaxCategory, TaxRate } from 'shared/generated-types';
+import { Administrator, Channel, Country, Role, TaxCategory, TaxRate } from 'shared/generated-types';
 
 import { createResolveData } from '../common/base-entity-resolver';
 import { detailBreadcrumb } from '../common/detail-breadcrumb';
@@ -7,6 +7,8 @@ import { _ } from '../core/providers/i18n/mark-for-extraction';
 
 import { AdminDetailComponent } from './components/admin-detail/admin-detail.component';
 import { AdministratorListComponent } from './components/administrator-list/administrator-list.component';
+import { ChannelDetailComponent } from './components/channel-detail/channel-detail.component';
+import { ChannelListComponent } from './components/channel-list/channel-list.component';
 import { CountryDetailComponent } from './components/country-detail/country-detail.component';
 import { CountryListComponent } from './components/country-list/country-list.component';
 import { RoleDetailComponent } from './components/role-detail/role-detail.component';
@@ -16,6 +18,7 @@ import { TaxCategoryListComponent } from './components/tax-category-list/tax-cat
 import { TaxRateDetailComponent } from './components/tax-rate-detail/tax-rate-detail.component';
 import { TaxRateListComponent } from './components/tax-rate-list/tax-rate-list.component';
 import { AdministratorResolver } from './providers/routing/administrator-resolver';
+import { ChannelResolver } from './providers/routing/channel-resolver';
 import { CountryResolver } from './providers/routing/country-resolver';
 import { RoleResolver } from './providers/routing/role-resolver';
 import { TaxCategoryResolver } from './providers/routing/tax-category-resolver';
@@ -35,6 +38,19 @@ export const settingsRoutes: Route[] = [
         resolve: createResolveData(AdministratorResolver),
         data: { breadcrumb: administratorBreadcrumb },
     },
+    {
+        path: 'channels',
+        component: ChannelListComponent,
+        data: {
+            breadcrumb: _('breadcrumb.channels'),
+        },
+    },
+    {
+        path: 'channels/:id',
+        component: ChannelDetailComponent,
+        resolve: createResolveData(ChannelResolver),
+        data: { breadcrumb: channelBreadcrumb },
+    },
     {
         path: 'roles',
         component: RoleListComponent,
@@ -105,6 +121,16 @@ export function administratorBreadcrumb(data: any, params: any) {
     });
 }
 
+export function channelBreadcrumb(data: any, params: any) {
+    return detailBreadcrumb<Channel>({
+        entity: data.entity,
+        id: params.id,
+        breadcrumbKey: 'breadcrumb.channels',
+        getName: channel => channel.code,
+        route: 'channels',
+    });
+}
+
 export function roleBreadcrumb(data: any, params: any) {
     return detailBreadcrumb<Role>({
         entity: data.entity,

+ 6 - 1
admin-ui/src/i18n-messages/en.json

@@ -5,6 +5,7 @@
   "breadcrumb": {
     "administrators": "Administrators",
     "assets": "Assets",
+    "channels": "Channels",
     "countries": "Countries",
     "dashboard": "Dashboard",
     "facets": "Facets",
@@ -13,7 +14,7 @@
     "promotions": "Promotions",
     "roles": "Roles",
     "tax-categories": "Tax categories",
-    "tax-rates": ""
+    "tax-rates": "Tax rates"
   },
   "catalog": {
     "add-asset": "Add asset",
@@ -113,6 +114,7 @@
     "assets": "Assets",
     "catalog": "Catalog",
     "categories": "Categories",
+    "channels": "Channels",
     "countries": "Countries",
     "facets": "Facets",
     "marketing": "Marketing",
@@ -134,12 +136,15 @@
     "administrator": "Administrator",
     "catalog": "Catalog",
     "create": "Create",
+    "create-new-channel": "Create new channel",
     "create-new-country": "Create new country",
     "create-new-role": "Create new role",
     "create-new-tax-category": "Create tax category",
     "create-new-tax-rate": "Create new tax rate",
     "create-zone": "Create zone",
     "customer": "Customer",
+    "default-shipping-zone": "Default shipping zone",
+    "default-tax-zone": "Default tax zone",
     "delete": "Delete",
     "description": "Description",
     "email-address": "Email address",

Plik diff jest za duży
+ 0 - 0
schema.json


+ 1 - 1
server/mock-data/data-sources/countries.json

@@ -3034,7 +3034,7 @@
     "alpha-3": "GBR",
     "country-code": "826",
     "iso_3166-2": "ISO 3166-2:GB",
-    "region": "Europe",
+    "region": "UK",
     "sub-region": "Northern Europe",
     "intermediate-region": "",
     "region-code": "150",

+ 70 - 23
server/mock-data/mock-data.service.ts

@@ -5,20 +5,25 @@ import * as path from 'path';
 import {
     AddOptionGroupToProduct,
     Asset,
-    Country,
+    Channel,
     CreateAddressInput,
+    CreateChannel,
     CreateCountry,
     CreateCustomerInput,
     CreateFacet,
     CreateFacetValueWithFacetInput,
     CreateProduct,
     CreateProductOptionGroup,
+    CreateTaxRate,
     CreateZone,
     GenerateProductVariants,
+    GetChannels,
     LanguageCode,
     ProductTranslationInput,
     ProductVariant,
+    UpdateChannel,
     UpdateProductVariants,
+    Zone,
 } from 'shared/generated-types';
 
 import { CREATE_FACET } from '../../admin-ui/src/app/data/definitions/facet-definitions';
@@ -29,10 +34,14 @@ import {
     GENERATE_PRODUCT_VARIANTS,
     UPDATE_PRODUCT_VARIANTS,
 } from '../../admin-ui/src/app/data/definitions/product-definitions';
-import { CREATE_COUNTRY, CREATE_ZONE } from '../../admin-ui/src/app/data/definitions/settings-definitions';
-import { taxAction } from '../src/config/adjustment/required-adjustment-actions';
-import { taxCondition } from '../src/config/adjustment/required-adjustment-conditions';
-import { Channel } from '../src/entity/channel/channel.entity';
+import {
+    CREATE_CHANNEL,
+    CREATE_COUNTRY,
+    CREATE_TAX_RATE,
+    CREATE_ZONE,
+    GET_CHANNELS,
+    UPDATE_CHANNEL,
+} from '../../admin-ui/src/app/data/definitions/settings-definitions';
 import { Customer } from '../src/entity/customer/customer.entity';
 
 import { SimpleGraphQLClient } from './simple-graphql-client';
@@ -50,25 +59,26 @@ export class MockDataService {
         faker.seed(1);
     }
 
-    async populateChannels(channelCodes: string[]): Promise<Channel[]> {
-        const channels: Channel[] = [];
+    async populateChannels(channelCodes: string[]): Promise<Channel.Fragment[]> {
+        const channels: Channel.Fragment[] = [];
         for (const code of channelCodes) {
-            const channel = await this.client.query<any>(gql`
-                mutation {
-                    createChannel(code: "${code}") {
-                        id
-                        code
-                        token
-                    }
-                }
-            `);
+            const channel = await this.client.query<CreateChannel.Mutation, CreateChannel.Variables>(
+                CREATE_CHANNEL,
+                {
+                    input: {
+                        code,
+                        token: `${code}_token`,
+                        defaultLanguageCode: LanguageCode.en,
+                    },
+                },
+            );
             channels.push(channel.createChannel);
             this.log(`Created Channel: ${channel.createChannel.code}`);
         }
         return channels;
     }
 
-    async populateCountries() {
+    async populateCountries(): Promise<Zone.Fragment[]> {
         const countriesFile = await fs.readFile(
             path.join(__dirname, 'data-sources', 'countries.json'),
             'utf8',
@@ -91,15 +101,38 @@ export class MockDataService {
             }
             zones[country.region].push(result.createCountry.id);
         }
+
+        const createdZones: Zone.Fragment[] = [];
         for (const [name, memberIds] of Object.entries(zones)) {
-            await this.client.query<CreateZone.Mutation, CreateZone.Variables>(CREATE_ZONE, {
+            const result = await this.client.query<CreateZone.Mutation, CreateZone.Variables>(CREATE_ZONE, {
                 input: {
                     name,
                     memberIds,
                 },
             });
+            createdZones.push(result.createZone);
         }
         this.log(`Created ${countries.length} Countries in ${Object.keys(zones).length} Zones`);
+        return createdZones;
+    }
+
+    async setChannelDefaultZones(zones: Zone.Fragment[]) {
+        const defaultZone = zones.find(z => z.name === 'UK');
+        if (!defaultZone) {
+            this.log(`Default zone could not be found`);
+            return;
+        }
+        const result = await this.client.query<GetChannels.Query>(GET_CHANNELS);
+        for (const channel of result.channels) {
+            await this.client.query<UpdateChannel.Mutation, UpdateChannel.Variables>(UPDATE_CHANNEL, {
+                input: {
+                    id: channel.id,
+                    defaultTaxZoneId: defaultZone.id,
+                    defaultShippingZoneId: defaultZone.id,
+                },
+            });
+        }
+        this.log(`Set default zones for ${result.channels.length} Channels`);
     }
 
     async populateOptions(): Promise<string> {
@@ -138,10 +171,10 @@ export class MockDataService {
             });
     }
 
-    async populateTaxCategories() {
+    async populateTaxCategories(zones: Zone.Fragment[]) {
         const taxCategories = [{ name: 'Standard Tax' }, { name: 'Reduced Tax' }, { name: 'Zero Tax' }];
 
-        const results: TaxCategory[] = [];
+        const createdTaxCategories: TaxCategory[] = [];
 
         for (const category of taxCategories) {
             const result = await this.client.query(
@@ -158,10 +191,24 @@ export class MockDataService {
                     },
                 },
             );
-            results.push(result.createTaxCategory);
+            createdTaxCategories.push(result.createTaxCategory);
         }
-        this.log(`Created ${results.length} tax categories`);
-        return results;
+        this.log(`Created ${createdTaxCategories.length} tax categories`);
+
+        // create tax rates
+        for (const zone of zones) {
+            await this.client.query<CreateTaxRate.Mutation, CreateTaxRate.Variables>(CREATE_TAX_RATE, {
+                input: {
+                    name: `Standard Tax for ${zone.name}`,
+                    enabled: true,
+                    value: 20,
+                    categoryId: createdTaxCategories[0].id,
+                    zoneId: zone.id,
+                },
+            });
+        }
+
+        return createdTaxCategories;
     }
 
     async populateCustomers(count: number = 5): Promise<any> {

+ 5 - 5
server/mock-data/populate.ts

@@ -1,8 +1,8 @@
 import { INestApplication } from '@nestjs/common';
+import { Channel } from 'shared/generated-types';
 
 import { VendureBootstrapFunction } from '../src/bootstrap';
 import { setConfig, VendureConfig } from '../src/config/vendure-config';
-import { Channel } from '../src/entity/channel/channel.entity';
 
 import { clearAllTables } from './clear-all-tables';
 import { getDefaultChannelToken } from './get-default-channel-token';
@@ -35,14 +35,14 @@ export async function populate(
     client.setChannelToken(defaultChannelToken);
     await client.asSuperAdmin();
     const mockDataService = new MockDataService(client, logging);
-    let channels: Channel[] = [];
     if (options.channels) {
-        channels = await mockDataService.populateChannels(options.channels);
+        await mockDataService.populateChannels(options.channels);
     }
-    await mockDataService.populateCountries();
+    const zones = await mockDataService.populateCountries();
+    await mockDataService.setChannelDefaultZones(zones);
     const assets = await mockDataService.populateAssets();
     const optionGroupId = await mockDataService.populateOptions();
-    const taxCategories = await mockDataService.populateTaxCategories();
+    const taxCategories = await mockDataService.populateTaxCategories(zones);
     await mockDataService.populateProducts(options.productCount, optionGroupId, assets, taxCategories);
     await mockDataService.populateCustomers(options.customerCount);
     await mockDataService.populateFacets();

+ 29 - 4
server/src/api/resolvers/channel.resolver.ts

@@ -1,17 +1,42 @@
-import { Args, Mutation, Resolver } from '@nestjs/graphql';
-import { CreateChannelMutationArgs, Permission } from 'shared/generated-types';
+import { Args, Mutation, Query, Resolver } from '@nestjs/graphql';
+import {
+    ChannelQueryArgs,
+    CreateChannelMutationArgs,
+    Permission,
+    UpdateChannelMutationArgs,
+} from 'shared/generated-types';
 
 import { Channel } from '../../entity/channel/channel.entity';
 import { ChannelService } from '../../service/providers/channel.service';
 import { Allow } from '../common/auth-guard';
+import { RequestContext } from '../common/request-context';
+import { Ctx } from '../common/request-context.decorator';
 
 @Resolver('Channel')
 export class ChannelResolver {
     constructor(private channelService: ChannelService) {}
 
+    @Query()
+    @Allow(Permission.SuperAdmin)
+    channels(@Ctx() ctx: RequestContext): Promise<Channel[]> {
+        return this.channelService.findAll();
+    }
+
+    @Query()
+    @Allow(Permission.SuperAdmin)
+    async channel(@Ctx() ctx: RequestContext, @Args() args: ChannelQueryArgs): Promise<Channel | undefined> {
+        return this.channelService.findOne(args.id);
+    }
+
+    @Mutation()
+    @Allow(Permission.SuperAdmin)
+    async createChannel(@Args() args: CreateChannelMutationArgs): Promise<Channel> {
+        return this.channelService.create(args.input);
+    }
+
     @Mutation()
     @Allow(Permission.SuperAdmin)
-    createChannel(@Args() args: CreateChannelMutationArgs): Promise<Channel> {
-        return this.channelService.create(args.code);
+    async updateChannel(@Args() args: UpdateChannelMutationArgs): Promise<Channel> {
+        return this.channelService.update(args.input);
     }
 }

+ 10 - 1
server/src/api/types/channel.api.graphql

@@ -1,3 +1,12 @@
+type Query {
+    channels: [Channel!]!
+    channel(id: ID!): Channel
+}
+
 type Mutation {
-  createChannel(code: String!): Channel!
+    "Create a new Channel"
+    createChannel(input: CreateChannelInput!): Channel!
+
+    "Update an existing Channel"
+    updateChannel(input: UpdateChannelInput!): Channel!
 }

+ 8 - 1
server/src/entity/channel/channel.entity.ts

@@ -1,8 +1,9 @@
 import { LanguageCode } from 'shared/generated-types';
 import { DeepPartial } from 'shared/shared-types';
-import { Column, Entity } from 'typeorm';
+import { Column, Entity, ManyToOne } from 'typeorm';
 
 import { VendureEntity } from '../base/base.entity';
+import { Zone } from '../zone/zone.entity';
 
 @Entity()
 export class Channel extends VendureEntity {
@@ -21,6 +22,12 @@ export class Channel extends VendureEntity {
 
     @Column('varchar') defaultLanguageCode: LanguageCode;
 
+    @ManyToOne(type => Zone)
+    defaultTaxZone: Zone;
+
+    @ManyToOne(type => Zone)
+    defaultShippingZone: Zone;
+
     private generateToken(): string {
         const randomString = () =>
             Math.random()

+ 20 - 0
server/src/entity/channel/channel.graphql

@@ -4,4 +4,24 @@ type Channel implements Node {
     updatedAt: DateTime!
     code: String!
     token: String!
+    defaultTaxZone: Zone
+    defaultShippingZone: Zone
+    defaultLanguageCode: LanguageCode!
+}
+
+input CreateChannelInput {
+    code: String!
+    token: String!
+    defaultLanguageCode: LanguageCode!
+    defaultTaxZoneId: ID
+    defaultShippingZoneId: ID
+}
+
+input UpdateChannelInput {
+    id: ID!
+    code: String
+    token: String
+    defaultLanguageCode: LanguageCode
+    defaultTaxZoneId: ID
+    defaultShippingZoneId: ID
 }

+ 52 - 6
server/src/service/providers/channel.service.ts

@@ -1,14 +1,19 @@
 import { Injectable } from '@nestjs/common';
 import { InjectConnection } from '@nestjs/typeorm';
+import { CreateChannelInput, UpdateChannelInput } from 'shared/generated-types';
 import { DEFAULT_CHANNEL_CODE } from 'shared/shared-constants';
+import { ID } from 'shared/shared-types';
 import { Connection } from 'typeorm';
 
 import { RequestContext } from '../../api/common/request-context';
 import { DEFAULT_LANGUAGE_CODE } from '../../common/constants';
 import { ChannelAware } from '../../common/types/common-types';
+import { assertFound } from '../../common/utils';
 import { ConfigService } from '../../config/config.service';
 import { Channel } from '../../entity/channel/channel.entity';
+import { Zone } from '../../entity/zone/zone.entity';
 import { I18nError } from '../../i18n/i18n-error';
+import { patchEntity } from '../helpers/patch-entity';
 
 @Injectable()
 export class ChannelService {
@@ -55,19 +60,49 @@ export class ChannelService {
     }
 
     findAll(): Promise<Channel[]> {
-        return this.connection.getRepository(Channel).find();
+        return this.connection
+            .getRepository(Channel)
+            .find({ relations: ['defaultShippingZone', 'defaultTaxZone'] });
     }
 
-    async create(code: string): Promise<Channel> {
-        const channel = new Channel({
-            code,
-            defaultLanguageCode: DEFAULT_LANGUAGE_CODE,
-        });
+    findOne(id: ID): Promise<Channel | undefined> {
+        return this.connection
+            .getRepository(Channel)
+            .findOne(id, { relations: ['defaultShippingZone', 'defaultTaxZone'] });
+    }
+
+    async create(input: CreateChannelInput): Promise<Channel> {
+        const channel = new Channel(input);
+        if (input.defaultTaxZoneId) {
+            channel.defaultTaxZone = await this.getZoneOrThrow(input.defaultTaxZoneId);
+        }
+        if (input.defaultShippingZoneId) {
+            channel.defaultShippingZone = await this.getZoneOrThrow(input.defaultShippingZoneId);
+        }
         const newChannel = await this.connection.getRepository(Channel).save(channel);
         this.allChannels.push(channel);
         return channel;
     }
 
+    async update(input: UpdateChannelInput): Promise<Channel> {
+        const channel = await this.findOne(input.id);
+        if (!channel) {
+            throw new I18nError(`error.entity-with-id-not-found`, {
+                entityName: 'Channel',
+                id: input.id,
+            });
+        }
+        const updatedChannel = patchEntity(channel, input);
+        if (input.defaultTaxZoneId) {
+            updatedChannel.defaultTaxZone = await this.getZoneOrThrow(input.defaultTaxZoneId);
+        }
+        if (input.defaultShippingZoneId) {
+            updatedChannel.defaultShippingZone = await this.getZoneOrThrow(input.defaultShippingZoneId);
+        }
+        await this.connection.getRepository(Channel).save(updatedChannel);
+        return assertFound(this.findOne(channel.id));
+    }
+
     /**
      * There must always be a default Channel. If none yet exists, this method creates one.
      * Also ensures the default Channel token matches the defaultChannelToken config setting.
@@ -92,4 +127,15 @@ export class ChannelService {
             await this.connection.manager.save(defaultChannel);
         }
     }
+
+    private async getZoneOrThrow(id: ID): Promise<Zone> {
+        const zone = await this.connection.getRepository(Zone).findOne(id);
+        if (!zone) {
+            throw new I18nError(`error.entity-with-id-not-found`, {
+                entityName: 'Zone',
+                id,
+            });
+        }
+        return zone;
+    }
 }

+ 201 - 53
shared/generated-types.ts

@@ -44,6 +44,8 @@ export interface Query {
     assets: AssetList;
     asset?: Asset | null;
     me?: CurrentUser | null;
+    channels: Channel[];
+    channel?: Channel | null;
     config: Config;
     countries: CountryList;
     country?: Country | null;
@@ -118,6 +120,24 @@ export interface Channel extends Node {
     updatedAt: DateTime;
     code: string;
     token: string;
+    defaultTaxZone: Zone;
+    defaultShippingZone: Zone;
+    defaultLanguageCode: LanguageCode;
+}
+
+export interface Zone extends Node {
+    id: string;
+    createdAt: DateTime;
+    updatedAt: DateTime;
+    name: string;
+    members: Country[];
+}
+
+export interface Country extends Node {
+    id: string;
+    code: string;
+    name: string;
+    enabled: boolean;
 }
 
 export interface AssetList extends PaginatedList {
@@ -150,13 +170,6 @@ export interface CountryList extends PaginatedList {
     totalItems: number;
 }
 
-export interface Country extends Node {
-    id: string;
-    code: string;
-    name: string;
-    enabled: boolean;
-}
-
 export interface CustomerGroup extends Node {
     id: string;
     createdAt: DateTime;
@@ -274,6 +287,7 @@ export interface OrderLine extends Node {
     quantity: number;
     items: OrderItem[];
     totalPrice: number;
+    adjustments: Adjustment[];
     order: Order;
 }
 
@@ -459,14 +473,6 @@ export interface TaxRate extends Node {
     customerGroup?: CustomerGroup | null;
 }
 
-export interface Zone extends Node {
-    id: string;
-    createdAt: DateTime;
-    updatedAt: DateTime;
-    name: string;
-    members: Country[];
-}
-
 export interface NetworkStatus {
     inFlightRequests: number;
 }
@@ -489,6 +495,7 @@ export interface Mutation {
     login: LoginResult;
     logout: boolean;
     createChannel: Channel;
+    updateChannel: Channel;
     createCountry: Country;
     updateCountry: Country;
     createCustomerGroup: CustomerGroup;
@@ -816,6 +823,23 @@ export interface CreateAssetInput {
     file: Upload;
 }
 
+export interface CreateChannelInput {
+    code: string;
+    token: string;
+    defaultLanguageCode: LanguageCode;
+    defaultTaxZoneId?: string | null;
+    defaultShippingZoneId?: string | null;
+}
+
+export interface UpdateChannelInput {
+    id: string;
+    code?: string | null;
+    token?: string | null;
+    defaultLanguageCode?: LanguageCode | null;
+    defaultTaxZoneId?: string | null;
+    defaultShippingZoneId?: string | null;
+}
+
 export interface CreateCountryInput {
     code: string;
     name: string;
@@ -1120,6 +1144,9 @@ export interface AssetsQueryArgs {
 export interface AssetQueryArgs {
     id: string;
 }
+export interface ChannelQueryArgs {
+    id: string;
+}
 export interface CountriesQueryArgs {
     options?: CountryListOptions | null;
 }
@@ -1208,7 +1235,10 @@ export interface LoginMutationArgs {
     rememberMe?: boolean | null;
 }
 export interface CreateChannelMutationArgs {
-    code: string;
+    input: CreateChannelInput;
+}
+export interface UpdateChannelMutationArgs {
+    input: UpdateChannelInput;
 }
 export interface CreateCountryMutationArgs {
     input: CreateCountryInput;
@@ -1376,12 +1406,6 @@ export enum Permission {
     DeleteSettings = 'DeleteSettings',
 }
 
-export enum AssetType {
-    IMAGE = 'IMAGE',
-    VIDEO = 'VIDEO',
-    BINARY = 'BINARY',
-}
-
 export enum LanguageCode {
     aa = 'aa',
     ab = 'ab',
@@ -1569,6 +1593,12 @@ export enum LanguageCode {
     zu = 'zu',
 }
 
+export enum AssetType {
+    IMAGE = 'IMAGE',
+    VIDEO = 'VIDEO',
+    BINARY = 'BINARY',
+}
+
 export namespace QueryResolvers {
     export interface Resolvers<Context = any> {
         administrators?: AdministratorsResolver<AdministratorList, any, Context>;
@@ -1576,6 +1606,8 @@ export namespace QueryResolvers {
         assets?: AssetsResolver<AssetList, any, Context>;
         asset?: AssetResolver<Asset | null, any, Context>;
         me?: MeResolver<CurrentUser | null, any, Context>;
+        channels?: ChannelsResolver<Channel[], any, Context>;
+        channel?: ChannelResolver<Channel | null, any, Context>;
         config?: ConfigResolver<Config, any, Context>;
         countries?: CountriesResolver<CountryList, any, Context>;
         country?: CountryResolver<Country | null, any, Context>;
@@ -1653,6 +1685,17 @@ export namespace QueryResolvers {
         Parent,
         Context
     >;
+    export type ChannelsResolver<R = Channel[], Parent = any, Context = any> = Resolver<R, Parent, Context>;
+    export type ChannelResolver<R = Channel | null, Parent = any, Context = any> = Resolver<
+        R,
+        Parent,
+        Context,
+        ChannelArgs
+    >;
+    export interface ChannelArgs {
+        id: string;
+    }
+
     export type ConfigResolver<R = Config, Parent = any, Context = any> = Resolver<R, Parent, Context>;
     export type CountriesResolver<R = CountryList, Parent = any, Context = any> = Resolver<
         R,
@@ -1998,6 +2041,9 @@ export namespace ChannelResolvers {
         updatedAt?: UpdatedAtResolver<DateTime, any, Context>;
         code?: CodeResolver<string, any, Context>;
         token?: TokenResolver<string, any, Context>;
+        defaultTaxZone?: DefaultTaxZoneResolver<Zone, any, Context>;
+        defaultShippingZone?: DefaultShippingZoneResolver<Zone, any, Context>;
+        defaultLanguageCode?: DefaultLanguageCodeResolver<LanguageCode, any, Context>;
     }
 
     export type IdResolver<R = string, Parent = any, Context = any> = Resolver<R, Parent, Context>;
@@ -2005,6 +2051,47 @@ export namespace ChannelResolvers {
     export type UpdatedAtResolver<R = DateTime, Parent = any, Context = any> = Resolver<R, Parent, Context>;
     export type CodeResolver<R = string, Parent = any, Context = any> = Resolver<R, Parent, Context>;
     export type TokenResolver<R = string, Parent = any, Context = any> = Resolver<R, Parent, Context>;
+    export type DefaultTaxZoneResolver<R = Zone, Parent = any, Context = any> = Resolver<R, Parent, Context>;
+    export type DefaultShippingZoneResolver<R = Zone, Parent = any, Context = any> = Resolver<
+        R,
+        Parent,
+        Context
+    >;
+    export type DefaultLanguageCodeResolver<R = LanguageCode, Parent = any, Context = any> = Resolver<
+        R,
+        Parent,
+        Context
+    >;
+}
+
+export namespace ZoneResolvers {
+    export interface Resolvers<Context = any> {
+        id?: IdResolver<string, any, Context>;
+        createdAt?: CreatedAtResolver<DateTime, any, Context>;
+        updatedAt?: UpdatedAtResolver<DateTime, any, Context>;
+        name?: NameResolver<string, any, Context>;
+        members?: MembersResolver<Country[], any, Context>;
+    }
+
+    export type IdResolver<R = string, Parent = any, Context = any> = Resolver<R, Parent, Context>;
+    export type CreatedAtResolver<R = DateTime, Parent = any, Context = any> = Resolver<R, Parent, Context>;
+    export type UpdatedAtResolver<R = DateTime, Parent = any, Context = any> = Resolver<R, Parent, Context>;
+    export type NameResolver<R = string, Parent = any, Context = any> = Resolver<R, Parent, Context>;
+    export type MembersResolver<R = Country[], Parent = any, Context = any> = Resolver<R, Parent, Context>;
+}
+
+export namespace CountryResolvers {
+    export interface Resolvers<Context = any> {
+        id?: IdResolver<string, any, Context>;
+        code?: CodeResolver<string, any, Context>;
+        name?: NameResolver<string, any, Context>;
+        enabled?: EnabledResolver<boolean, any, Context>;
+    }
+
+    export type IdResolver<R = string, Parent = any, Context = any> = Resolver<R, Parent, Context>;
+    export type CodeResolver<R = string, Parent = any, Context = any> = Resolver<R, Parent, Context>;
+    export type NameResolver<R = string, Parent = any, Context = any> = Resolver<R, Parent, Context>;
+    export type EnabledResolver<R = boolean, Parent = any, Context = any> = Resolver<R, Parent, Context>;
 }
 
 export namespace AssetListResolvers {
@@ -2075,20 +2162,6 @@ export namespace CountryListResolvers {
     export type TotalItemsResolver<R = number, Parent = any, Context = any> = Resolver<R, Parent, Context>;
 }
 
-export namespace CountryResolvers {
-    export interface Resolvers<Context = any> {
-        id?: IdResolver<string, any, Context>;
-        code?: CodeResolver<string, any, Context>;
-        name?: NameResolver<string, any, Context>;
-        enabled?: EnabledResolver<boolean, any, Context>;
-    }
-
-    export type IdResolver<R = string, Parent = any, Context = any> = Resolver<R, Parent, Context>;
-    export type CodeResolver<R = string, Parent = any, Context = any> = Resolver<R, Parent, Context>;
-    export type NameResolver<R = string, Parent = any, Context = any> = Resolver<R, Parent, Context>;
-    export type EnabledResolver<R = boolean, Parent = any, Context = any> = Resolver<R, Parent, Context>;
-}
-
 export namespace CustomerGroupResolvers {
     export interface Resolvers<Context = any> {
         id?: IdResolver<string, any, Context>;
@@ -2418,6 +2491,7 @@ export namespace OrderLineResolvers {
         quantity?: QuantityResolver<number, any, Context>;
         items?: ItemsResolver<OrderItem[], any, Context>;
         totalPrice?: TotalPriceResolver<number, any, Context>;
+        adjustments?: AdjustmentsResolver<Adjustment[], any, Context>;
         order?: OrderResolver<Order, any, Context>;
     }
 
@@ -2438,6 +2512,11 @@ export namespace OrderLineResolvers {
     export type QuantityResolver<R = number, Parent = any, Context = any> = Resolver<R, Parent, Context>;
     export type ItemsResolver<R = OrderItem[], Parent = any, Context = any> = Resolver<R, Parent, Context>;
     export type TotalPriceResolver<R = number, Parent = any, Context = any> = Resolver<R, Parent, Context>;
+    export type AdjustmentsResolver<R = Adjustment[], Parent = any, Context = any> = Resolver<
+        R,
+        Parent,
+        Context
+    >;
     export type OrderResolver<R = Order, Parent = any, Context = any> = Resolver<R, Parent, Context>;
 }
 
@@ -2937,22 +3016,6 @@ export namespace TaxRateResolvers {
     >;
 }
 
-export namespace ZoneResolvers {
-    export interface Resolvers<Context = any> {
-        id?: IdResolver<string, any, Context>;
-        createdAt?: CreatedAtResolver<DateTime, any, Context>;
-        updatedAt?: UpdatedAtResolver<DateTime, any, Context>;
-        name?: NameResolver<string, any, Context>;
-        members?: MembersResolver<Country[], any, Context>;
-    }
-
-    export type IdResolver<R = string, Parent = any, Context = any> = Resolver<R, Parent, Context>;
-    export type CreatedAtResolver<R = DateTime, Parent = any, Context = any> = Resolver<R, Parent, Context>;
-    export type UpdatedAtResolver<R = DateTime, Parent = any, Context = any> = Resolver<R, Parent, Context>;
-    export type NameResolver<R = string, Parent = any, Context = any> = Resolver<R, Parent, Context>;
-    export type MembersResolver<R = Country[], Parent = any, Context = any> = Resolver<R, Parent, Context>;
-}
-
 export namespace NetworkStatusResolvers {
     export interface Resolvers<Context = any> {
         inFlightRequests?: InFlightRequestsResolver<number, any, Context>;
@@ -2998,6 +3061,7 @@ export namespace MutationResolvers {
         login?: LoginResolver<LoginResult, any, Context>;
         logout?: LogoutResolver<boolean, any, Context>;
         createChannel?: CreateChannelResolver<Channel, any, Context>;
+        updateChannel?: UpdateChannelResolver<Channel, any, Context>;
         createCountry?: CreateCountryResolver<Country, any, Context>;
         updateCountry?: UpdateCountryResolver<Country, any, Context>;
         createCustomerGroup?: CreateCustomerGroupResolver<CustomerGroup, any, Context>;
@@ -3106,7 +3170,17 @@ export namespace MutationResolvers {
         CreateChannelArgs
     >;
     export interface CreateChannelArgs {
-        code: string;
+        input: CreateChannelInput;
+    }
+
+    export type UpdateChannelResolver<R = Channel, Parent = any, Context = any> = Resolver<
+        R,
+        Parent,
+        Context,
+        UpdateChannelArgs
+    >;
+    export interface UpdateChannelArgs {
+        input: UpdateChannelInput;
     }
 
     export type CreateCountryResolver<R = Country, Parent = any, Context = any> = Resolver<
@@ -4488,6 +4562,56 @@ export namespace UpdateTaxRate {
     export type UpdateTaxRate = TaxRate.Fragment;
 }
 
+export namespace GetChannels {
+    export type Variables = {};
+
+    export type Query = {
+        __typename?: 'Query';
+        channels: Channels[];
+    };
+
+    export type Channels = Channel.Fragment;
+}
+
+export namespace GetChannel {
+    export type Variables = {
+        id: string;
+    };
+
+    export type Query = {
+        __typename?: 'Query';
+        channel?: Channel | null;
+    };
+
+    export type Channel = Channel.Fragment;
+}
+
+export namespace CreateChannel {
+    export type Variables = {
+        input: CreateChannelInput;
+    };
+
+    export type Mutation = {
+        __typename?: 'Mutation';
+        createChannel: CreateChannel;
+    };
+
+    export type CreateChannel = Channel.Fragment;
+}
+
+export namespace UpdateChannel {
+    export type Variables = {
+        input: UpdateChannelInput;
+    };
+
+    export type Mutation = {
+        __typename?: 'Mutation';
+        updateChannel: UpdateChannel;
+    };
+
+    export type UpdateChannel = Channel.Fragment;
+}
+
 export namespace Administrator {
     export type Fragment = {
         __typename?: 'Administrator';
@@ -4815,3 +4939,27 @@ export namespace TaxRate {
         name: string;
     };
 }
+
+export namespace Channel {
+    export type Fragment = {
+        __typename?: 'Channel';
+        id: string;
+        code: string;
+        token: string;
+        defaultLanguageCode: LanguageCode;
+        defaultShippingZone: DefaultShippingZone;
+        defaultTaxZone: DefaultTaxZone;
+    };
+
+    export type DefaultShippingZone = {
+        __typename?: 'Zone';
+        id: string;
+        name: string;
+    };
+
+    export type DefaultTaxZone = {
+        __typename?: 'Zone';
+        id: string;
+        name: string;
+    };
+}

Niektóre pliki nie zostały wyświetlone z powodu dużej ilości zmienionych plików