Browse Source

feat(admin-ui): Display Collection contents in CollectionListComponent

Michael Bromley 6 years ago
parent
commit
8ffa373d67
20 changed files with 671 additions and 225 deletions
  1. 3 3
      admin-ui/src/app/catalog/components/collection-detail/collection-detail.component.ts
  2. 38 4
      admin-ui/src/app/catalog/components/collection-list/collection-list.component.html
  3. 59 0
      admin-ui/src/app/catalog/components/collection-list/collection-list.component.scss
  4. 81 8
      admin-ui/src/app/catalog/components/collection-list/collection-list.component.ts
  5. 14 3
      admin-ui/src/app/catalog/components/collection-tree/collection-tree-node.component.html
  6. 7 4
      admin-ui/src/app/catalog/components/collection-tree/collection-tree-node.component.scss
  7. 2 1
      admin-ui/src/app/catalog/components/collection-tree/collection-tree-node.component.ts
  8. 3 2
      admin-ui/src/app/catalog/components/collection-tree/collection-tree.component.html
  9. 5 4
      admin-ui/src/app/catalog/components/collection-tree/collection-tree.component.ts
  10. 2 4
      admin-ui/src/app/catalog/components/product-detail/product-detail.component.ts
  11. 1 1
      admin-ui/src/app/catalog/providers/routing/collection-resolver.ts
  12. 1 1
      admin-ui/src/app/common/base-list.component.ts
  13. 120 0
      admin-ui/src/app/data/definitions/collection-definitions.ts
  14. 0 101
      admin-ui/src/app/data/definitions/product-definitions.ts
  15. 112 0
      admin-ui/src/app/data/providers/collection-data.service.ts
  16. 3 0
      admin-ui/src/app/data/providers/data.service.ts
  17. 0 84
      admin-ui/src/app/data/providers/product-data.service.ts
  18. 4 3
      admin-ui/src/i18n-messages/en.json
  19. 1 1
      shared/generated-shop-types.ts
  20. 215 1
      shared/generated-types.ts

+ 3 - 3
admin-ui/src/app/catalog/components/collection-detail/collection-detail.component.ts

@@ -66,7 +66,7 @@ export class CollectionDetailComponent extends BaseDetailComponent<Collection.Fr
             .mapSingle(data => data.facets.items)
             .pipe(shareReplay(1));
 
-        this.dataService.product.getCollectionFilters().single$.subscribe(res => {
+        this.dataService.collection.getCollectionFilters().single$.subscribe(res => {
             this.allFilters = res.collectionFilters;
         });
     }
@@ -126,7 +126,7 @@ export class CollectionDetailComponent extends BaseDetailComponent<Collection.Fr
                         this.detailForm,
                         languageCode,
                     ) as CreateCollectionInput;
-                    return this.dataService.product.createCollection(input);
+                    return this.dataService.collection.createCollection(input);
                 }),
             )
             .subscribe(
@@ -156,7 +156,7 @@ export class CollectionDetailComponent extends BaseDetailComponent<Collection.Fr
                         this.detailForm,
                         languageCode,
                     ) as UpdateCollectionInput;
