From e189bd40411a9c690c8ff3be707a1013e7167208 Mon Sep 17 00:00:00 2001 From: Michael Bromley Date: Tue, 10 Nov 2020 20:14:08 +0100 Subject: [PATCH] feat(admin-ui): Support for ShippingMethod translations & custom fields Relates to #530 --- .../facet-detail/facet-detail.component.ts | 37 ++--- .../lib/core/src/common/generated-types.ts | 24 ++- .../data/definitions/shipping-definitions.ts | 9 +- .../providers/shipping-method-data.service.ts | 16 +- ...ing-eligibility-test-result.component.html | 2 +- .../shipping-method-detail.component.html | 46 +++++- .../shipping-method-detail.component.ts | 145 +++++++++++++----- .../shipping-method-list.component.html | 4 +- .../routing/shipping-method-resolver.ts | 2 + .../src/lib/settings/src/settings.routes.ts | 2 +- packages/dev-server/dev-config.ts | 7 +- 11 files changed, 216 insertions(+), 78 deletions(-) diff --git a/packages/admin-ui/src/lib/catalog/src/components/facet-detail/facet-detail.component.ts b/packages/admin-ui/src/lib/catalog/src/components/facet-detail/facet-detail.component.ts index f627211fd1..d5f56febda 100644 --- a/packages/admin-ui/src/lib/catalog/src/components/facet-detail/facet-detail.component.ts +++ b/packages/admin-ui/src/lib/catalog/src/components/facet-detail/facet-detail.component.ts @@ -29,7 +29,8 @@ import { map, mapTo, mergeMap, switchMap, take } from 'rxjs/operators'; styleUrls: ['./facet-detail.component.scss'], changeDetection: ChangeDetectionStrategy.OnPush, }) -export class FacetDetailComponent extends BaseDetailComponent +export class FacetDetailComponent + extends BaseDetailComponent implements OnInit, OnDestroy { customFields: CustomFieldConfig[]; customValueFields: CustomFieldConfig[]; @@ -130,16 +131,16 @@ export class FacetDetailComponent extends BaseDetailComponent this.dataService.facet.getAllFacets(true).single$.pipe(mapTo(data))), + switchMap(data => this.dataService.facet.getAllFacets(true).single$.pipe(mapTo(data))), ) .subscribe( - (data) => { + data => { this.notificationService.success(_('common.notify-create-success'), { entity: 'Facet' }); this.detailForm.markAsPristine(); this.changeDetector.markForCheck(); this.router.navigate(['../', data.createFacet.id], { relativeTo: this.route }); }, - (err) => { + err => { this.notificationService.error(_('common.notify-create-error'), { entity: 'Facet', }); @@ -168,8 +169,8 @@ export class FacetDetailComponent extends BaseDetailComponent !c.value.id) - .map((c) => ({ + .filter(c => !c.value.id) + .map(c => ({ facetId: facet.id, code: c.value.code, translations: [{ name: c.value.name, languageCode }], @@ -201,7 +202,7 @@ export class FacetDetailComponent extends BaseDetailComponent { + err => { this.notificationService.error(_('common.notify-update-error'), { entity: 'Facet', }); @@ -221,16 +222,16 @@ export class FacetDetailComponent extends BaseDetailComponent { + switchMap(response => { if (response.result === DeletionResult.DELETED) { return [true]; } else { return this.showModalAndDelete(facetValueId, response.message || '').pipe( - map((r) => r.result === DeletionResult.DELETED), + map(r => r.result === DeletionResult.DELETED), ); } }), - switchMap((deleted) => (deleted ? this.dataService.facet.getFacet(this.id).single$ : [])), + switchMap(deleted => (deleted ? this.dataService.facet.getFacet(this.id).single$ : [])), ) .subscribe( () => { @@ -238,7 +239,7 @@ export class FacetDetailComponent extends BaseDetailComponent { + err => { this.notificationService.error(_('common.notify-delete-error'), { entity: 'FacetValue', }); @@ -257,10 +258,10 @@ export class FacetDetailComponent extends BaseDetailComponent + switchMap(result => result ? this.dataService.facet.deleteFacetValues([facetValueId], !!message) : EMPTY, ), - map((result) => result.deleteFacetValues[0]), + map(result => result.deleteFacetValues[0]), ); } @@ -268,13 +269,13 @@ export class FacetDetailComponent extends BaseDetailComponent t.languageCode === languageCode); + const currentTranslation = facet.translations.find(t => t.languageCode === languageCode); this.detailForm.patchValue({ facet: { code: facet.code, visible: !facet.isPrivate, - name: currentTranslation ? currentTranslation.name : '', + name: currentTranslation?.name ?? '', }, }); @@ -299,7 +300,7 @@ export class FacetDetailComponent extends BaseDetailComponent { const valueTranslation = - value.translations && value.translations.find((t) => t.languageCode === languageCode); + value.translations && value.translations.find(t => t.languageCode === languageCode); const group = { id: value.id, code: value.code, @@ -372,8 +373,8 @@ export class FacetDetailComponent extends BaseDetailComponent c.dirty && c.value.id) - .map((c) => c.value); + .filter(c => c.dirty && c.value.id) + .map(c => c.value); if (dirtyValues.length !== dirtyValueValues.length) { throw new Error(_(`error.facet-value-form-values-do-not-match`)); diff --git a/packages/admin-ui/src/lib/core/src/common/generated-types.ts b/packages/admin-ui/src/lib/core/src/common/generated-types.ts index b9d4dc3e06..30dad0cb7d 100644 --- a/packages/admin-ui/src/lib/core/src/common/generated-types.ts +++ b/packages/admin-ui/src/lib/core/src/common/generated-types.ts @@ -1891,7 +1891,6 @@ export type TestShippingMethodQuote = { __typename?: 'TestShippingMethodQuote'; price: Scalars['Int']; priceWithTax: Scalars['Int']; - description: Scalars['String']; metadata?: Maybe; }; @@ -3609,9 +3608,20 @@ export type ShippingMethod = Node & { description: Scalars['String']; checker: ConfigurableOperation; calculator: ConfigurableOperation; + translations: Array; customFields?: Maybe; }; +export type ShippingMethodTranslation = { + __typename?: 'ShippingMethodTranslation'; + id: Scalars['ID']; + createdAt: Scalars['DateTime']; + updatedAt: Scalars['DateTime']; + languageCode: LanguageCode; + name: Scalars['String']; + description: Scalars['String']; +}; + export type ShippingMethodList = PaginatedList & { __typename?: 'ShippingMethodList'; items: Array; @@ -7015,14 +7025,17 @@ export type ErrorResultFragment = ErrorResult_MimeTypeError_Fragment | ErrorResu export type ShippingMethodFragment = ( { __typename?: 'ShippingMethod' } - & Pick + & Pick & { checker: ( { __typename?: 'ConfigurableOperation' } & ConfigurableOperationFragment ), calculator: ( { __typename?: 'ConfigurableOperation' } & ConfigurableOperationFragment - ) } + ), translations: Array<( + { __typename?: 'ShippingMethodTranslation' } + & Pick + )> } ); export type GetShippingMethodListQueryVariables = Exact<{ @@ -7100,7 +7113,7 @@ export type TestShippingMethodQuery = { testShippingMethod: ( & Pick & { quote?: Maybe<( { __typename?: 'TestShippingMethodQuote' } - & Pick + & Pick )> } ) }; @@ -7111,7 +7124,7 @@ export type TestEligibleShippingMethodsQueryVariables = Exact<{ export type TestEligibleShippingMethodsQuery = { testEligibleShippingMethods: Array<( { __typename?: 'ShippingMethodQuote' } - & Pick + & Pick )> }; type DiscriminateUnion = T extends U ? T : never; @@ -8330,6 +8343,7 @@ export namespace ShippingMethod { export type Fragment = ShippingMethodFragment; export type Checker = (NonNullable); export type Calculator = (NonNullable); + export type Translations = NonNullable<(NonNullable)[number]>; } export namespace GetShippingMethodList { diff --git a/packages/admin-ui/src/lib/core/src/data/definitions/shipping-definitions.ts b/packages/admin-ui/src/lib/core/src/data/definitions/shipping-definitions.ts index 1b6d4733f7..2da3e01154 100644 --- a/packages/admin-ui/src/lib/core/src/data/definitions/shipping-definitions.ts +++ b/packages/admin-ui/src/lib/core/src/data/definitions/shipping-definitions.ts @@ -8,6 +8,7 @@ export const SHIPPING_METHOD_FRAGMENT = gql` createdAt updatedAt code + name description checker { ...ConfigurableOperation @@ -15,6 +16,12 @@ export const SHIPPING_METHOD_FRAGMENT = gql` calculator { ...ConfigurableOperation } + translations { + id + languageCode + name + description + } } ${CONFIGURABLE_OPERATION_FRAGMENT} `; @@ -86,7 +93,6 @@ export const TEST_SHIPPING_METHOD = gql` quote { price priceWithTax - description metadata } } @@ -97,6 +103,7 @@ export const TEST_ELIGIBLE_SHIPPING_METHODS = gql` query TestEligibleShippingMethods($input: TestEligibleShippingMethodsInput!) { testEligibleShippingMethods(input: $input) { id + name description price priceWithTax diff --git a/packages/admin-ui/src/lib/core/src/data/providers/shipping-method-data.service.ts b/packages/admin-ui/src/lib/core/src/data/providers/shipping-method-data.service.ts index a979e9158d..88f3229022 100644 --- a/packages/admin-ui/src/lib/core/src/data/providers/shipping-method-data.service.ts +++ b/packages/admin-ui/src/lib/core/src/data/providers/shipping-method-data.service.ts @@ -1,3 +1,5 @@ +import { pick } from '@vendure/common/lib/pick'; + import { CreateShippingMethod, CreateShippingMethodInput, @@ -54,20 +56,22 @@ export class ShippingMethodDataService { } createShippingMethod(input: CreateShippingMethodInput) { + const variables: CreateShippingMethod.Variables = { + input: pick(input, ['code', 'checker', 'calculator', 'customFields', 'translations']), + }; return this.baseDataService.mutate( CREATE_SHIPPING_METHOD, - { - input, - }, + variables, ); } updateShippingMethod(input: UpdateShippingMethodInput) { + const variables: UpdateShippingMethod.Variables = { + input: pick(input, ['id', 'code', 'checker', 'calculator', 'customFields', 'translations']), + }; return this.baseDataService.mutate( UPDATE_SHIPPING_METHOD, - { - input, - }, + variables, ); } diff --git a/packages/admin-ui/src/lib/settings/src/components/shipping-eligibility-test-result/shipping-eligibility-test-result.component.html b/packages/admin-ui/src/lib/settings/src/components/shipping-eligibility-test-result/shipping-eligibility-test-result.component.html index 25c814d521..8dd068e075 100644 --- a/packages/admin-ui/src/lib/settings/src/components/shipping-eligibility-test-result/shipping-eligibility-test-result.component.html +++ b/packages/admin-ui/src/lib/settings/src/components/shipping-eligibility-test-result/shipping-eligibility-test-result.component.html @@ -5,7 +5,7 @@
- {{ quote.description }} + {{ quote.name }}
diff --git a/packages/admin-ui/src/lib/settings/src/components/shipping-method-detail/shipping-method-detail.component.html b/packages/admin-ui/src/lib/settings/src/components/shipping-method-detail/shipping-method-detail.component.html index 385dbb5c7d..0ecc94d346 100644 --- a/packages/admin-ui/src/lib/settings/src/components/shipping-method-detail/shipping-method-detail.component.html +++ b/packages/admin-ui/src/lib/settings/src/components/shipping-method-detail/shipping-method-detail.component.html @@ -1,6 +1,12 @@ + @@ -18,7 +24,9 @@ class="btn btn-primary" (click)="save()" *vdrIfPermissions="'UpdateSettings'" - [disabled]="detailForm.pristine || detailForm.invalid || !selectedChecker || !selectedCalculator" + [disabled]=" + detailForm.pristine || detailForm.invalid || !selectedChecker || !selectedCalculator + " > {{ 'common.update' | translate }} @@ -27,18 +35,44 @@
- + - - + + + + +
+ + + + +
diff --git a/packages/admin-ui/src/lib/settings/src/components/shipping-method-detail/shipping-method-detail.component.ts b/packages/admin-ui/src/lib/settings/src/components/shipping-method-detail/shipping-method-detail.component.ts index e76663ee6e..2afc10c84f 100644 --- a/packages/admin-ui/src/lib/settings/src/components/shipping-method-detail/shipping-method-detail.component.ts +++ b/packages/admin-ui/src/lib/settings/src/components/shipping-method-detail/shipping-method-detail.component.ts @@ -7,17 +7,23 @@ import { ConfigurableOperation, ConfigurableOperationDefinition, ConfigurableOperationInput, + CreateFacetInput, CreateShippingMethodInput, + createUpdatedTranslatable, + CustomFieldConfig, DataService, encodeConfigArgValue, + FacetWithValues, GetActiveChannel, getConfigArgValue, getDefaultConfigArgValue, + LanguageCode, NotificationService, ServerConfigService, ShippingMethod, TestShippingMethodInput, TestShippingMethodResult, + UpdateFacetInput, UpdateShippingMethodInput, } from '@vendure/admin-ui/core'; import { normalizeString } from '@vendure/common/lib/normalize-string'; @@ -34,7 +40,8 @@ import { TestOrderLine } from '../test-order-builder/test-order-builder.componen styleUrls: ['./shipping-method-detail.component.scss'], changeDetection: ChangeDetectionStrategy.OnPush, }) -export class ShippingMethodDetailComponent extends BaseDetailComponent +export class ShippingMethodDetailComponent + extends BaseDetailComponent implements OnInit, OnDestroy { detailForm: FormGroup; checkers: ConfigurableOperationDefinition[] = []; @@ -48,6 +55,7 @@ export class ShippingMethodDetailComponent extends BaseDetailComponent; + customFields: CustomFieldConfig[]; private fetchTestResult$ = new Subject<[TestAddress, TestOrderLine[]]>(); constructor( @@ -60,11 +68,16 @@ export class ShippingMethodDetailComponent extends BaseDetailComponent ({ ...hash, [field.name]: '' }), {}), + ), }); } @@ -124,6 +137,10 @@ export class ShippingMethodDetailComponent extends BaseDetailComponent { - this.notificationService.success(_('common.notify-create-success'), { - entity: 'ShippingMethod', - }); - this.detailForm.markAsPristine(); - this.changeDetector.markForCheck(); - this.router.navigate(['../', data.createShippingMethod.id], { relativeTo: this.route }); - }, - err => { - this.notificationService.error(_('common.notify-create-error'), { - entity: 'ShippingMethod', - }); - }, - ); + combineLatest(this.entity$, this.languageCode$) + .pipe( + take(1), + mergeMap(([shippingMethod, languageCode]) => { + const formValue = this.detailForm.value; + const input = { + ...(this.getUpdatedShippingMethod( + shippingMethod, + this.detailForm, + languageCode, + ) as CreateShippingMethodInput), + checker: this.toAdjustmentOperationInput(selectedChecker, formValue.checker), + calculator: this.toAdjustmentOperationInput(selectedCalculator, formValue.calculator), + }; + return this.dataService.shippingMethod.createShippingMethod(input); + }), + ) + .subscribe( + data => { + this.notificationService.success(_('common.notify-create-success'), { + entity: 'ShippingMethod', + }); + this.detailForm.markAsPristine(); + this.changeDetector.markForCheck(); + this.router.navigate(['../', data.createShippingMethod.id], { relativeTo: this.route }); + }, + err => { + this.notificationService.error(_('common.notify-create-error'), { + entity: 'ShippingMethod', + }); + }, + ); } save() { @@ -199,15 +228,17 @@ export class ShippingMethodDetailComponent extends BaseDetailComponent { + mergeMap(([shippingMethod, languageCode]) => { const formValue = this.detailForm.value; - const input: UpdateShippingMethodInput = { - id, - code: formValue.code, - description: formValue.description, + const input = { + ...(this.getUpdatedShippingMethod( + shippingMethod, + this.detailForm, + languageCode, + ) as UpdateShippingMethodInput), checker: this.toAdjustmentOperationInput(selectedChecker, formValue.checker), calculator: this.toAdjustmentOperationInput(selectedCalculator, formValue.calculator), }; @@ -255,6 +286,29 @@ export class ShippingMethodDetailComponent extends BaseDetailComponent { + const input = createUpdatedTranslatable({ + translatable: shippingMethod, + updatedFields: formGroup.value, + customFieldConfig: this.customFields, + languageCode, + defaultTranslation: { + languageCode, + name: shippingMethod.name || '', + description: shippingMethod.description || '', + }, + }); + return input; + } + /** * Maps an array of conditions or actions to the input format expected by the GraphQL API. */ @@ -273,20 +327,37 @@ export class ShippingMethodDetailComponent extends BaseDetailComponent t.languageCode === languageCode); this.detailForm.patchValue({ - description: shippingMethod.description, + name: currentTranslation?.name ?? '', + description: currentTranslation?.description ?? '', code: shippingMethod.code, checker: shippingMethod.checker || {}, calculator: shippingMethod.calculator || {}, }); - this.selectedChecker = { + this.selectedChecker = shippingMethod.checker && { code: shippingMethod.checker.code, args: shippingMethod.checker.args.map(a => ({ ...a, value: getConfigArgValue(a.value) })), }; - this.selectedCalculator = { - code: shippingMethod.calculator.code, - args: shippingMethod.calculator.args.map(a => ({ ...a, value: getConfigArgValue(a.value) })), + this.selectedCalculator = shippingMethod.calculator && { + code: shippingMethod.calculator?.code, + args: shippingMethod.calculator?.args.map(a => ({ ...a, value: getConfigArgValue(a.value) })), }; + if (this.customFields.length) { + const customFieldsGroup = this.detailForm.get('customFields') as FormGroup; + + for (const fieldDef of this.customFields) { + const key = fieldDef.name; + const value = + fieldDef.type === 'localeString' + ? (currentTranslation as any).customFields[key] + : (shippingMethod as any).customFields[key]; + const control = customFieldsGroup.get(key); + if (control) { + control.patchValue(value); + } + } + } } } diff --git a/packages/admin-ui/src/lib/settings/src/components/shipping-method-list/shipping-method-list.component.html b/packages/admin-ui/src/lib/settings/src/components/shipping-method-list/shipping-method-list.component.html index c7c2e920c3..c27af80c7c 100644 --- a/packages/admin-ui/src/lib/settings/src/components/shipping-method-list/shipping-method-list.component.html +++ b/packages/admin-ui/src/lib/settings/src/components/shipping-method-list/shipping-method-list.component.html @@ -17,12 +17,12 @@ (itemsPerPageChange)="setItemsPerPage($event)" > {{ 'common.code' | translate }} - {{ 'common.description' | translate }} + {{ 'common.name' | translate }} {{ shippingMethod.code }} - {{ shippingMethod.description }} + {{ shippingMethod.name }} dataService.shippingMethod.getShippingMethod(id).mapStream(data => data.shippingMethod), ); diff --git a/packages/admin-ui/src/lib/settings/src/settings.routes.ts b/packages/admin-ui/src/lib/settings/src/settings.routes.ts index 5d2c11ce39..f6709a2981 100644 --- a/packages/admin-ui/src/lib/settings/src/settings.routes.ts +++ b/packages/admin-ui/src/lib/settings/src/settings.routes.ts @@ -247,7 +247,7 @@ export function shippingMethodBreadcrumb(data: any, params: any) { entity: data.entity, id: params.id, breadcrumbKey: 'breadcrumb.shipping-methods', - getName: method => method.description, + getName: method => method.name, route: 'shipping-methods', }); } diff --git a/packages/dev-server/dev-config.ts b/packages/dev-server/dev-config.ts index ae612a2268..fac1d2124e 100644 --- a/packages/dev-server/dev-config.ts +++ b/packages/dev-server/dev-config.ts @@ -52,7 +52,12 @@ export const devConfig: VendureConfig = { paymentOptions: { paymentMethodHandlers: [examplePaymentHandler], }, - customFields: {}, + customFields: { + ShippingMethod: [ + { name: 'isGood', type: 'boolean' }, + { name: 'localName', type: 'localeString' }, + ], + }, logger: new DefaultLogger({ level: LogLevel.Info }), importExportOptions: { importAssetsDir: path.join(__dirname, 'import-assets'),