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 274b1ad26b..944e6b4c7b 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 @@ -804,9 +804,9 @@ export type CreatePromotionInput = { customFields?: InputMaybe; enabled: Scalars['Boolean']; endsAt?: InputMaybe; - name: Scalars['String']; perCustomerUsageLimit?: InputMaybe; startsAt?: InputMaybe; + translations: Array; }; export type CreatePromotionResult = MissingConditionsError | Promotion; @@ -4487,18 +4487,21 @@ export type Promotion = Node & { couponCode?: Maybe; createdAt: Scalars['DateTime']; customFields?: Maybe; + description: Scalars['String']; enabled: Scalars['Boolean']; endsAt?: Maybe; id: Scalars['ID']; name: Scalars['String']; perCustomerUsageLimit?: Maybe; startsAt?: Maybe; + translations: Array; updatedAt: Scalars['DateTime']; }; export type PromotionFilterParameter = { couponCode?: InputMaybe; createdAt?: InputMaybe; + description?: InputMaybe; enabled?: InputMaybe; endsAt?: InputMaybe; id?: InputMaybe; @@ -4530,6 +4533,7 @@ export type PromotionListOptions = { export type PromotionSortParameter = { couponCode?: InputMaybe; createdAt?: InputMaybe; + description?: InputMaybe; endsAt?: InputMaybe; id?: InputMaybe; name?: InputMaybe; @@ -4538,6 +4542,24 @@ export type PromotionSortParameter = { updatedAt?: InputMaybe; }; +export type PromotionTranslation = { + __typename?: 'PromotionTranslation'; + createdAt: Scalars['DateTime']; + description: Scalars['String']; + id: Scalars['ID']; + languageCode: LanguageCode; + name: Scalars['String']; + updatedAt: Scalars['DateTime']; +}; + +export type PromotionTranslationInput = { + customFields?: InputMaybe; + description?: InputMaybe; + id?: InputMaybe; + languageCode: LanguageCode; + name?: InputMaybe; +}; + /** Returned if the specified quantity of an OrderLine is greater than the number of items in that line */ export type QuantityTooGreatError = ErrorResult & { __typename?: 'QuantityTooGreatError'; @@ -5878,9 +5900,9 @@ export type UpdatePromotionInput = { enabled?: InputMaybe; endsAt?: InputMaybe; id: Scalars['ID']; - name?: InputMaybe; perCustomerUsageLimit?: InputMaybe; startsAt?: InputMaybe; + translations?: InputMaybe>; }; export type UpdatePromotionResult = MissingConditionsError | Promotion; @@ -7088,21 +7110,21 @@ export type DeleteTagMutationVariables = Exact<{ export type DeleteTagMutation = { deleteTag: { __typename?: 'DeletionResponse', message?: string | null, result: DeletionResult } }; -export type PromotionFragment = { __typename?: 'Promotion', id: string, createdAt: any, updatedAt: any, name: string, enabled: boolean, couponCode?: string | null, perCustomerUsageLimit?: number | null, startsAt?: any | null, endsAt?: any | null, conditions: Array<{ __typename?: 'ConfigurableOperation', code: string, args: Array<{ __typename?: 'ConfigArg', name: string, value: string }> }>, actions: Array<{ __typename?: 'ConfigurableOperation', code: string, args: Array<{ __typename?: 'ConfigArg', name: string, value: string }> }> }; +export type PromotionFragment = { __typename?: 'Promotion', id: string, createdAt: any, updatedAt: any, name: string, description: string, enabled: boolean, couponCode?: string | null, perCustomerUsageLimit?: number | null, startsAt?: any | null, endsAt?: any | null, conditions: Array<{ __typename?: 'ConfigurableOperation', code: string, args: Array<{ __typename?: 'ConfigArg', name: string, value: string }> }>, actions: Array<{ __typename?: 'ConfigurableOperation', code: string, args: Array<{ __typename?: 'ConfigArg', name: string, value: string }> }>, translations: Array<{ __typename?: 'PromotionTranslation', id: string, languageCode: LanguageCode, name: string, description: string }> }; export type GetPromotionListQueryVariables = Exact<{ options?: InputMaybe; }>; -export type GetPromotionListQuery = { promotions: { __typename?: 'PromotionList', totalItems: number, items: Array<{ __typename?: 'Promotion', id: string, createdAt: any, updatedAt: any, name: string, enabled: boolean, couponCode?: string | null, perCustomerUsageLimit?: number | null, startsAt?: any | null, endsAt?: any | null, conditions: Array<{ __typename?: 'ConfigurableOperation', code: string, args: Array<{ __typename?: 'ConfigArg', name: string, value: string }> }>, actions: Array<{ __typename?: 'ConfigurableOperation', code: string, args: Array<{ __typename?: 'ConfigArg', name: string, value: string }> }> }> } }; +export type GetPromotionListQuery = { promotions: { __typename?: 'PromotionList', totalItems: number, items: Array<{ __typename?: 'Promotion', id: string, createdAt: any, updatedAt: any, name: string, description: string, enabled: boolean, couponCode?: string | null, perCustomerUsageLimit?: number | null, startsAt?: any | null, endsAt?: any | null, conditions: Array<{ __typename?: 'ConfigurableOperation', code: string, args: Array<{ __typename?: 'ConfigArg', name: string, value: string }> }>, actions: Array<{ __typename?: 'ConfigurableOperation', code: string, args: Array<{ __typename?: 'ConfigArg', name: string, value: string }> }>, translations: Array<{ __typename?: 'PromotionTranslation', id: string, languageCode: LanguageCode, name: string, description: string }> }> } }; export type GetPromotionQueryVariables = Exact<{ id: Scalars['ID']; }>; -export type GetPromotionQuery = { promotion?: { __typename?: 'Promotion', id: string, createdAt: any, updatedAt: any, name: string, enabled: boolean, couponCode?: string | null, perCustomerUsageLimit?: number | null, startsAt?: any | null, endsAt?: any | null, conditions: Array<{ __typename?: 'ConfigurableOperation', code: string, args: Array<{ __typename?: 'ConfigArg', name: string, value: string }> }>, actions: Array<{ __typename?: 'ConfigurableOperation', code: string, args: Array<{ __typename?: 'ConfigArg', name: string, value: string }> }> } | null }; +export type GetPromotionQuery = { promotion?: { __typename?: 'Promotion', id: string, createdAt: any, updatedAt: any, name: string, description: string, enabled: boolean, couponCode?: string | null, perCustomerUsageLimit?: number | null, startsAt?: any | null, endsAt?: any | null, conditions: Array<{ __typename?: 'ConfigurableOperation', code: string, args: Array<{ __typename?: 'ConfigArg', name: string, value: string }> }>, actions: Array<{ __typename?: 'ConfigurableOperation', code: string, args: Array<{ __typename?: 'ConfigArg', name: string, value: string }> }>, translations: Array<{ __typename?: 'PromotionTranslation', id: string, languageCode: LanguageCode, name: string, description: string }> } | null }; export type GetAdjustmentOperationsQueryVariables = Exact<{ [key: string]: never; }>; @@ -7114,14 +7136,14 @@ export type CreatePromotionMutationVariables = Exact<{ }>; -export type CreatePromotionMutation = { createPromotion: { __typename?: 'MissingConditionsError', errorCode: ErrorCode, message: string } | { __typename?: 'Promotion', id: string, createdAt: any, updatedAt: any, name: string, enabled: boolean, couponCode?: string | null, perCustomerUsageLimit?: number | null, startsAt?: any | null, endsAt?: any | null, conditions: Array<{ __typename?: 'ConfigurableOperation', code: string, args: Array<{ __typename?: 'ConfigArg', name: string, value: string }> }>, actions: Array<{ __typename?: 'ConfigurableOperation', code: string, args: Array<{ __typename?: 'ConfigArg', name: string, value: string }> }> } }; +export type CreatePromotionMutation = { createPromotion: { __typename?: 'MissingConditionsError', errorCode: ErrorCode, message: string } | { __typename?: 'Promotion', id: string, createdAt: any, updatedAt: any, name: string, description: string, enabled: boolean, couponCode?: string | null, perCustomerUsageLimit?: number | null, startsAt?: any | null, endsAt?: any | null, conditions: Array<{ __typename?: 'ConfigurableOperation', code: string, args: Array<{ __typename?: 'ConfigArg', name: string, value: string }> }>, actions: Array<{ __typename?: 'ConfigurableOperation', code: string, args: Array<{ __typename?: 'ConfigArg', name: string, value: string }> }>, translations: Array<{ __typename?: 'PromotionTranslation', id: string, languageCode: LanguageCode, name: string, description: string }> } }; export type UpdatePromotionMutationVariables = Exact<{ input: UpdatePromotionInput; }>; -export type UpdatePromotionMutation = { updatePromotion: { __typename?: 'MissingConditionsError' } | { __typename?: 'Promotion', id: string, createdAt: any, updatedAt: any, name: string, enabled: boolean, couponCode?: string | null, perCustomerUsageLimit?: number | null, startsAt?: any | null, endsAt?: any | null, conditions: Array<{ __typename?: 'ConfigurableOperation', code: string, args: Array<{ __typename?: 'ConfigArg', name: string, value: string }> }>, actions: Array<{ __typename?: 'ConfigurableOperation', code: string, args: Array<{ __typename?: 'ConfigArg', name: string, value: string }> }> } }; +export type UpdatePromotionMutation = { updatePromotion: { __typename?: 'MissingConditionsError' } | { __typename?: 'Promotion', id: string, createdAt: any, updatedAt: any, name: string, description: string, enabled: boolean, couponCode?: string | null, perCustomerUsageLimit?: number | null, startsAt?: any | null, endsAt?: any | null, conditions: Array<{ __typename?: 'ConfigurableOperation', code: string, args: Array<{ __typename?: 'ConfigArg', name: string, value: string }> }>, actions: Array<{ __typename?: 'ConfigurableOperation', code: string, args: Array<{ __typename?: 'ConfigArg', name: string, value: string }> }>, translations: Array<{ __typename?: 'PromotionTranslation', id: string, languageCode: LanguageCode, name: string, description: string }> } }; export type DeletePromotionMutationVariables = Exact<{ id: Scalars['ID']; diff --git a/packages/admin-ui/src/lib/core/src/data/definitions/promotion-definitions.ts b/packages/admin-ui/src/lib/core/src/data/definitions/promotion-definitions.ts index fbe79640cc..89fe8a8251 100644 --- a/packages/admin-ui/src/lib/core/src/data/definitions/promotion-definitions.ts +++ b/packages/admin-ui/src/lib/core/src/data/definitions/promotion-definitions.ts @@ -12,6 +12,7 @@ export const PROMOTION_FRAGMENT = gql` createdAt updatedAt name + description enabled couponCode perCustomerUsageLimit @@ -23,6 +24,12 @@ export const PROMOTION_FRAGMENT = gql` actions { ...ConfigurableOperation } + translations { + id + languageCode + name + description + } } ${CONFIGURABLE_OPERATION_FRAGMENT} `; diff --git a/packages/admin-ui/src/lib/core/src/data/providers/promotion-data.service.ts b/packages/admin-ui/src/lib/core/src/data/providers/promotion-data.service.ts index cccca526aa..20e431b00b 100644 --- a/packages/admin-ui/src/lib/core/src/data/providers/promotion-data.service.ts +++ b/packages/admin-ui/src/lib/core/src/data/providers/promotion-data.service.ts @@ -1,3 +1,5 @@ +import { pick } from '@vendure/common/lib/pick'; + import * as Codegen from '../../common/generated-types'; import { CREATE_PROMOTION, @@ -44,7 +46,17 @@ export class PromotionDataService { Codegen.CreatePromotionMutation, Codegen.CreatePromotionMutationVariables >(CREATE_PROMOTION, { - input, + input: pick(input, [ + 'conditions', + 'actions', + 'couponCode', + 'startsAt', + 'endsAt', + 'perCustomerUsageLimit', + 'enabled', + 'translations', + 'customFields', + ]), }); } @@ -53,7 +65,18 @@ export class PromotionDataService { Codegen.UpdatePromotionMutation, Codegen.UpdatePromotionMutationVariables >(UPDATE_PROMOTION, { - input, + input: pick(input, [ + 'id', + 'conditions', + 'actions', + 'couponCode', + 'startsAt', + 'endsAt', + 'perCustomerUsageLimit', + 'enabled', + 'translations', + 'customFields', + ]), }); } diff --git a/packages/admin-ui/src/lib/marketing/src/components/promotion-detail/promotion-detail.component.html b/packages/admin-ui/src/lib/marketing/src/components/promotion-detail/promotion-detail.component.html index 9f3cc02498..51615a235d 100644 --- a/packages/admin-ui/src/lib/marketing/src/components/promotion-detail/promotion-detail.component.html +++ b/packages/admin-ui/src/lib/marketing/src/components/promotion-detail/promotion-detail.component.html @@ -6,6 +6,12 @@ + @@ -41,6 +47,11 @@ formControlName="name" /> + diff --git a/packages/admin-ui/src/lib/marketing/src/components/promotion-detail/promotion-detail.component.ts b/packages/admin-ui/src/lib/marketing/src/components/promotion-detail/promotion-detail.component.ts index e64efd9f37..9b80ff3383 100644 --- a/packages/admin-ui/src/lib/marketing/src/components/promotion-detail/promotion-detail.component.ts +++ b/packages/admin-ui/src/lib/marketing/src/components/promotion-detail/promotion-detail.component.ts @@ -7,19 +7,25 @@ import { ConfigurableOperation, ConfigurableOperationDefinition, ConfigurableOperationInput, + CreatePaymentMethodInput, CreatePromotionInput, + createUpdatedTranslatable, CustomFieldConfig, DataService, encodeConfigArgValue, + findTranslation, getConfigArgValue, getDefaultConfigArgValue, LanguageCode, NotificationService, + PaymentMethodFragment, PromotionFragment, ServerConfigService, + toConfigurableOperationInput, + UpdatePaymentMethodInput, UpdatePromotionInput, } from '@vendure/admin-ui/core'; -import { Observable } from 'rxjs'; +import { combineLatest, Observable } from 'rxjs'; import { mergeMap, take } from 'rxjs/operators'; @Component({ @@ -54,6 +60,7 @@ export class PromotionDetailComponent this.customFields = this.getCustomFieldConfig('Promotion'); this.detailForm = this.formBuilder.group({ name: ['', Validators.required], + description: '', enabled: true, couponCode: null, perCustomerUsageLimit: null, @@ -134,63 +141,55 @@ export class PromotionDetailComponent if (!this.detailForm.dirty) { return; } - const formValue = this.detailForm.value; - const input: CreatePromotionInput = { - name: formValue.name, - enabled: true, - couponCode: formValue.couponCode, - perCustomerUsageLimit: formValue.perCustomerUsageLimit, - startsAt: formValue.startsAt, - endsAt: formValue.endsAt, - conditions: this.mapOperationsToInputs(this.conditions, formValue.conditions), - actions: this.mapOperationsToInputs(this.actions, formValue.actions), - customFields: formValue.customFields, - }; - this.dataService.promotion.createPromotion(input).subscribe( - ({ createPromotion }) => { - switch (createPromotion.__typename) { - case 'Promotion': - this.notificationService.success(_('common.notify-create-success'), { - entity: 'Promotion', - }); - this.detailForm.markAsPristine(); - this.changeDetector.markForCheck(); - this.router.navigate(['../', createPromotion.id], { relativeTo: this.route }); - break; - case 'MissingConditionsError': - this.notificationService.error(createPromotion.message); - break; - } - }, - err => { - this.notificationService.error(_('common.notify-create-error'), { - entity: 'Promotion', - }); - }, - ); + combineLatest(this.entity$, this.languageCode$) + .pipe( + take(1), + mergeMap(([promotion, languageCode]) => { + const input = this.getUpdatedPromotion( + promotion, + this.detailForm, + languageCode, + ) as CreatePromotionInput; + return this.dataService.promotion.createPromotion(input); + }), + ) + .subscribe( + ({ createPromotion }) => { + switch (createPromotion.__typename) { + case 'Promotion': + this.notificationService.success(_('common.notify-create-success'), { + entity: 'Promotion', + }); + this.detailForm.markAsPristine(); + this.changeDetector.markForCheck(); + this.router.navigate(['../', createPromotion.id], { relativeTo: this.route }); + break; + case 'MissingConditionsError': + this.notificationService.error(createPromotion.message); + break; + } + }, + err => { + this.notificationService.error(_('common.notify-create-error'), { + entity: 'Promotion', + }); + }, + ); } save() { if (!this.detailForm.dirty) { return; } - const formValue = this.detailForm.value; - this.promotion$ + combineLatest(this.entity$, this.languageCode$) .pipe( take(1), - mergeMap(promotion => { - const input: UpdatePromotionInput = { - id: promotion.id, - name: formValue.name, - enabled: formValue.enabled, - couponCode: formValue.couponCode, - perCustomerUsageLimit: formValue.perCustomerUsageLimit, - startsAt: formValue.startsAt, - endsAt: formValue.endsAt, - conditions: this.mapOperationsToInputs(this.conditions, formValue.conditions), - actions: this.mapOperationsToInputs(this.actions, formValue.actions), - customFields: formValue.customFields, - }; + mergeMap(([paymentMethod, languageCode]) => { + const input = this.getUpdatedPromotion( + paymentMethod, + this.detailForm, + languageCode, + ) as UpdatePromotionInput; return this.dataService.promotion.updatePromotion(input); }), ) @@ -210,12 +209,43 @@ export class PromotionDetailComponent ); } + /** + * Given a PaymentMethod and the value of the detailForm, this method creates an updated copy of it which + * can then be persisted to the API. + */ + private getUpdatedPromotion( + promotion: PromotionFragment, + formGroup: FormGroup, + languageCode: LanguageCode, + ): UpdatePromotionInput | CreatePromotionInput { + const formValue = formGroup.value; + const input = createUpdatedTranslatable({ + translatable: promotion, + updatedFields: formValue, + customFieldConfig: this.customFields, + languageCode, + defaultTranslation: { + languageCode, + name: promotion.name || '', + description: promotion.description || '', + }, + }); + + return { + ...input, + conditions: this.mapOperationsToInputs(this.conditions, formValue.conditions), + actions: this.mapOperationsToInputs(this.actions, formValue.actions), + }; + } + /** * Update the form values when the entity changes. */ protected setFormValues(entity: PromotionFragment, languageCode: LanguageCode): void { + const currentTranslation = findTranslation(entity, languageCode); this.detailForm.patchValue({ - name: entity.name, + name: currentTranslation?.name, + description: currentTranslation?.description, enabled: entity.enabled, couponCode: entity.couponCode, perCustomerUsageLimit: entity.perCustomerUsageLimit, diff --git a/packages/admin-ui/src/lib/marketing/src/providers/routing/promotion-resolver.ts b/packages/admin-ui/src/lib/marketing/src/providers/routing/promotion-resolver.ts index f0cfa73b8f..0d9998f6c9 100644 --- a/packages/admin-ui/src/lib/marketing/src/providers/routing/promotion-resolver.ts +++ b/packages/admin-ui/src/lib/marketing/src/providers/routing/promotion-resolver.ts @@ -18,9 +18,12 @@ export class PromotionResolver extends BaseEntityResolver { createdAt: '', updatedAt: '', name: '', + description: '', + couponCode: '', enabled: false, conditions: [], actions: [], + translations: [], }, id => dataService.promotion.getPromotion(id).mapStream(data => data.promotion), ); diff --git a/packages/admin-ui/src/lib/settings/src/components/payment-method-detail/payment-method-detail.component.ts b/packages/admin-ui/src/lib/settings/src/components/payment-method-detail/payment-method-detail.component.ts index 2d1d3eb4d4..8a64568477 100644 --- a/packages/admin-ui/src/lib/settings/src/components/payment-method-detail/payment-method-detail.component.ts +++ b/packages/admin-ui/src/lib/settings/src/components/payment-method-detail/payment-method-detail.component.ts @@ -219,7 +219,7 @@ export class PaymentMethodDetailComponent } /** - * Given a facet and the value of the detailForm, this method creates an updated copy of the facet which + * Given a PaymentMethod and the value of the detailForm, this method creates an updated copy of it which * can then be persisted to the API. */ private getUpdatedPaymentMethod(