-                    return this.dataService.product.updateCollection(input);
+                    return this.dataService.collection.updateCollection(input);
                 }),
             )
             .subscribe(

+ 38 - 4
admin-ui/src/app/catalog/components/collection-list/collection-list.component.html

@@ -6,8 +6,42 @@
         </a>
     </vdr-ab-right>
 </vdr-action-bar>
+<div class="collection-wrapper">
+    <vdr-collection-tree
+        [collections]="items$ | async"
+        [activeCollectionId]="activeCollectionId$ | async"
+        (rearrange)="onRearrange($event)"
+    ></vdr-collection-tree>
 
-<vdr-collection-tree
-    [productCategories]="items$ | async"
-    (rearrange)="onRearrange($event)"
-></vdr-collection-tree>
+    <div class="collection-contents" [class.expanded]="activeCollectionId$ | async">
+        <div class="contents-header">
+            <div class="collection-title">
+                {{ activeCollectionTitle$ | async }} ({{
+                    'common.results-count' | translate: { count: contentsTotalItems$ | async }
+                }})
+            </div>
+            <button type="button" class="close-button" (click)="closeContents()">
+                <clr-icon shape="close"></clr-icon>
+            </button>
+        </div>
+        <vdr-data-table
+            [items]="contents$ | async"
+            [itemsPerPage]="contentsItemsPerPage$ | async"
+            [totalItems]="contentsTotalItems$ | async"
+            [currentPage]="contentsCurrentPage$ | async"
+            (pageChange)="setContentsPageNumber($event)"
+            (itemsPerPageChange)="setContentsItemsPerPage($event)"
+        >
+            <ng-template let-variant="item">
+                <td class="left align-middle">{{ variant.name }}</td>
+                <td class="right align-middle">
+                    <vdr-table-row-action
+                        iconShape="edit"
+                        [label]="'common.edit' | translate"
+                        [linkTo]="['../products', variant.productId, { tab: 'variants' }]"
+                    ></vdr-table-row-action>
+                </td>
+            </ng-template>
+        </vdr-data-table>
+    </div>
+</div>

+ 59 - 0
admin-ui/src/app/catalog/components/collection-list/collection-list.component.scss

@@ -0,0 +1,59 @@
+@import "variables";
+
+:host {
+    height: 100%;
+    display: flex;
+    flex-direction: column;
+}
+
+.collection-wrapper {
+    display: flex;
+    height: 100%;
+
+    vdr-collection-tree {
+        flex: 1;
+    }
+
+    .collection-contents {
+
+        height: 100%;
+        width: 0;
+        opacity: 0;
+        visibility: hidden;
+        overflow: auto;
+        transition: width 0.3s, opacity 0.2s 0.3s, visibility 0s 0.3s;
+
+        &.expanded {
+            width: 30vw;
+            visibility: visible;
+            opacity: 1;
+            padding-left: 12px;
+        }
+
+        .contents-header {
+            display: flex;
+            justify-content: space-between;
+            align-items: center;
+            background-color: $color-grey-1;
+            position: sticky;
+            top: 0;
+            padding: 6px;
+            z-index: 1;
+            border-bottom: 1px solid $color-grey-3;
+
+            .collection-title {
+
+            }
+
+            .close-button {
+                margin: 0;
+                background: none;
+                border: none;
+                cursor: pointer;
+            }
+        }
+        ::ng-deep table {
+            margin-top: -1px;
+        }
+    }
+}

+ 81 - 8
admin-ui/src/app/catalog/components/collection-list/collection-list.component.ts

@@ -1,6 +1,8 @@
-import { ChangeDetectionStrategy, Component } from '@angular/core';
+import { ChangeDetectionStrategy, Component, OnInit } from '@angular/core';
 import { ActivatedRoute, Router } from '@angular/router';
-import { GetCollectionList } from 'shared/generated-types';
+import { combineLatest, Observable, of } from 'rxjs';
+import { distinctUntilChanged, map, startWith, switchMap, takeUntil } from 'rxjs/operators';
+import { GetCollectionContents, GetCollectionList } from 'shared/generated-types';
 
 import { BaseListComponent } from '../../../common/base-list.component';
 import { _ } from '../../../core/providers/i18n/mark-for-extraction';
@@ -14,10 +16,16 @@ import { RearrangeEvent } from '../collection-tree/collection-tree.component';
     styleUrls: ['./collection-list.component.scss'],
     changeDetection: ChangeDetectionStrategy.OnPush,
 })
