Browse Source

Merge branch 'minor' into major

Michael Bromley 3 years ago
parent
commit
347d576036
45 changed files with 1129 additions and 865 deletions
  1. 59 0
      CHANGELOG.md
  2. 8 0
      packages/admin-ui/src/lib/catalog/src/components/assets/assets.component.scss
  3. 9 5
      packages/admin-ui/src/lib/catalog/src/components/collection-detail/collection-detail.component.ts
  4. 7 8
      packages/admin-ui/src/lib/catalog/src/components/product-list/product-list.component.html
  5. 9 0
      packages/admin-ui/src/lib/catalog/src/components/product-list/product-list.component.scss
  6. 290 293
      packages/admin-ui/src/lib/core/src/common/generated-types.ts
  7. 1 1
      packages/admin-ui/src/lib/core/src/components/app-shell/app-shell.component.html
  8. 13 1
      packages/admin-ui/src/lib/core/src/components/app-shell/app-shell.component.scss
  9. 1 0
      packages/admin-ui/src/lib/core/src/components/app-shell/app-shell.component.ts
  10. 1 0
      packages/admin-ui/src/lib/core/src/data/definitions/order-definitions.ts
  11. 1 1
      packages/admin-ui/src/lib/core/src/shared/components/asset-gallery/asset-gallery.component.html
  12. 5 0
      packages/admin-ui/src/lib/core/src/shared/components/asset-gallery/asset-gallery.component.scss
  13. 2 1
      packages/admin-ui/src/lib/core/src/shared/components/rich-text-editor/rich-text-editor.component.scss
  14. 8 7
      packages/admin-ui/src/lib/core/src/shared/dynamic-form-inputs/combination-mode-form-input/combination-mode-form-input.component.ts
  15. 6 0
      packages/admin-ui/src/lib/dashboard/src/components/dashboard/dashboard.component.scss
  16. 0 3
      packages/admin-ui/src/lib/dashboard/src/widgets/order-summary-widget/order-summary-widget.component.scss
  17. 4 1
      packages/admin-ui/src/lib/login/src/components/login/login.component.html
  18. 39 1
      packages/admin-ui/src/lib/login/src/components/login/login.component.scss
  19. 1 1
      packages/admin-ui/src/lib/order/src/components/order-list/order-list.component.html
  20. 1 1
      packages/admin-ui/src/lib/static/styles/global/_forms.scss
  21. 5 1
      packages/admin-ui/src/lib/static/styles/global/_overrides.scss
  22. 13 1
      packages/admin-ui/src/lib/static/styles/theme/default.scss
  23. 16 1
      packages/asset-server-plugin/e2e/asset-server-plugin.e2e-spec.ts
  24. 1 0
      packages/asset-server-plugin/e2e/fixtures/assets/bad-image.jpg
  25. 1 1
      packages/asset-server-plugin/src/plugin.ts
  26. 10 0
      packages/asset-server-plugin/src/s3-asset-storage-strategy.ts
  27. 37 21
      packages/asset-server-plugin/src/sharp-asset-preview-strategy.ts
  28. 505 495
      packages/core/e2e/graphql/generated-e2e-admin-types.ts
  29. 2 2
      packages/core/src/config/payment/dummy-payment-method-handler.ts
  30. 2 2
      packages/core/src/config/payment/example-payment-method-handler.ts
  31. 10 4
      packages/core/src/config/payment/payment-method-eligibility-checker.ts
  32. 16 5
      packages/core/src/config/payment/payment-method-handler.ts
  33. 1 1
      packages/core/src/event-bus/events/role-event.ts
  34. 22 0
      packages/core/src/event-bus/events/search-event.ts
  35. 1 0
      packages/core/src/event-bus/index.ts
  36. 1 1
      packages/core/src/job-queue/subscribable-job.ts
  37. 3 0
      packages/core/src/plugin/default-search-plugin/fulltext-search.service.ts
  38. 1 1
      packages/core/src/plugin/default-search-plugin/indexer/search-index.service.ts
  39. 3 2
      packages/core/src/service/helpers/list-query-builder/list-query-builder.ts
  40. 1 1
      packages/core/src/service/services/payment-method.service.ts
  41. 4 1
      packages/core/src/service/services/payment.service.ts
  42. 1 0
      packages/dev-server/package.json
  43. 5 0
      packages/elasticsearch-plugin/src/elasticsearch.service.ts
  44. 2 1
      packages/elasticsearch-plugin/src/indexing/elasticsearch-index.service.ts
  45. 1 0
      packages/job-queue-plugin/package.json

+ 59 - 0
CHANGELOG.md