-export class CollectionListComponent extends BaseListComponent<
-    GetCollectionList.Query,
-    GetCollectionList.Items
-> {
+export class CollectionListComponent
+    extends BaseListComponent<GetCollectionList.Query, GetCollectionList.Items>
+    implements OnInit {
+    contents$: Observable<GetCollectionContents.Items[]>;
+    contentsTotalItems$: Observable<number>;
+    contentsItemsPerPage$: Observable<number>;
+    contentsCurrentPage$: Observable<number>;
+    activeCollectionId$: Observable<string | null>;
+    activeCollectionTitle$: Observable<string>;
+
     constructor(
         private dataService: DataService,
         private notificationService: NotificationService,
@@ -26,13 +34,59 @@ export class CollectionListComponent extends BaseListComponent<
     ) {
         super(router, route);
         super.setQueryFn(
-            (...args: any[]) => this.dataService.product.getCollections(99999, 0),
+            (...args: any[]) => this.dataService.collection.getCollections(99999, 0),
             data => data.collections,
         );
     }
 
+    ngOnInit() {
+        super.ngOnInit();
+
+        this.activeCollectionId$ = this.route.paramMap.pipe(
+            map(pm => pm.get('contents')),
+            distinctUntilChanged(),
+        );
+        this.contentsCurrentPage$ = this.route.paramMap.pipe(
+            map(qpm => qpm.get('contentsPage')),
+            map(page => (!page ? 1 : +page)),
+            startWith(1),
+            distinctUntilChanged(),
+        );
+        this.contentsItemsPerPage$ = this.route.paramMap.pipe(
+            map(qpm => qpm.get('contentsPerPage')),
+            map(perPage => (!perPage ? 10 : +perPage)),
+            startWith(10),
+            distinctUntilChanged(),
+        );
+
+        const collection$ = combineLatest(
+            this.activeCollectionId$,
+            this.contentsCurrentPage$,
+            this.contentsItemsPerPage$,
+        ).pipe(
+            takeUntil(this.destroy$),
+            switchMap(([id, currentPage, itemsPerPage]) => {
+                if (id) {
+                    const take = itemsPerPage;
+                    const skip = (currentPage - 1) * itemsPerPage;
+                    return this.dataService.collection
+                        .getCollectionContents(id, take, skip)
+                        .mapSingle(data => data.collection);
+                } else {
+                    return of(null);
+                }
+            }),
+        );
+
+        this.contents$ = collection$.pipe(map(result => (result ? result.productVariants.items : [])));
+        this.contentsTotalItems$ = collection$.pipe(
+            map(result => (result ? result.productVariants.totalItems : 0)),
+        );
+        this.activeCollectionTitle$ = collection$.pipe(map(result => (result ? result.name : '')));
+    }
+
     onRearrange(event: RearrangeEvent) {
-        this.dataService.product.moveCollection([event]).subscribe({
+        this.dataService.collection.moveCollection([event]).subscribe({
             next: () => {
                 this.notificationService.success(_('common.notify-saved-changes'));
                 this.refresh();
@@ -42,4 +96,23 @@ export class CollectionListComponent extends BaseListComponent<
             },
         });
     }
+
+    setContentsPageNumber(page: number) {
+        this.setParam('contentsPage', page);
+    }
+
+    setContentsItemsPerPage(perPage: number) {
+        this.setParam('contentsPerPage', perPage);
+    }
+
+    closeContents() {
+        this.setParam('contents', null);
+    }
+
+    private setParam(key: string, value: any) {
+        this.router.navigate(['./', { ...this.route.snapshot.params, [key]: value }], {
+            relativeTo: this.route,
+            queryParamsHandling: 'merge',
+        });
+    }
 }

+ 14 - 3
admin-ui/src/app/catalog/components/collection-tree/collection-tree-node.component.html

@@ -7,12 +7,16 @@
     (cdkDropListDropped)="drop($event)"
 >
     <div
-        class="category"
+        class="collection"
         *ngFor="let collection of collectionTree.children; index as i"
         cdkDrag
         [cdkDragData]="collection"
     >
-        <div class="category-detail" [ngClass]="'depth-' + depth">
+        <div
+            class="collection-detail"
+            [ngClass]="'depth-' + depth"
+            [class.active]="collection.id === activeCollectionId"
+        >
             <div class="name">
                 <clr-icon shape="folder"></clr-icon>
                 {{ collection.name }}
@@ -25,6 +29,10 @@
                     [removable]="false"
                 ></vdr-facet-value-chip>
             </div>
+            <a class="btn btn-link btn-sm" [routerLink]="['./', { contents: collection.id }]">
+                <clr-icon shape="view-list"></clr-icon>
+                {{ 'catalog.view-contents' | translate }}
+            </a>
             <a class="btn btn-link btn-sm" [routerLink]="['./', collection.id]">
                 <clr-icon shape="edit"></clr-icon>
                 {{ 'common.edit' | translate }}
@@ -71,6 +79,9 @@
                 </clr-dropdown-menu>
             </clr-dropdown>
         </div>
-        <vdr-collection-tree-node [collectionTree]="collection"></vdr-collection-tree-node>
+        <vdr-collection-tree-node
+            [collectionTree]="collection"
+            [activeCollectionId]="activeCollectionId"
+        ></vdr-collection-tree-node>
     </div>
 </div>

+ 7 - 4
admin-ui/src/app/catalog/components/collection-tree/collection-tree-node.component.scss

@@ -3,18 +3,22 @@
 :host {
     display: block;
 }
-.category {
+.collection {
     background-color: white;
     color: rgba(0, 0, 0, 0.87);
     transition: transform 250ms cubic-bezier(0, 0, 0.2, 1);
 
-    .category-detail {
-        padding: 12px;
+    .collection-detail {
+        padding: 6px 12px;
         display: flex;
         align-items: center;
         justify-content: space-between;
         border-bottom: 1px solid $color-grey-2;
 
+        &.active {
+            background-color: $color-grey-2;
+        }
+
         &.depth-1 { padding-left: 12px + 24px; }
         &.depth-2 { padding-left: 12px + 48px; }
         &.depth-3 { padding-left: 12px + 72px; }
@@ -29,7 +33,6 @@
 .tree-node {
     display: block;
     background: white;
-    border-radius: 4px;
     overflow: hidden;
 }
 

+ 2 - 1
admin-ui/src/app/catalog/components/collection-tree/collection-tree-node.component.ts

@@ -15,6 +15,7 @@ export class CollectionTreeNodeComponent implements OnInit {
     depth = 0;
     parentName: string;
     @Input() collectionTree: TreeNode<Collection.Fragment>;
+    @Input() activeCollectionId: string;
 
     constructor(
         @SkipSelf() @Optional() private parent: CollectionTreeNodeComponent,
@@ -44,7 +45,7 @@ export class CollectionTreeNodeComponent implements OnInit {
             }
             return output;
         };
-        return visit(this.root.categoryTree, [], []);
+        return visit(this.root.collectionTree, [], []);
     }
 
     move(category: Collection.Fragment, parentId: string) {

+ 3 - 2
admin-ui/src/app/catalog/components/collection-tree/collection-tree.component.html

@@ -1,5 +1,6 @@
 <vdr-collection-tree-node
-    *ngIf="categoryTree"
+    *ngIf="collectionTree"
     cdkDropListGroup
-    [collectionTree]="categoryTree"
+    [collectionTree]="collectionTree"
+    [activeCollectionId]="activeCollectionId"
 ></vdr-collection-tree-node>

+ 5 - 4
admin-ui/src/app/catalog/components/collection-tree/collection-tree.component.ts

@@ -21,13 +21,14 @@ export type RearrangeEvent = { collectionId: string; parentId: string; index: nu
     changeDetection: ChangeDetectionStrategy.OnPush,
 })
 export class CollectionTreeComponent implements OnChanges {
-    @Input() productCategories: Collection.Fragment[];
+    @Input() collections: Collection.Fragment[];
+    @Input() activeCollectionId: string;
     @Output() rearrange = new EventEmitter<RearrangeEvent>();
-    categoryTree: RootNode<Collection.Fragment>;
+    collectionTree: RootNode<Collection.Fragment>;
 
     ngOnChanges(changes: SimpleChanges) {
-        if ('productCategories' in changes && this.productCategories) {
-            this.categoryTree = arrayToTree(this.productCategories);
+        if ('collections' in changes && this.collections) {
+            this.collectionTree = arrayToTree(this.collections);
         }
     }
 

+ 2 - 4
admin-ui/src/app/catalog/components/product-detail/product-detail.component.ts

@@ -101,7 +101,7 @@ export class ProductDetailComponent extends BaseDetailComponent<ProductWithVaria
         this.taxCategories$ = this.dataService.settings
             .getTaxCategories()
             .mapSingle(data => data.taxCategories);
-        this.activeTab$ = this.route.queryParamMap.pipe(map(qpm => qpm.get('tab') as any));
+        this.activeTab$ = this.route.paramMap.pipe(map(qpm => qpm.get('tab') as any));
 
         // FacetValues are provided initially by the nested array of the
         // Product entity, but once a fetch to get all Facets is made (as when
@@ -128,10 +128,8 @@ export class ProductDetailComponent extends BaseDetailComponent<ProductWithVaria
     }
 
     navigateToTab(tabName: TabName) {
-        this.router.navigate(['./'], {
-            queryParams: { tab: tabName },
+        this.router.navigate(['./', { tab: tabName }], {
             relativeTo: this.route,
-            queryParamsHandling: 'merge',
         });
     }
 

+ 1 - 1
admin-ui/src/app/catalog/providers/routing/collection-resolver.ts

@@ -22,7 +22,7 @@ export class CollectionResolver extends BaseEntityResolver<Collection.Fragment>
                 parent: {} as any,
                 children: null,
             },
-            id => this.dataService.product.getCollection(id).mapStream(data => data.collection),
+            id => this.dataService.collection.getCollection(id).mapStream(data => data.collection),
         );
     }
 }

+ 1 - 1
admin-ui/src/app/common/base-list.component.ts

@@ -25,7 +25,7 @@ export class BaseListComponent<ResultType, ItemType, VariableType = any> impleme
         ({ options: { skip, take } } as any);
     private refresh$ = new BehaviorSubject<undefined>(undefined);
 
-    constructor(private router: Router, private route: ActivatedRoute) {}
+    constructor(protected router: Router, protected route: ActivatedRoute) {}
 
     /**
      * Sets the fetch function for the list being implemented.

+ 120 - 0
admin-ui/src/app/data/definitions/collection-definitions.ts

@@ -0,0 +1,120 @@
+import gql from 'graphql-tag';
+
+import { ASSET_FRAGMENT } from './product-definitions';
+import { CONFIGURABLE_FRAGMENT } from './promotion-definitions';
+
+export const GET_COLLECTION_FILTERS = gql`
+    query GetCollectionFilters {
+        collectionFilters {
+            ...ConfigurableOperation
+        }
+    }
+    ${CONFIGURABLE_FRAGMENT}
+`;
+
+export const COLLECTION_FRAGMENT = gql`
+    fragment Collection on Collection {
+        id
+        name
+        description
+        languageCode
+        featuredAsset {
+            ...Asset
+        }
+        assets {
+            ...Asset
+        }
+        filters {
+            ...ConfigurableOperation
+        }
+        translations {
+            id
+            languageCode
+            name
+            description
+        }
+        parent {
+            id
+            name
+        }
+        children {
+            id
+            name
+        }
+    }
+    ${ASSET_FRAGMENT}
+    ${CONFIGURABLE_FRAGMENT}
+`;
+
+export const GET_COLLECTION_LIST = gql`
+    query GetCollectionList($options: CollectionListOptions, $languageCode: LanguageCode) {
+        collections(languageCode: $languageCode, options: $options) {
+            items {
+                id
+                name
+                description
+                featuredAsset {
+                    ...Asset
+                }
+                parent {
+                    id
+                }
+            }
+            totalItems
+        }
+    }
+    ${ASSET_FRAGMENT}
+`;
+
+export const GET_COLLECTION = gql`
+    query GetCollection($id: ID!, $languageCode: LanguageCode) {
+        collection(id: $id, languageCode: $languageCode) {
+            ...Collection
+        }
+    }
+    ${COLLECTION_FRAGMENT}
+`;
+
+export const CREATE_COLLECTION = gql`
+    mutation CreateCollection($input: CreateCollectionInput!) {
+        createCollection(input: $input) {
+            ...Collection
+        }
+    }
+    ${COLLECTION_FRAGMENT}
+`;
+
+export const UPDATE_COLLECTION = gql`
+    mutation UpdateCollection($input: UpdateCollectionInput!) {
+        updateCollection(input: $input) {
+            ...Collection
+        }
+    }
+    ${COLLECTION_FRAGMENT}
+`;
+
+export const MOVE_COLLECTION = gql`
+    mutation MoveCollection($input: MoveCollectionInput!) {
+        moveCollection(input: $input) {
+            ...Collection
+        }
+    }
+    ${COLLECTION_FRAGMENT}
+`;
+
+export const GET_COLLECTION_CONTENTS = gql`
+    query GetCollectionContents($id: ID!, $options: ProductVariantListOptions) {
+        collection(id: $id) {
+            id
+            name
+            productVariants(options: $options) {
+                items {
+                    id
+                    productId
+                    name
+                }
+                totalItems
+            }
+        }
+    }
+`;

+ 0 - 101
admin-ui/src/app/data/definitions/product-definitions.ts

@@ -1,7 +1,5 @@
 import gql from 'graphql-tag';
 
-import { CONFIGURABLE_FRAGMENT } from './promotion-definitions';
-
 export const ASSET_FRAGMENT = gql`
     fragment Asset on Asset {
         id
@@ -287,105 +285,6 @@ export const CREATE_ASSETS = gql`
     ${ASSET_FRAGMENT}
 `;
 
-export const GET_COLLECTION_FILTERS = gql`
-    query GetCollectionFilters {
-        collectionFilters {
-            ...ConfigurableOperation
-        }
-    }
-    ${CONFIGURABLE_FRAGMENT}
-`;
-
-export const COLLECTION_FRAGMENT = gql`
-    fragment Collection on Collection {
-        id
-        name
-        description
-        languageCode
-        featuredAsset {
-            ...Asset
-        }
-        assets {
-            ...Asset
-        }
-        filters {
-            ...ConfigurableOperation
-        }
-        translations {
-            id
-            languageCode
-            name
-            description
-        }
-        parent {
-            id
-            name
-        }
-        children {
-            id
-            name
-        }
-    }
-    ${ASSET_FRAGMENT}
-    ${CONFIGURABLE_FRAGMENT}
-`;
-
-export const GET_COLLECTION_LIST = gql`
-    query GetCollectionList($options: CollectionListOptions, $languageCode: LanguageCode) {
-        collections(languageCode: $languageCode, options: $options) {
-            items {
-                id
-                name
-                description
-                featuredAsset {
-                    ...Asset
-                }
-                parent {
-                    id
-                }
-            }
-            totalItems
-        }
-    }
-    ${ASSET_FRAGMENT}
-`;
-
-export const GET_COLLECTION = gql`
-    query GetCollection($id: ID!, $languageCode: LanguageCode) {
-        collection(id: $id, languageCode: $languageCode) {
-            ...Collection
-        }
-    }
-    ${COLLECTION_FRAGMENT}
-`;
-
-export const CREATE_COLLECTION = gql`
-    mutation CreateCollection($input: CreateCollectionInput!) {
-        createCollection(input: $input) {
-            ...Collection
-        }
-    }
-    ${COLLECTION_FRAGMENT}
-`;
-
-export const UPDATE_COLLECTION = gql`
-    mutation UpdateCollection($input: UpdateCollectionInput!) {
-        updateCollection(input: $input) {
-            ...Collection
-        }
-    }
-    ${COLLECTION_FRAGMENT}
-`;
-
-export const MOVE_COLLECTION = gql`
-    mutation MoveCollection($input: MoveCollectionInput!) {
-        moveCollection(input: $input) {
-            ...Collection
-        }
-    }
-    ${COLLECTION_FRAGMENT}
-`;
-
 export const SEARCH_PRODUCTS = gql`
     query SearchProducts($input: SearchInput!) {
         search(input: $input) {

+ 112 - 0
admin-ui/src/app/data/providers/collection-data.service.ts

@@ -0,0 +1,112 @@
+import { from } from 'rxjs';
+import { bufferCount, concatMap } from 'rxjs/operators';
+import {
+    CreateCollection,
+    CreateCollectionInput,
+    GetCollection,
+    GetCollectionContents,
+    GetCollectionFilters,
+    GetCollectionList,
+    MoveCollection,
+    MoveCollectionInput,
+    UpdateCollection,
+    UpdateCollectionInput,
+} from 'shared/generated-types';
+import { pick } from 'shared/pick';
+
+import { getDefaultLanguage } from '../../common/utilities/get-default-language';
+import {
+    CREATE_COLLECTION,
+    GET_COLLECTION,
+    GET_COLLECTION_CONTENTS,
+    GET_COLLECTION_FILTERS,
+    GET_COLLECTION_LIST,
+    MOVE_COLLECTION,
+    UPDATE_COLLECTION,
+} from '../definitions/collection-definitions';
+
+import { BaseDataService } from './base-data.service';
+
+export class CollectionDataService {
+    constructor(private baseDataService: BaseDataService) {}
+
+    getCollectionFilters() {
+        return this.baseDataService.query<GetCollectionFilters.Query>(GET_COLLECTION_FILTERS);
+    }
+
+    getCollections(take: number = 10, skip: number = 0) {
+        return this.baseDataService.query<GetCollectionList.Query, GetCollectionList.Variables>(
+            GET_COLLECTION_LIST,
+            {
+                options: {
+                    take,
+                    skip,
+                },
+                languageCode: getDefaultLanguage(),
+            },
+        );
+    }
+
+    getCollection(id: string) {
+        return this.baseDataService.query<GetCollection.Query, GetCollection.Variables>(GET_COLLECTION, {
+            id,
+            languageCode: getDefaultLanguage(),
+        });
+    }
+
+    createCollection(input: CreateCollectionInput) {
+        return this.baseDataService.mutate<CreateCollection.Mutation, CreateCollection.Variables>(
+            CREATE_COLLECTION,
+            {
+                input: pick(input, [
+                    'translations',
+                    'assetIds',
+                    'featuredAssetId',
+                    'filters',
+                    'customFields',
+                ]),
+            },
+        );
+    }
+
+    updateCollection(input: UpdateCollectionInput) {
+        return this.baseDataService.mutate<UpdateCollection.Mutation, UpdateCollection.Variables>(
+            UPDATE_COLLECTION,
+            {
+                input: pick(input, [
+                    'id',
+                    'translations',
+                    'assetIds',
+                    'featuredAssetId',
+                    'filters',
+                    'customFields',
+                ]),
+            },
+        );
+    }
+
+    moveCollection(inputs: MoveCollectionInput[]) {
+        return from(inputs).pipe(
+            concatMap(input =>
+                this.baseDataService.mutate<MoveCollection.Mutation, MoveCollection.Variables>(
+                    MOVE_COLLECTION,
+                    { input },
+                ),
+            ),
+            bufferCount(inputs.length),
+        );
+    }
+
+    getCollectionContents(id: string, take: number = 10, skip: number = 0) {
+        return this.baseDataService.query<GetCollectionContents.Query, GetCollectionContents.Variables>(
+            GET_COLLECTION_CONTENTS,
+            {
+                id,
+                options: {
+                    skip,
+                    take,
+                },
+            },
+        );
+    }
+}

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

@@ -4,6 +4,7 @@ import { AdministratorDataService } from './administrator-data.service';
 import { AuthDataService } from './auth-data.service';
 import { BaseDataService } from './base-data.service';
 import { ClientDataService } from './client-data.service';
+import { CollectionDataService } from './collection-data.service';
 import { CustomerDataService } from './customer-data.service';
 import { FacetDataService } from './facet-data.service';
 import { OrderDataService } from './order-data.service';
@@ -17,6 +18,7 @@ export class DataService {
     promotion: PromotionDataService;
     administrator: AdministratorDataService;
     auth: AuthDataService;
+    collection: CollectionDataService;
     product: ProductDataService;
     client: ClientDataService;
     facet: FacetDataService;
@@ -29,6 +31,7 @@ export class DataService {
         this.promotion = new PromotionDataService(baseDataService);
         this.administrator = new AdministratorDataService(baseDataService);
         this.auth = new AuthDataService(baseDataService);
+        this.collection = new CollectionDataService(baseDataService);
         this.product = new ProductDataService(baseDataService);
         this.client = new ClientDataService(baseDataService);
         this.facet = new FacetDataService(baseDataService);

+ 0 - 84
admin-ui/src/app/data/providers/product-data.service.ts

@@ -1,10 +1,6 @@
-import { from } from 'rxjs';
-import { bufferCount, concatMap } from 'rxjs/operators';
 import {
     AddOptionGroupToProduct,
     CreateAssets,
-    CreateCollection,
-    CreateCollectionInput,
     CreateProduct,
     CreateProductInput,
     CreateProductOptionGroup,
@@ -12,18 +8,11 @@ import {
     DeleteProduct,
     GenerateProductVariants,
     GetAssetList,
-    GetCollection,
-    GetCollectionFilters,
-    GetCollectionList,
     GetProductList,
     GetProductOptionGroups,
     GetProductWithVariants,
-    MoveCollection,
-    MoveCollectionInput,
     RemoveOptionGroupFromProduct,
     SearchProducts,
-    UpdateCollection,
-    UpdateCollectionInput,
     UpdateProduct,
     UpdateProductInput,
     UpdateProductVariantInput,
@@ -35,22 +24,16 @@ import { getDefaultLanguage } from '../../common/utilities/get-default-language'
 import {
     ADD_OPTION_GROUP_TO_PRODUCT,
     CREATE_ASSETS,
-    CREATE_COLLECTION,
     CREATE_PRODUCT,
     CREATE_PRODUCT_OPTION_GROUP,
     DELETE_PRODUCT,
     GENERATE_PRODUCT_VARIANTS,
     GET_ASSET_LIST,
-    GET_COLLECTION,
-    GET_COLLECTION_FILTERS,
-    GET_COLLECTION_LIST,
     GET_PRODUCT_LIST,
     GET_PRODUCT_OPTION_GROUPS,
     GET_PRODUCT_WITH_VARIANTS,
-    MOVE_COLLECTION,
     REMOVE_OPTION_GROUP_FROM_PRODUCT,
     SEARCH_PRODUCTS,
-    UPDATE_COLLECTION,
     UPDATE_PRODUCT,
     UPDATE_PRODUCT_VARIANTS,
 } from '../definitions/product-definitions';
@@ -206,71 +189,4 @@ export class ProductDataService {
             input: files.map(file => ({ file })),
         });
     }
-
-    getCollectionFilters() {
-        return this.baseDataService.query<GetCollectionFilters.Query>(GET_COLLECTION_FILTERS);
-    }
-
-    getCollections(take: number = 10, skip: number = 0) {
-        return this.baseDataService.query<GetCollectionList.Query, GetCollectionList.Variables>(
-            GET_COLLECTION_LIST,
-            {
-                options: {
-                    take,
-                    skip,
-                },
-                languageCode: getDefaultLanguage(),
-            },
-        );
-    }
-
-    getCollection(id: string) {
-        return this.baseDataService.query<GetCollection.Query, GetCollection.Variables>(GET_COLLECTION, {
-            id,
-            languageCode: getDefaultLanguage(),
-        });
-    }
-
-    createCollection(input: CreateCollectionInput) {
-        return this.baseDataService.mutate<CreateCollection.Mutation, CreateCollection.Variables>(
-            CREATE_COLLECTION,
-            {
-                input: pick(input, [
-                    'translations',
-                    'assetIds',
-                    'featuredAssetId',
-                    'filters',
-                    'customFields',
-                ]),
-            },
-        );
-    }
-
-    updateCollection(input: UpdateCollectionInput) {
-        return this.baseDataService.mutate<UpdateCollection.Mutation, UpdateCollection.Variables>(
-            UPDATE_COLLECTION,
-            {
-                input: pick(input, [
-                    'id',
-                    'translations',
-                    'assetIds',
-                    'featuredAssetId',
-                    'filters',
-                    'customFields',
-                ]),
-            },
-        );
-    }
-
-    moveCollection(inputs: MoveCollectionInput[]) {
-        return from(inputs).pipe(
-            concatMap(input =>
-                this.baseDataService.mutate<MoveCollection.Mutation, MoveCollection.Variables>(
-                    MOVE_COLLECTION,
-                    { input },
-                ),
-            ),
-            bufferCount(inputs.length),
-        );
-    }
 }

+ 4 - 3
admin-ui/src/i18n-messages/en.json

@@ -24,7 +24,6 @@
   "catalog": {
     "add-asset": "Add asset",
     "add-asset-to-product": "Add {count, plural, 0 {assets} one {1 asset} other {{count} assets}} to product",
-    "add-facet": "Add facet",
     "add-facet-value": "Add facet value",
     "add-facets": "Add facets",
     "assets-selected-count": "{ count } assets selected",
@@ -41,8 +40,8 @@
     "drop-files-to-upload": "Drop files to upload",
     "facet": "Facet",
     "facet-values": "Facet values",
-    "facets": "Facets",
     "filter-by-group-name": "Filter by group name",
+    "filters": "Filters",
     "generate-product-variants": "Generate product variants",
     "generate-variants-default-only": "This product does not have options",
     "generate-variants-with-options": "This product has options",
@@ -79,7 +78,8 @@
     "taxes": "Taxes",
     "truncated-options-count": "{count} further {count, plural, one {option} other {options}}",
     "upload-assets": "Upload assets",
-    "values": "Values"
+    "values": "Values",
+    "view-contents": "View contents"
   },
   "common": {
     "ID": "ID",
@@ -124,6 +124,7 @@
     "password": "Password",
     "remember-me": "Remember me",
     "remove": "Remove",
+    "results-count": "{ count } {count, plural, one {result} other {results}}",
     "select": "Select...",
     "there-are-unsaved-changes": "There are unsaved changes. Navigating away will cause these changes to be lost.",
     "update": "Update",

+ 1 - 1
shared/generated-shop-types.ts

@@ -1,5 +1,5 @@
 // tslint:disable
-// Generated in 2019-03-07T10:54:02+01:00
+// Generated in 2019-03-07T11:46:05+01:00
 export type Maybe<T> = T | null;
 
 export interface OrderListOptions {

+ 215 - 1
shared/generated-types.ts

@@ -1,5 +1,5 @@
 // tslint:disable
-// Generated in 2019-03-07T10:54:03+01:00
+// Generated in 2019-03-07T11:46:05+01:00
 export type Maybe<T> = T | null;
 
 
@@ -2021,6 +2021,160 @@ export namespace GetUiState {
   } 
 }
 
+export namespace GetCollectionFilters {
+  export type Variables = {
+  }
+
+  export type Query = {
+    __typename?: "Query";
+    
+    collectionFilters: CollectionFilters[];
+  }
+
+  export type CollectionFilters = ConfigurableOperation.Fragment
+}
+
+export namespace GetCollectionList {
+  export type Variables = {
+    options?: Maybe<CollectionListOptions>;
+    languageCode?: Maybe<LanguageCode>;
+  }
+
+  export type Query = {
+    __typename?: "Query";
+    
+    collections: Collections;
+  }
+
+  export type Collections = {
+    __typename?: "CollectionList";
+    
+    items: Items[];
+    
+    totalItems: number;
+  } 
+
+  export type Items = {
+    __typename?: "Collection";
+    
+    id: string;
+    
+    name: string;
+    
+    description: string;
+    
+    featuredAsset: Maybe<FeaturedAsset>;
+    
+    parent: Parent;
+  } 
+
+  export type FeaturedAsset = Asset.Fragment
+
+  export type Parent = {
+    __typename?: "Collection";
+    
+    id: string;
+  } 
+}
+
+export namespace GetCollection {
+  export type Variables = {
+    id: string;
+    languageCode?: Maybe<LanguageCode>;
+  }
+
+  export type Query = {
+    __typename?: "Query";
+    
+    collection: Maybe<Collection>;
+  }
+
+  export type Collection = Collection.Fragment
+}
+
+export namespace CreateCollection {
+  export type Variables = {
+    input: CreateCollectionInput;
+  }
+
+  export type Mutation = {
+    __typename?: "Mutation";
+    
+    createCollection: CreateCollection;
+  }
+
+  export type CreateCollection = Collection.Fragment
+}
+
+export namespace UpdateCollection {
+  export type Variables = {
+    input: UpdateCollectionInput;
+  }
+
+  export type Mutation = {
+    __typename?: "Mutation";
+    
+    updateCollection: UpdateCollection;
+  }
+
+  export type UpdateCollection = Collection.Fragment
+}
+
+export namespace MoveCollection {
+  export type Variables = {
+    input: MoveCollectionInput;
+  }
+
+  export type Mutation = {
+    __typename?: "Mutation";
+    
+    moveCollection: MoveCollection;
+  }
+
+  export type MoveCollection = Collection.Fragment
+}
+
+export namespace GetCollectionContents {
+  export type Variables = {
+    id: string;
+    options?: Maybe<ProductVariantListOptions>;
+  }
+
+  export type Query = {
+    __typename?: "Query";
+    
+    collection: Maybe<Collection>;
+  }
+
+  export type Collection = {
+    __typename?: "Collection";
+    
+    id: string;
+    
+    name: string;
+    
+    productVariants: ProductVariants;
+  } 
+
+  export type ProductVariants = {
+    __typename?: "ProductVariantList";
+    
+    items: Items[];
+    
+    totalItems: number;
+  } 
+
+  export type Items = {
+    __typename?: "ProductVariant";
+    
+    id: string;
+    
+    productId: string;
+    
+    name: string;
+  } 
+}
+
 export namespace GetCustomerList {
   export type Variables = {
     options?: Maybe<CustomerListOptions>;
@@ -3439,6 +3593,66 @@ export namespace CurrentUser {
   }
 }
 
+export namespace Collection {
+  export type Fragment = {
+    __typename?: "Collection";
+    
+    id: string;
+    
+    name: string;
+    
+    description: string;
+    
+    languageCode: Maybe<LanguageCode>;
+    
+    featuredAsset: Maybe<FeaturedAsset>;
+    
+    assets: Assets[];
+    
+    filters: Filters[];
+    
+    translations: Translations[];
+    
+    parent: Parent;
+    
+    children: Maybe<Children[]>;
+  }
+
+  export type FeaturedAsset =Asset.Fragment
+
+  export type Assets =Asset.Fragment
+
+  export type Filters =ConfigurableOperation.Fragment
+
+  export type Translations = {
+    __typename?: "CollectionTranslation";
+    
+    id: string;
+    
+    languageCode: LanguageCode;
+    
+    name: string;
+    
+    description: string;
+  }
+
+  export type Parent = {
+    __typename?: "Collection";
+    
+    id: string;
+    
+    name: string;
+  }
+
+  export type Children = {
+    __typename?: "Collection";
+    
+    id: string;
+    
+    name: string;
+  }
+}
+
 export namespace Address {
   export type Fragment = {
     __typename?: "Address";