@@ -1,3 +1,62 @@
+## 1.6.0 (2022-05-18)
+
+
+#### Fixes
+
+* **admin-ui** Display total with tax in order list ([92661da](https://github.com/vendure-ecommerce/vendure/commit/92661da))
+* **admin-ui** Fix form change detection in collection filters ([0938be0](https://github.com/vendure-ecommerce/vendure/commit/0938be0))
+* **admin-ui** Improve display of many channels on Product detail ([87b8a53](https://github.com/vendure-ecommerce/vendure/commit/87b8a53)), closes [#1431](https://github.com/vendure-ecommerce/vendure/issues/1431)
+* **admin-ui** Prevent route change on collection contents list change ([5589628](https://github.com/vendure-ecommerce/vendure/commit/5589628)), closes [#1530](https://github.com/vendure-ecommerce/vendure/issues/1530)
+* **admin-ui** Styling improvements to image display ([7b308c1](https://github.com/vendure-ecommerce/vendure/commit/7b308c1)), closes [#1514](https://github.com/vendure-ecommerce/vendure/issues/1514)
+* **asset-server-plugin** Gracefully handle unsupported image previews ([91b69f0](https://github.com/vendure-ecommerce/vendure/commit/91b69f0)), closes [#1563](https://github.com/vendure-ecommerce/vendure/issues/1563)
+* **asset-server-plugin** Use EXIF data to correctly orient images ([aa9bd03](https://github.com/vendure-ecommerce/vendure/commit/aa9bd03)), closes [#1548](https://github.com/vendure-ecommerce/vendure/issues/1548)
+* **core** Fix error in configurable operation codec when arg not found ([9ba44f4](https://github.com/vendure-ecommerce/vendure/commit/9ba44f4))
+* **core** Fix variants not being returned in some language configs ([6a4e0d4](https://github.com/vendure-ecommerce/vendure/commit/6a4e0d4)), closes [#1539](https://github.com/vendure-ecommerce/vendure/issues/1539)
+* **core** Improve error logging when search indexing fails ([bf75171](https://github.com/vendure-ecommerce/vendure/commit/bf75171)), closes [#1556](https://github.com/vendure-ecommerce/vendure/issues/1556)
+* **core** Job update doesn't emit if progress didn't change (#1550) ([2ce444f](https://github.com/vendure-ecommerce/vendure/commit/2ce444f)), closes [#1550](https://github.com/vendure-ecommerce/vendure/issues/1550)
+* **core** Make OrderLine.items eager-loaded from the DB ([8465d84](https://github.com/vendure-ecommerce/vendure/commit/8465d84))
+* **core** Manage transactions outside of orderService.modifyOrder function. (#1533) ([e707274](https://github.com/vendure-ecommerce/vendure/commit/e707274)), closes [#1533](https://github.com/vendure-ecommerce/vendure/issues/1533)
+* **elasticsearch-plugin** Improve error log when search indexing fails ([7dcad6e](https://github.com/vendure-ecommerce/vendure/commit/7dcad6e)), closes [#1556](https://github.com/vendure-ecommerce/vendure/issues/1556)
+* **job-queue-plugin** Fix Redis health indicator error reporting ([48a30fb](https://github.com/vendure-ecommerce/vendure/commit/48a30fb))
+* **ui-devkit** Wrap output path in quotes. (#1519) ([755d2e2](https://github.com/vendure-ecommerce/vendure/commit/755d2e2)), closes [#1519](https://github.com/vendure-ecommerce/vendure/issues/1519)
+
+#### Features
+
+* **admin-ui** Add live preview of Collection filter changes ([ba6c64a](https://github.com/vendure-ecommerce/vendure/commit/ba6c64a)), closes [#1530](https://github.com/vendure-ecommerce/vendure/issues/1530)
+* **admin-ui** Add sku to Collection contents table ([8c2263c](https://github.com/vendure-ecommerce/vendure/commit/8c2263c))
+* **admin-ui** Display description tooltip for configurable args ([837e1f2](https://github.com/vendure-ecommerce/vendure/commit/837e1f2))
+* **admin-ui** Implement combination mode toggle for Collection filters ([cb1e137](https://github.com/vendure-ecommerce/vendure/commit/cb1e137))
+* **admin-ui** Implement content preview when creating collection ([1e4f072](https://github.com/vendure-ecommerce/vendure/commit/1e4f072)), closes [#1530](https://github.com/vendure-ecommerce/vendure/issues/1530)
+* **admin-ui** Implement FormInput for multi product/variant selection ([47c9b0e](https://github.com/vendure-ecommerce/vendure/commit/47c9b0e))
+* **admin-ui** Improve styling of configurable arg inputs ([d20a1dc](https://github.com/vendure-ecommerce/vendure/commit/d20a1dc))
+* **admin-ui** Persist Collection list expanded states to the url ([d67187e](https://github.com/vendure-ecommerce/vendure/commit/d67187e)), closes [#1532](https://github.com/vendure-ecommerce/vendure/issues/1532)
+* **admin-ui** Persist Collection list filter term to the url ([dcdd05b](https://github.com/vendure-ecommerce/vendure/commit/dcdd05b)), closes [#1532](https://github.com/vendure-ecommerce/vendure/issues/1532)
+* **admin-ui** Various styling improvements ([c76aba0](https://github.com/vendure-ecommerce/vendure/commit/c76aba0))
+* **core** Add `metadataModifiers` for low-level DB entity config ([16e52f2](https://github.com/vendure-ecommerce/vendure/commit/16e52f2)), closes [#1506](https://github.com/vendure-ecommerce/vendure/issues/1506) [#1502](https://github.com/vendure-ecommerce/vendure/issues/1502)
+* **core** Add boolean combination support on default CollectionFilters ([8889ac2](https://github.com/vendure-ecommerce/vendure/commit/8889ac2))
+* **core** Add new variantIdCollectionFilter default CollectionFilter ([449c584](https://github.com/vendure-ecommerce/vendure/commit/449c584))
+* **core** Allow entity alias to be specified in ListQueryBuilder ([f221940](https://github.com/vendure-ecommerce/vendure/commit/f221940))
+* **core** Create Relations decorator ([063b5fe](https://github.com/vendure-ecommerce/vendure/commit/063b5fe)), closes [#1506](https://github.com/vendure-ecommerce/vendure/issues/1506)
+* **core** Expose Importer.importProducts method ([bbe09aa](https://github.com/vendure-ecommerce/vendure/commit/bbe09aa))
+* **core** Implement `previewCollectionVariants` query in Admin API ([1c3b38c](https://github.com/vendure-ecommerce/vendure/commit/1c3b38c)), closes [#1530](https://github.com/vendure-ecommerce/vendure/issues/1530)
+* **core** Implement unique constraint for custom fields ([07e1601](https://github.com/vendure-ecommerce/vendure/commit/07e1601)), closes [#1476](https://github.com/vendure-ecommerce/vendure/issues/1476)
+* **core** Make all health checks configurable ([f3d2d59](https://github.com/vendure-ecommerce/vendure/commit/f3d2d59)), closes [#1494](https://github.com/vendure-ecommerce/vendure/issues/1494)
+* **core** Make OrderService.applyPriceAdjustments() public ([826fd55](https://github.com/vendure-ecommerce/vendure/commit/826fd55)), closes [#1522](https://github.com/vendure-ecommerce/vendure/issues/1522)
+* **core** Make search strategy configurable via plugin options (#1504) ([b31694f](https://github.com/vendure-ecommerce/vendure/commit/b31694f)), closes [#1504](https://github.com/vendure-ecommerce/vendure/issues/1504)
+* **core** Pass payment method to handler and eligibility checker (#1564) ([4e63180](https://github.com/vendure-ecommerce/vendure/commit/4e63180)), closes [#1564](https://github.com/vendure-ecommerce/vendure/issues/1564)
+* **core** Pass shipping method to calculator and eligibility checker (#1509) ([826aa4a](https://github.com/vendure-ecommerce/vendure/commit/826aa4a)), closes [#1509](https://github.com/vendure-ecommerce/vendure/issues/1509)
+* **core** Use query relations data to optimize DB joins ([0421285](https://github.com/vendure-ecommerce/vendure/commit/0421285)), closes [#1506](https://github.com/vendure-ecommerce/vendure/issues/1506) [#1407](https://github.com/vendure-ecommerce/vendure/issues/1407)
+* **core** Use variant featuredAsset in OrderLine if available ([0c308e2](https://github.com/vendure-ecommerce/vendure/commit/0c308e2)), closes [#1488](https://github.com/vendure-ecommerce/vendure/issues/1488)
+* **core** Add `SearchEvent` & publish when search query is executed, closes [#1553](https://github.com/vendure-ecommerce/vendure/issues/1553)
+* **elasticsearch-plugin** Publish `SearchEvent` when search query is executed, closes [#1553](https://github.com/vendure-ecommerce/vendure/issues/1553)
+* **payments-plugin** Deprecate orderId when generating Braintree token ([8ba76f2](https://github.com/vendure-ecommerce/vendure/commit/8ba76f2)), closes [#1517](https://github.com/vendure-ecommerce/vendure/issues/1517)
+
+#### Perf
+
+* **core** Further optimizations to ListQueryBuilder ([d9577f8](https://github.com/vendure-ecommerce/vendure/commit/d9577f8)), closes [#1506](https://github.com/vendure-ecommerce/vendure/issues/1506) [#1503](https://github.com/vendure-ecommerce/vendure/issues/1503)
+* **core** Optimize ListQueryBuilder performance ([8d87f05](https://github.com/vendure-ecommerce/vendure/commit/8d87f05)), closes [#1503](https://github.com/vendure-ecommerce/vendure/issues/1503) [#1506](https://github.com/vendure-ecommerce/vendure/issues/1506) [1#L122](https://github.com/1/issues/L122)
+* **core** Optimize query to fetch all collection ids on changes ([a362fb4](https://github.com/vendure-ecommerce/vendure/commit/a362fb4))
+
 ## <small>1.5.2 (2022-04-21)</small>
 
 

+ 8 - 0
packages/admin-ui/src/lib/catalog/src/components/assets/assets.component.scss

@@ -17,6 +17,11 @@
     background: var(--color-component-bg-200);
     padding: 6px;
     cursor: pointer;
+    border-radius: var(--border-radius-img);
+
+    img {
+        border-radius: var(--border-radius-img);
+    }
 
     &.compact {
         width: 100%;
@@ -46,15 +51,18 @@
         margin: 3px;
         padding: 0;
         border: 2px solid var(--color-component-border-100);
+        border-radius: var(--border-radius-img);
         cursor: pointer;
 
         img {
             width: 50px;
             height: 50px;
+            border-radius: var(--border-radius-img);
         }
 
         &.featured {
             border-color: var(--color-primary-500);
+            border-radius: calc(var(--border-radius-img) + 2px);
         }
     }
 

+ 9 - 5
packages/admin-ui/src/lib/catalog/src/components/collection-detail/collection-detail.component.ts

@@ -34,7 +34,7 @@ import {
     UpdateCollectionInput,
 } from '@vendure/admin-ui/core';
 import { normalizeString } from '@vendure/common/lib/normalize-string';
-import { combineLatest, Observable, of } from 'rxjs';
+import { combineLatest, merge, Observable, of, Subject } from 'rxjs';
 import { debounceTime, filter, map, mergeMap, switchMap, take } from 'rxjs/operators';
 
 import { CollectionContentsComponent } from '../collection-contents/collection-contents.component';
@@ -58,6 +58,7 @@ export class CollectionDetailComponent
     livePreview = false;
     parentId$: Observable<string | undefined>;
     readonly updatePermission = [Permission.UpdateCatalog, Permission.UpdateCollection];
+    private filterRemoved$ = new Subject<void>();
     @ViewChild('collectionContents') contentsComponent: CollectionContentsComponent;
 
     constructor(
@@ -93,14 +94,14 @@ export class CollectionDetailComponent
             this.allFilters = res.collectionFilters;
         });
         const filtersFormArray = this.detailForm.get('filters') as FormArray;
-        this.updatedFilters$ = filtersFormArray.statusChanges.pipe(
+        this.updatedFilters$ = merge(filtersFormArray.statusChanges, this.filterRemoved$).pipe(
             debounceTime(200),
             filter(() => filtersFormArray.touched),
-            map(status =>
-                this.mapOperationsToInputs(this.filters, filtersFormArray.value).filter(filter => {
+            map(() =>
+                this.mapOperationsToInputs(this.filters, filtersFormArray.value).filter(_filter => {
                     // ensure all the arguments have valid values. E.g. a newly-added
                     // filter will not yet have valid values
-                    for (const arg of filter.arguments) {
+                    for (const arg of _filter.arguments) {
                         if (arg.value === '') {
                             return false;
                         }
@@ -174,7 +175,10 @@ export class CollectionDetailComponent
         const filtersArray = this.detailForm.get('filters') as FormArray;
         if (index !== -1) {
             filtersArray.removeAt(index);
+            filtersArray.markAsDirty();
+            filtersArray.markAsTouched();
             this.filters.splice(index, 1);
+            this.filterRemoved$.next();
         }
     }
 

+ 7 - 8
packages/admin-ui/src/lib/catalog/src/components/product-list/product-list.component.html

@@ -20,7 +20,7 @@
                     vdrDropdownTrigger
                 >
                     <clr-icon shape="cog"></clr-icon>
-                    <vdr-status-badge *ngIf="pendingSearchIndexUpdates" type="warning"> </vdr-status-badge>
+                    <vdr-status-badge *ngIf="pendingSearchIndexUpdates" type="warning"></vdr-status-badge>
                 </button>
                 <vdr-dropdown-menu vdrPosition="bottom-right">
                     <h4 class="dropdown-header">{{ 'catalog.search-index-controls' | translate }}</h4>
@@ -32,10 +32,10 @@
                             (click)="runPendingSearchIndexUpdates()"
                             [disabled]="!(['UpdateCatalog', 'UpdateProduct'] | hasPermission)"
                         >
-                            <vdr-status-badge type="warning"> </vdr-status-badge>
+                            <vdr-status-badge type="warning"></vdr-status-badge>
                             {{
-                                'catalog.run-pending-search-index-updates'
-                                    | translate: { count: pendingSearchIndexUpdates }
+                            'catalog.run-pending-search-index-updates'
+                                | translate: {count: pendingSearchIndexUpdates}
                             }}
                         </button>
                         <div class="dropdown-divider"></div>
@@ -53,7 +53,7 @@
         </div>
         <div class="flex wrap">
             <clr-checkbox-wrapper class="mt2">
-                <input type="checkbox" clrCheckbox [(ngModel)]="groupByProduct" (ngModelChange)="refresh()" />
+                <input type="checkbox" clrCheckbox [(ngModel)]="groupByProduct" (ngModelChange)="refresh()"/>
                 <label>{{ 'catalog.group-by-product' | translate }}</label>
             </clr-checkbox-wrapper>
             <vdr-language-selector
@@ -85,7 +85,7 @@
     (itemsPerPageChange)="setItemsPerPage($event)"
 >
     <ng-template let-result="item">
-        <td class="left align-middle" [class.disabled]="!result.enabled">
+        <td class="left align-middle image-col" [class.disabled]="!result.enabled">
             <div class="image-placeholder">
                 <img
                     *ngIf="
@@ -111,12 +111,11 @@
         </td>
         <td class="right align-middle" [class.disabled]="!result.enabled">
             <vdr-table-row-action
+                class="edit-button"
                 iconShape="edit"
                 [label]="'common.edit' | translate"
                 [linkTo]="['./', result.productId]"
             ></vdr-table-row-action>
-        </td>
-        <td class="right align-middle" [class.disabled]="!result.enabled">
             <vdr-dropdown>
                 <button type="button" class="btn btn-link btn-sm" vdrDropdownTrigger>
                     {{ 'common.actions' | translate }}

+ 9 - 0
packages/admin-ui/src/lib/catalog/src/components/product-list/product-list.component.scss

@@ -1,9 +1,15 @@
 @import "variables";
 
+.image-col {
+    width: 70px;
+}
 .image-placeholder {
     width: 50px;
     height: 50px;
     background-color: var(--color-component-bg-200);
+    img {
+        border-radius: var(--border-radius-img);
+    }
     .placeholder {
         text-align: center;
         color: var(--color-grey-300);
@@ -41,3 +47,6 @@ td.disabled {
         top: 10px;
     }
 }
+.edit-button {
+    margin-right: 24px;
+}

File diff suppressed because it is too large
+ 290 - 293
packages/admin-ui/src/lib/core/src/common/generated-types.ts


+ 1 - 1
packages/admin-ui/src/lib/core/src/components/app-shell/app-shell.component.html

@@ -1,7 +1,7 @@
 <clr-main-container>
     <clr-header>
         <div class="branding">
-            <a [routerLink]="['/']"><img src="assets/logo-75px.png" class="logo" /></a>
+            <a [routerLink]="['/']"><img src="assets/logo-75px.png" class="logo" /><span class="wordmark" *ngIf="!hideVendureBranding">vendure</span></a>
         </div>
         <div class="header-nav"></div>
         <div class="header-actions">

+ 13 - 1
packages/admin-ui/src/lib/core/src/components/app-shell/app-shell.component.scss

@@ -4,7 +4,13 @@
     min-width: 0;
 }
 .logo {
-    width: 60px;
+    width: 40px;
+}
+.wordmark {
+    font-weight: bold;
+    margin-left: 12px;
+    font-size: 24px;
+    color: var(--color-primary-500);
 }
 vdr-breadcrumb {
     @media screen and (min-width: $breakpoint-small){
@@ -17,3 +23,9 @@ vdr-breadcrumb {
 .content-area {
     position: relative;
 }
+
+::ng-deep {
+    .header {
+        background-image: linear-gradient(to right, var(--color-header-gradient-from), var(--color-header-gradient-to));
+    }
+}

+ 1 - 0
packages/admin-ui/src/lib/core/src/components/app-shell/app-shell.component.ts

@@ -21,6 +21,7 @@ export class AppShellComponent implements OnInit {
     userName$: Observable<string>;
     uiLanguageAndLocale$: Observable<[LanguageCode, string | undefined]>;
     availableLanguages: LanguageCode[] = [];
+    hideVendureBranding = getAppConfig().hideVendureBranding;
 
     constructor(
         private authService: AuthService,

+ 1 - 0
packages/admin-ui/src/lib/core/src/data/definitions/order-definitions.ts

@@ -49,6 +49,7 @@ export const ORDER_FRAGMENT = gql`
         state
         nextStates
         total
+        totalWithTax
         currencyCode
         customer {
             id

+ 1 - 1
packages/admin-ui/src/lib/core/src/shared/components/asset-gallery/asset-gallery.component.html

@@ -11,7 +11,7 @@
                 [disabled]="true"
                 [hiddenWhenOff]="true"
             ></vdr-select-toggle>
-            <img [src]="asset | assetPreview: 'thumb'" />
+            <img class="asset-thumb" [src]="asset | assetPreview: 'thumb'" />
         </div>
         <div class="detail">
             <vdr-entity-info

+ 5 - 0
packages/admin-ui/src/lib/core/src/shared/components/asset-gallery/asset-gallery.component.scss

@@ -28,6 +28,11 @@
     position: relative;
 }
 
+
+img.asset-thumb {
+    aspect-ratio: 1;
+}
+
 vdr-select-toggle {
     position: absolute;
     ::ng-deep .toggle {

+ 2 - 1
packages/admin-ui/src/lib/core/src/shared/components/rich-text-editor/rich-text-editor.component.scss

@@ -21,6 +21,7 @@
     border-bottom: none;
     background-color: var(--color-component-bg-200);
     color: var(--color-icon-button);
+    border-radius: var(--border-radius-input) var(--border-radius-input) 0 0;
     padding: 6px 12px;
     display: flex;
     flex-wrap: wrap;
@@ -31,7 +32,7 @@
     min-height: 128px;
     min-width: 200px;
     border: 1px solid var(--color-component-border-200);
-    border-radius: 0 0 3px 3px;
+    border-radius: 0 0 var(--border-radius-input) var(--border-radius-input);
     transition: border-color 0.2s;
     overflow: auto;
     text-align: initial;

+ 8 - 7
packages/admin-ui/src/lib/core/src/shared/dynamic-form-inputs/combination-mode-form-input/combination-mode-form-input.component.ts

@@ -1,4 +1,4 @@
-import { ChangeDetectionStrategy, Component, Optional } from '@angular/core';
+import { ChangeDetectionStrategy, Component, OnInit, Optional } from '@angular/core';
 import { FormControl } from '@angular/forms';
 import { DefaultFormComponentConfig, DefaultFormComponentId } from '@vendure/common/lib/shared-types';
 import { Observable, of } from 'rxjs';
@@ -20,22 +20,23 @@ import { ConfigurableInputComponent } from '../../components/configurable-input/
     styleUrls: ['./combination-mode-form-input.component.scss'],
     changeDetection: ChangeDetectionStrategy.OnPush,
 })
-export class CombinationModeFormInputComponent implements FormInputComponent {
+export class CombinationModeFormInputComponent implements FormInputComponent, OnInit {
     static readonly id: DefaultFormComponentId = 'combination-mode-form-input';
     readonly: boolean;
     formControl: FormControl;
     config: DefaultFormComponentConfig<'combination-mode-form-input'>;
     selectable$: Observable<boolean>;
 
-    constructor(@Optional() private configurableInputComponent: ConfigurableInputComponent) {
-        const selectable$ = configurableInputComponent
-            ? configurableInputComponent.positionChange$.pipe(map(position => 0 < position))
-            : of(true);
+    constructor(@Optional() private configurableInputComponent: ConfigurableInputComponent) {}
 
+    ngOnInit() {
+        const selectable$ = this.configurableInputComponent
+            ? this.configurableInputComponent.positionChange$.pipe(map(position => 0 < position))
+            : of(true);
         this.selectable$ = selectable$.pipe(
             tap(selectable => {
                 if (!selectable) {
-                    this.setCombinationModeAnd();
+                    this.formControl.setValue(true, { emitEvent: false });
                 }
             }),
         );

+ 6 - 0
packages/admin-ui/src/lib/dashboard/src/components/dashboard/dashboard.component.scss

@@ -1,5 +1,11 @@
 @import 'variables';
 
+:host {
+    display: block;
+    max-width: 1200px;
+    margin: auto;
+}
+
 .widget-header {
     display: flex;
     justify-content: flex-end;

+ 0 - 3
packages/admin-ui/src/lib/dashboard/src/widgets/order-summary-widget/order-summary-widget.component.scss

@@ -21,8 +21,5 @@
     margin-top: 24px;
     display: flex;
     flex-direction: column;
-    @media screen and (min-width: $breakpoint-small) {
-        flex-direction: row;
-    }
     justify-content: space-between;
 }

+ 4 - 1
packages/admin-ui/src/lib/login/src/components/login/login.component.html

@@ -1,6 +1,9 @@
 <div class="login-wrapper">
     <form class="login">
-        <label class="title"><img src="assets/logo-300px.png" /></label>
+        <label class="title">
+            <img src="assets/logo-300px.png" />
+            <span *ngIf="!hideVendureBranding">vendure</span>
+        </label>
         <div class="login-group">
             <input
                 class="username"

+ 39 - 1
packages/admin-ui/src/lib/login/src/components/login/login.component.scss

@@ -1,13 +1,50 @@
 @import 'variables';
 
 .login-wrapper {
-    background: var(--login-page-bg);
+    background-image: linear-gradient(135deg, var(--color-login-gradient-top), var(--color-login-gradient-bottom)),
+    var(--login-page-bg);
+    background-blend-mode: screen;
+    background-repeat: repeat;
     background-size: auto;
+    background-position: initial;
     justify-content: center;
 }
 
+@media screen and (max-width: $breakpoint-small) {
+    .login-wrapper {
+        justify-content: center;
+        background-color: transparent;
+        .login {
+            margin: 20px;
+            padding: 24px;
+        }
+    }
+}
+
+.login {
+    margin: 5vh 0;
+    border-radius: 6px;
+    min-height: calc(100vh - 10vh);
+    max-height: 800px;
+}
+
 .title {
+    display: flex;
+    flex-direction: column;
+    align-items: center;
     text-align: center;
+    margin-top: 8vh;
+
+    img {
+        max-width: 100%;
+        width: 150px;
+    }
+    span {
+        padding-top: 12px;
+        font-weight: bold;
+        color: var(--color-primary-500);
+        font-size: 38px;
+    }
 }
 
 .version {
@@ -25,6 +62,7 @@
 .login-error {
     max-height: 0;
     overflow: hidden;
+
     &.visible {
         max-height: 46px;
         transition: max-height 0.2s;

+ 1 - 1
packages/admin-ui/src/lib/order/src/components/order-list/order-list.component.html

@@ -105,7 +105,7 @@
         <td class="left align-middle">
             <vdr-order-state-label [state]="order.state"></vdr-order-state-label>
         </td>
-        <td class="left align-middle">{{ order.total | localeCurrency: order.currencyCode }}</td>
+        <td class="left align-middle">{{ order.totalWithTax | localeCurrency: order.currencyCode }}</td>
         <td class="left align-middle">{{ order.updatedAt | timeAgo }}</td>
         <td class="left align-middle">{{ order.orderPlacedAt | localeDate: 'medium' }}</td>
         <td class="left align-middle">{{ getShippingNames(order) }}</td>

+ 1 - 1
packages/admin-ui/src/lib/static/styles/global/_forms.scss

@@ -46,7 +46,7 @@ textarea,
 
 input,
 select {
-    border-radius: 3px !important;
+    border-radius: var(--border-radius-input) !important;
     border: 1px solid var(--color-grey-300) !important;
     padding: 5px !important;
     height: initial !important;

+ 5 - 1
packages/admin-ui/src/lib/static/styles/global/_overrides.scss

@@ -5,7 +5,7 @@
     background-color: var(--clr-global-app-background);
 }
 
-.content-area img {
+.content-area img, .modal-content img {
     object-fit: cover;
     width: 100%;
     height: 100%;
@@ -37,6 +37,10 @@ a:focus, button:focus {
     td.align-middle {
         vertical-align: middle!important;
     }
+
+    td.right {
+        text-align: right;
+    }
 }
 
 .full-label, .compact-label {

+ 13 - 1
packages/admin-ui/src/lib/static/styles/theme/default.scss

@@ -74,6 +74,7 @@
     --color-icon-button: var(--color-grey-600);
     --color-form-input-bg: white;
     --color-timeline-thread: var(--color-primary-100);
+
     --color-chip-warning-border: var(--color-warning-200);
     --color-chip-warning-text: var(--color-warning-600);
     --color-chip-warning-bg: var(--color-warning-100);
@@ -83,6 +84,7 @@
     --color-chip-error-border: var(--color-error-200);
     --color-chip-error-text: var(--color-error-600);
     --color-chip-error-bg: var(--color-error-100);
+
     --color-json-editor-background-color: var(--color-grey-200);
     --color-json-editor-text: var(--color-grey-600);
     --color-json-editor-string: var(--color-secondary-600);
@@ -92,7 +94,17 @@
     --color-json-editor-key: var(--color-success-500);
     --color-json-editor-error: var(--color-error-500);
 
-    // Other variables
+    // Login page
     --login-page-bg: url(data:image/gif;base64,R0lGODlhFAAhAKIAABwcHBsbGxcXFx0dHRUVFRkZGR4eHhQUFCH/C1hNUCBEYXRhWE1QPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4gPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iQWRvYmUgWE1QIENvcmUgNS4zLWMwMTEgNjYuMTQ2NzI5LCAyMDEyLzA1LzAzLTEzOjQwOjAzICAgICAgICAiPiA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiPiA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtbG5zOnhtcE1NPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvbW0vIiB4bWxuczpzdFJlZj0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL3NUeXBlL1Jlc291cmNlUmVmIyIgeG1wOkNyZWF0b3JUb29sPSJBZG9iZSBQaG90b3Nob3AgRWxlbWVudHMgMTIuMCBXaW5kb3dzIiB4bXBNTTpJbnN0YW5jZUlEPSJ4bXAuaWlkOjhBOTg1NDlCNjMwNDExRUFBNEUwQ0M2RDdENUQ3RTBFIiB4bXBNTTpEb2N1bWVudElEPSJ4bXAuZGlkOjhBOTg1NDlDNjMwNDExRUFBNEUwQ0M2RDdENUQ3RTBFIj4gPHhtcE1NOkRlcml2ZWRGcm9tIHN0UmVmOmluc3RhbmNlSUQ9InhtcC5paWQ6OEE5ODU0OTk2MzA0MTFFQUE0RTBDQzZEN0Q1RDdFMEUiIHN0UmVmOmRvY3VtZW50SUQ9InhtcC5kaWQ6OEE5ODU0OUE2MzA0MTFFQUE0RTBDQzZEN0Q1RDdFMEUiLz4gPC9yZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1ldGE+IDw/eHBhY2tldCBlbmQ9InIiPz4B//79/Pv6+fj39vX08/Lx8O/u7ezr6uno5+bl5OPi4eDf3t3c29rZ2NfW1dTT0tHQz87NzMvKycjHxsXEw8LBwL++vby7urm4t7a1tLOysbCvrq2sq6qpqKempaSjoqGgn56dnJuamZiXlpWUk5KRkI+OjYyLiomIh4aFhIOCgYB/fn18e3p5eHd2dXRzcnFwb25tbGtqaWhnZmVkY2JhYF9eXVxbWllYV1ZVVFNSUVBPTk1MS0pJSEdGRURDQkFAPz49PDs6OTg3NjU0MzIxMC8uLSwrKikoJyYlJCMiISAfHh0cGxoZGBcWFRQTEhEQDw4NDAsKCQgHBgUEAwIBAAAh+QQAAAAAACwAAAAAFAAhAAADfSi23M7igEcZOIfUSvApm1N42RAuQ0cqZ0Ri0xa8nlaltAdSY44RJsfAt3q4iLDGDFlj4Ji6RQ/6GwypqyOtwO12BQKv+EawnRq93dlwLa0NWtZpSYptniRzZfpSP9o0QBVaNHJKUHYoKkh6BnxIaoBMgnBYGAp0lgCLlgQJADs=);
+    --color-login-gradient-top: var(--color-primary-800);
+    --color-login-gradient-bottom: black;
+
+    // header
+    --color-header-gradient-from: var(--color-primary-800);
+    --color-header-gradient-to: var(--color-primary-900);
+
+    // Other variables
     --breakpoint-small: 768px;
+    --border-radius-img: var(--clr-global-borderradius);
+    --border-radius-input: var(--clr-global-borderradius);
 }

+ 16 - 1
packages/asset-server-plugin/e2e/asset-server-plugin.e2e-spec.ts

@@ -28,7 +28,7 @@ describe('AssetServerPlugin', () => {
 
     const { server, adminClient, shopClient } = createTestEnvironment(
         mergeConfig(testConfig(), {
-            logger: new DefaultLogger({ level: LogLevel.Info }),
+            // logger: new DefaultLogger({ level: LogLevel.Info }),
             plugins: [
                 AssetServerPlugin.init({
                     assetUploadDir: path.join(__dirname, TEST_ASSET_DIR),
@@ -263,6 +263,21 @@ describe('AssetServerPlugin', () => {
             await testMimeTypeOfAssetWithExt('webp', 'image/webp');
         });
     });
+
+    // https://github.com/vendure-ecommerce/vendure/issues/1563
+    it('falls back to binary preview if image file cannot be processed', async () => {
+        const filesToUpload = [path.join(__dirname, `fixtures/assets/bad-image.jpg`)];
+        const { createAssets }: CreateAssets.Mutation = await adminClient.fileUploadMutation({
+            mutation: CREATE_ASSETS,
+            filePaths: filesToUpload,
+            mapVariables: filePaths => ({
+                input: filePaths.map(p => ({ file: null })),
+            }),
+        });
+
+        expect(createAssets.length).toBe(1);
+        expect(createAssets[0].name).toBe('bad-image.jpg');
+    });
 });
 
 export const CREATE_ASSETS = gql`

+ 1 - 0
packages/asset-server-plugin/e2e/fixtures/assets/bad-image.jpg

@@ -0,0 +1 @@
+this is not an image file! haha.

+ 1 - 1
packages/asset-server-plugin/src/plugin.ts

@@ -256,7 +256,7 @@ export class AssetServerPlugin implements NestModule, OnApplicationBootstrap {
                         res.send(imageBuffer);
                         return;
                     } catch (e: any) {
-                        Logger.error(e, 'AssetServerPlugin', e.stack);
+                        Logger.error(e, loggerCtx, e.stack);
                         res.status(500).send(e.message);
                         return;
                     }

+ 10 - 0
packages/asset-server-plugin/src/s3-asset-storage-strategy.ts

@@ -66,6 +66,11 @@ export interface S3Config {
  *
  * @example
  * ```TypeScript
+ * import { AssetServerPlugin, configureS3AssetStorage } from '\@vendure/asset-server-plugin';
+ * import { DefaultAssetNamingStrategy } from '\@vendure/core';
+ *
+ * // ...
+ *
  * plugins: [
  *   AssetServerPlugin.init({
  *     route: 'assets',
@@ -88,6 +93,11 @@ export interface S3Config {
  *
  * @example
  * ```TypeScript
+ * import { AssetServerPlugin, configureS3AssetStorage } from '\@vendure/asset-server-plugin';
+ * import { DefaultAssetNamingStrategy } from '\@vendure/core';
+ *
+ * // ...
+ *
  * plugins: [
  *   AssetServerPlugin.init({
  *     route: 'assets',

+ 37 - 21
packages/asset-server-plugin/src/sharp-asset-preview-strategy.ts

@@ -1,8 +1,10 @@
 import { AssetType } from '@vendure/common/lib/generated-types';
-import { AssetPreviewStrategy, getAssetType, RequestContext } from '@vendure/core';
+import { AssetPreviewStrategy, getAssetType, Logger, RequestContext } from '@vendure/core';
 import path from 'path';
 import sharp from 'sharp';
 
+import { loggerCtx } from './constants';
+
 export class SharpAssetPreviewStrategy implements AssetPreviewStrategy {
     constructor(
         private config: {
@@ -16,30 +18,32 @@ export class SharpAssetPreviewStrategy implements AssetPreviewStrategy {
         const { maxWidth, maxHeight } = this.config;
 
         if (assetType === AssetType.IMAGE) {
-            const image = sharp(data);
-            const metadata = await image.metadata();
-            const width = metadata.width || 0;
-            const height = metadata.height || 0;
-            if (maxWidth < width || maxHeight < height) {
-                return image.resize(maxWidth, maxHeight, { fit: 'inside' }).toBuffer();
-            } else {
-                if (mimeType === 'image/svg+xml') {
-                    // Convert the SVG to a raster for the preview
-                    return image.toBuffer();
+            try {
+                const image = sharp(data);
+                const metadata = await image.metadata();
+                const width = metadata.width || 0;
+                const height = metadata.height || 0;
+                if (maxWidth < width || maxHeight < height) {
+                    return image.rotate().resize(maxWidth, maxHeight, { fit: 'inside' }).toBuffer();
                 } else {
-                    return data;
+                    if (mimeType === 'image/svg+xml') {
+                        // Convert the SVG to a raster for the preview
+                        return image.toBuffer();
+                    } else {
+                        return image.rotate().toBuffer();
+                    }
                 }
+            } catch (err: any) {
+                Logger.error(
+                    `An error occurred when generating preview for image with mimeType ${mimeType}: ${
+                        err.message ?? err.toString()
+                    }`,
+                    loggerCtx,
+                );
+                return this.generateBinaryFilePreview(mimeType);
             }
         } else {
-            return sharp(path.join(__dirname, 'file-icon.png'))
-                .resize(800, 800, { fit: 'outside' })
-                .composite([
-                    {
-                        input: this.generateMimeTypeOverlay(mimeType),
-                        gravity: sharp.gravity.center,
-                    },
-                ])
-                .toBuffer();
+            return this.generateBinaryFilePreview(mimeType);
         }
     }
 
@@ -57,4 +61,16 @@ export class SharpAssetPreviewStrategy implements AssetPreviewStrategy {
               <text x="400" y="110"  text-anchor="middle" width="800">${mimeType}</text>
             </svg>`);
     }
+
+    private generateBinaryFilePreview(mimeType: string): Promise<Buffer> {
+        return sharp(path.join(__dirname, 'file-icon.png'))
+            .resize(800, 800, { fit: 'outside' })
+            .composite([
+                {
+                    input: this.generateMimeTypeOverlay(mimeType),
+                    gravity: sharp.gravity.center,
+                },
+            ])
+            .toBuffer();
+    }
 }

File diff suppressed because it is too large
+ 505 - 495
packages/core/e2e/graphql/generated-e2e-admin-types.ts


+ 2 - 2
packages/core/src/config/payment/dummy-payment-method-handler.ts

@@ -51,7 +51,7 @@ export const dummyPaymentHandler = new PaymentMethodHandler({
             defaultValue: false,
         },
     },
-    createPayment: async (ctx, order, amount, args, metadata) => {
+    createPayment: async (ctx, order, amount, args, metadata, method) => {
         if (metadata.shouldDecline) {
             return {
                 amount,
@@ -78,7 +78,7 @@ export const dummyPaymentHandler = new PaymentMethodHandler({
             };
         }
     },
-    settlePayment: async (ctx, order, payment, args) => {
+    settlePayment: async (ctx, order, payment, args, method) => {
         if (payment.metadata.shouldErrorOnSettle) {
             return {
                 success: false,

+ 2 - 2
packages/core/src/config/payment/example-payment-method-handler.ts

@@ -29,7 +29,7 @@ export const examplePaymentHandler = new PaymentMethodHandler({
         automaticCapture: { type: 'boolean', required: false },
         apiKey: { type: 'string', required: false },
     },
-    createPayment: async (ctx, order, amount, args, metadata): Promise<CreatePaymentResult> => {
+    createPayment: async (ctx, order, amount, args, metadata, method): Promise<CreatePaymentResult> => {
         try {
             const result = await gripeSDK.charges.create({
                 apiKey: args.apiKey,
@@ -52,7 +52,7 @@ export const examplePaymentHandler = new PaymentMethodHandler({
             };
         }
     },
-    settlePayment: async (ctx, order, payment, args) => {
+    settlePayment: async (ctx, order, payment, args, method) => {
         const result = await gripeSDK.charges.capture(payment.transactionId);
         return {
             success: result,

+ 10 - 4
packages/core/src/config/payment/payment-method-eligibility-checker.ts

@@ -7,7 +7,7 @@ import {
     ConfigurableOperationDef,
     ConfigurableOperationDefOptions,
 } from '../../common/configurable-operation';
-import { Order } from '../../entity/order/order.entity';
+import { Order, PaymentMethod } from '../../entity';
 
 /**
  * @description
@@ -45,7 +45,7 @@ export interface PaymentMethodEligibilityCheckerConfig<T extends ConfigArgs>
  * @docsWeight 0
  */
 export class PaymentMethodEligibilityChecker<
-    T extends ConfigArgs = ConfigArgs
+    T extends ConfigArgs = ConfigArgs,
 > extends ConfigurableOperationDef<T> {
     private readonly checkFn: CheckPaymentMethodEligibilityCheckerFn<T>;
 
@@ -60,8 +60,13 @@ export class PaymentMethodEligibilityChecker<
      *
      * @internal
      */
-    async check(ctx: RequestContext, order: Order, args: ConfigArg[]): Promise<boolean | string> {
-        return this.checkFn(ctx, order, this.argsArrayToHash(args));
+    async check(
+        ctx: RequestContext,
+        order: Order,
+        args: ConfigArg[],
+        method: PaymentMethod,
+    ): Promise<boolean | string> {
+        return this.checkFn(ctx, order, this.argsArrayToHash(args), method);
     }
 }
 
@@ -79,4 +84,5 @@ export type CheckPaymentMethodEligibilityCheckerFn<T extends ConfigArgs> = (
     ctx: RequestContext,
     order: Order,
     args: ConfigArgValues<T>,
+    method: PaymentMethod,
 ) => boolean | string | Promise<boolean | string>;

+ 16 - 5
packages/core/src/config/payment/payment-method-handler.ts

@@ -9,8 +9,7 @@ import {
 } from '../../common/configurable-operation';
 import { OnTransitionStartFn, StateMachineConfig } from '../../common/finite-state-machine/types';
 import { PaymentMetadata } from '../../common/types/common-types';
-import { Order } from '../../entity/order/order.entity';
-import { Payment } from '../../entity/payment/payment.entity';
+import { Order, Payment, PaymentMethod } from '../../entity';
 import {
     PaymentState,
     PaymentTransitionData,
@@ -158,6 +157,7 @@ export type CreatePaymentFn<T extends ConfigArgs> = (
     amount: number,
     args: ConfigArgValues<T>,
     metadata: PaymentMetadata,
+    method: PaymentMethod,
 ) => CreatePaymentResult | CreatePaymentErrorResult | Promise<CreatePaymentResult | CreatePaymentErrorResult>;
 
 /**
@@ -172,6 +172,7 @@ export type SettlePaymentFn<T extends ConfigArgs> = (
     order: Order,
     payment: Payment,
     args: ConfigArgValues<T>,
+    method: PaymentMethod,
 ) => SettlePaymentResult | SettlePaymentErrorResult | Promise<SettlePaymentResult | SettlePaymentErrorResult>;
 
 /**
@@ -188,6 +189,7 @@ export type CreateRefundFn<T extends ConfigArgs> = (
     order: Order,
     payment: Payment,
     args: ConfigArgValues<T>,
+    method: PaymentMethod,
 ) => CreateRefundResult | Promise<CreateRefundResult>;
 
 /**
@@ -311,6 +313,7 @@ export class PaymentMethodHandler<T extends ConfigArgs = ConfigArgs> extends Con
         amount: number,
         args: ConfigArg[],
         metadata: PaymentMetadata,
+        method: PaymentMethod,
     ) {
         const paymentConfig = await this.createPaymentFn(
             ctx,
@@ -318,6 +321,7 @@ export class PaymentMethodHandler<T extends ConfigArgs = ConfigArgs> extends Con
             amount,
             this.argsArrayToHash(args),
             metadata,
+            method,
         );
         return {
             method: this.code,
@@ -332,8 +336,14 @@ export class PaymentMethodHandler<T extends ConfigArgs = ConfigArgs> extends Con
      *
      * @internal
      */
-    async settlePayment(ctx: RequestContext, order: Order, payment: Payment, args: ConfigArg[]) {
-        return this.settlePaymentFn(ctx, order, payment, this.argsArrayToHash(args));
+    async settlePayment(
+        ctx: RequestContext,
+        order: Order,
+        payment: Payment,
+        args: ConfigArg[],
+        method: PaymentMethod,
+    ) {
+        return this.settlePaymentFn(ctx, order, payment, this.argsArrayToHash(args), method);
     }
 
     /**
@@ -349,9 +359,10 @@ export class PaymentMethodHandler<T extends ConfigArgs = ConfigArgs> extends Con
         order: Order,
         payment: Payment,
         args: ConfigArg[],
+        method: PaymentMethod,
     ) {
         return this.createRefundFn
-            ? this.createRefundFn(ctx, input, amount, order, payment, this.argsArrayToHash(args))
+            ? this.createRefundFn(ctx, input, amount, order, payment, this.argsArrayToHash(args), method)
             : false;
     }
 

+ 1 - 1
packages/core/src/event-bus/events/role-event.ts

@@ -9,7 +9,7 @@ type RoleInputTypes = CreateRoleInput | UpdateRoleInput | ID;
 
 /**
  * @description
- * This event is fired whenever one {@link Role} is  is added, updated or deleted.
+ * This event is fired whenever one {@link Role} is added, updated or deleted.
  *
  * @docsCategory events
  * @docsPage Event Types

+ 22 - 0
packages/core/src/event-bus/events/search-event.ts

@@ -0,0 +1,22 @@
+import { SearchInput } from '@vendure/common/lib/generated-types';
+
+import { RequestContext } from '../../api/common/request-context';
+import { VendureEvent } from '../vendure-event';
+
+type ExtendedSearchInput = SearchInput & {
+    [extendedInputField: string]: any;
+};
+
+/**
+ * @description
+ * This event is fired whenever a search query is executed.
+ *
+ * @docsCategory events
+ * @docsPage Event Types
+ * @since 1.6.0
+ */
+export class SearchEvent extends VendureEvent {
+    constructor(public ctx: RequestContext, public input: ExtendedSearchInput) {
+        super();
+    }
+}

+ 1 - 0
packages/core/src/event-bus/index.ts

@@ -47,6 +47,7 @@ export * from './events/promotion-event';
 export * from './events/refund-state-transition-event';
 export * from './events/role-change-event';
 export * from './events/role-event';
+export * from './events/search-event';
 export * from './events/shipping-method-event';
 export * from './events/stock-movement-event';
 export * from './events/tax-category-event';

+ 1 - 1
packages/core/src/job-queue/subscribable-job.ts

@@ -81,7 +81,7 @@ export class SubscribableJob<T extends JobData<T> = any> extends Job<T> {
                     return strategy.findOne(id);
                 }),
                 filter(notNullOrUndefined),
-                distinctUntilChanged((a, b) => a?.progress === b?.progress || a?.state === b?.state),
+                distinctUntilChanged((a, b) => a?.progress === b?.progress && a?.state === b?.state),
                 takeWhile(
                     job =>
                         job?.state !== JobState.FAILED &&

+ 3 - 0
packages/core/src/plugin/default-search-plugin/fulltext-search.service.ts

@@ -7,6 +7,7 @@ import { InternalServerError } from '../../common/error/errors';
 import { TransactionalConnection } from '../../connection/transactional-connection';
 import { Collection, FacetValue } from '../../entity';
 import { EventBus } from '../../event-bus/event-bus';
+import { SearchEvent } from '../../event-bus/events/search-event';
 import { Job } from '../../job-queue/job';
 import { CollectionService } from '../../service/services/collection.service';
 import { FacetValueService } from '../../service/services/facet-value.service';
@@ -54,6 +55,8 @@ export class FulltextSearchService {
     ): Promise<Omit<Omit<SearchResponse, 'facetValues'>, 'collections'>> {
         const items = await this._searchStrategy.getSearchResults(ctx, input, enabledOnly);
         const totalItems = await this._searchStrategy.getTotalCount(ctx, input, enabledOnly);
+        this.eventBus.publish(new SearchEvent(ctx, input));
+
         return {
             items,
             totalItems,

+ 1 - 1
packages/core/src/plugin/default-search-plugin/indexer/search-index.service.ts

@@ -159,7 +159,7 @@ export class SearchIndexService implements OnApplicationBootstrap {
                     });
                 },
                 error: (err: any) => {
-                    Logger.error(JSON.stringify(err));
+                    Logger.error(err.message || JSON.stringify(err), undefined, err.stack);
                     reject(err);
                 },
             });

+ 3 - 2
packages/core/src/service/helpers/list-query-builder/list-query-builder.ts

@@ -496,8 +496,9 @@ export class ListQueryBuilder implements OnApplicationBootstrap {
             qb.andWhere(
                 new Brackets(qb1 => {
                     qb1.where(`${translationsAlias}.languageCode = :languageCode`, { languageCode });
-
-                    if (languageCode !== this.configService.defaultLanguageCode) {
+                    const defaultLanguageCode =
+                        ctx?.channel.defaultLanguageCode ?? this.configService.defaultLanguageCode;
+                    if (languageCode !== defaultLanguageCode) {
                         // If the current languageCode is not the default, then we create a more
                         // complex WHERE clause to allow us to use the non-default translations and
                         // fall back to the default language if no translation exists.

+ 1 - 1
packages/core/src/service/services/payment-method.service.ts

@@ -177,7 +177,7 @@ export class PaymentMethodService {
                     'PaymentMethodEligibilityChecker',
                     method.checker.code,
                 );
-                const eligible = await checker.check(ctx, order, method.checker.args);
+                const eligible = await checker.check(ctx, order, method.checker.args, method);
                 if (eligible === false || typeof eligible === 'string') {
                     isEligible = false;
                     eligibilityMessage = typeof eligible === 'string' ? eligible : undefined;

+ 4 - 1
packages/core/src/service/services/payment.service.ts

@@ -117,7 +117,7 @@ export class PaymentService {
             method,
         );
         if (paymentMethod.checker && checker) {
-            const eligible = await checker.check(ctx, order, paymentMethod.checker.args);
+            const eligible = await checker.check(ctx, order, paymentMethod.checker.args, paymentMethod);
             if (eligible === false || typeof eligible === 'string') {
                 return new IneligiblePaymentMethodError({
                     eligibilityCheckerMessage: typeof eligible === 'string' ? eligible : undefined,
@@ -130,6 +130,7 @@ export class PaymentService {
             amount,
             paymentMethod.handler.args,
             metadata || {},
+            paymentMethod,
         );
         const initialState = 'Created';
         const payment = await this.connection
@@ -164,6 +165,7 @@ export class PaymentService {
             payment.order,
             payment,
             paymentMethod.handler.args,
+            paymentMethod,
         );
         const fromState = payment.state;
         let toState: PaymentState;
@@ -290,6 +292,7 @@ export class PaymentService {
                 order,
                 paymentToRefund,
                 paymentMethod.handler.args,
+                paymentMethod,
             );
             if (createRefundResult) {
                 refund.transactionId = createRefundResult.transactionId || '';

+ 1 - 0
packages/dev-server/package.json

@@ -26,6 +26,7 @@
     "@types/csv-stringify": "^3.1.0",
     "@vendure/testing": "^2.0.0-next.5",
     "@vendure/ui-devkit": "^2.0.0-next.5",
+    "commander": "^7.1.0",
     "concurrently": "^5.0.0",
     "csv-stringify": "^5.3.3",
     "progress": "^2.0.3"

+ 5 - 0
packages/elasticsearch-plugin/src/elasticsearch.service.ts

@@ -6,12 +6,14 @@ import {
     CollectionService,
     ConfigService,
     DeepRequired,
+    EventBus,
     FacetValue,
     FacetValueService,
     InternalServerError,
     Job,
     Logger,
     RequestContext,
+    SearchEvent,
     SearchService,
 } from '@vendure/core';
 import equal from 'fast-deep-equal/es6';
@@ -46,6 +48,7 @@ export class ElasticsearchService implements OnModuleInit, OnModuleDestroy {
         private configService: ConfigService,
         private facetValueService: FacetValueService,
         private collectionService: CollectionService,
+        private eventBus: EventBus,
     ) {
         searchService.adopt(this);
     }
@@ -191,6 +194,7 @@ export class ElasticsearchService implements OnModuleInit, OnModuleDestroy {
                     body: elasticSearchBody,
                 });
                 const totalItems = await this.totalHits(ctx, input, groupByProduct);
+                this.eventBus.publish(new SearchEvent(ctx, input));
                 return {
                     items: body.hits.hits.map(hit => this.mapProductToSearchResult(hit)),
                     totalItems,
@@ -215,6 +219,7 @@ export class ElasticsearchService implements OnModuleInit, OnModuleDestroy {
                     index: indexPrefix + VARIANT_INDEX_NAME,
                     body: elasticSearchBody,
                 });
+                this.eventBus.publish(new SearchEvent(ctx, input));
                 return {
                     items: body.hits.hits.map(hit => this.mapVariantToSearchResult(hit)),
                     totalItems: body.hits.total ? body.hits.total.value : 0,

+ 2 - 1
packages/elasticsearch-plugin/src/indexing/elasticsearch-index.service.ts

@@ -13,6 +13,7 @@ import {
 } from '@vendure/core';
 import { Observable } from 'rxjs';
 
+import { loggerCtx } from '../constants';
 import { UpdateIndexQueueJobData } from '../types';
 
 import { ElasticsearchIndexerController, ReindexMessageResponse } from './indexer.controller';
@@ -161,7 +162,7 @@ export class ElasticsearchIndexService implements OnApplicationBootstrap {
                     });
                 },
                 error: (err: any) => {
-                    Logger.error(JSON.stringify(err));
+                    Logger.error(err.message || JSON.stringify(err), loggerCtx, err.stack);
                     reject(err);
                 },
             });

+ 1 - 0
packages/job-queue-plugin/package.json

@@ -27,6 +27,7 @@
     "@vendure/common": "^2.0.0-next.5",
     "@vendure/core": "^2.0.0-next.5",
     "bullmq": "^1.74.3",
+    "redis": "^3.0.2",
     "rimraf": "^3.0.2",
     "typescript": "4.5.5"
   }

Some files were not shown because too many files changed in this diff