Skip to content

Commit

Permalink
fix(admin-ui): Add custom field controls to ProductOption dialog
Browse files Browse the repository at this point in the history
Fixes #382
  • Loading branch information
michaelbromley committed Jun 29, 2020
1 parent 56449b8 commit 4678360
Show file tree
Hide file tree
Showing 20 changed files with 422 additions and 129 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,8 @@ <h4>{{ 'catalog.product-variants' | translate }}</h4>
[productVariantsFormArray]="detailForm.get('variants')"
[taxCategories]="taxCategories$ | async"
[customFields]="customVariantFields"
[customOptionFields]="customOptionFields"
[activeLanguage]="languageCode$ | async"
(assetChange)="variantAssetChange($event)"
(updateProductOption)="updateProductOption($event)"
(selectionChange)="selectedVariantIds = $event"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,8 @@ export class ProductDetailComponent extends BaseDetailComponent<ProductWithVaria
taxCategories$: Observable<TaxCategory.Fragment[]>;
customFields: CustomFieldConfig[];
customVariantFields: CustomFieldConfig[];
customOptionGroupFields: CustomFieldConfig[];
customOptionFields: CustomFieldConfig[];
detailForm: FormGroup;
assetChanges: SelectedAssets = {};
variantAssetChanges: { [variantId: string]: SelectedAssets } = {};
Expand All @@ -101,6 +103,8 @@ export class ProductDetailComponent extends BaseDetailComponent<ProductWithVaria
super(route, router, serverConfigService, dataService);
this.customFields = this.getCustomFieldConfig('Product');
this.customVariantFields = this.getCustomFieldConfig('ProductVariant');
this.customOptionGroupFields = this.getCustomFieldConfig('ProductOptionGroup');
this.customOptionFields = this.getCustomFieldConfig('ProductOption');
this.detailForm = this.formBuilder.group({
product: this.formBuilder.group({
enabled: true,
Expand Down Expand Up @@ -156,7 +160,8 @@ export class ProductDetailComponent extends BaseDetailComponent<ProductWithVaria
}

navigateToTab(tabName: TabName) {
this.router.navigate(['./', { tab: tabName }], {
this.router.navigate(['./', { ...this.route.snapshot.params, tab: tabName }], {
queryParamsHandling: 'merge',
relativeTo: this.route,
state: {
[IGNORE_CAN_DEACTIVATE_GUARD]: true,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,19 +1,19 @@
import { ChangeDetectionStrategy, Component, OnInit } from '@angular/core';
import { ActivatedRoute } from '@angular/router';
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
import { DeactivateAware } from '@vendure/admin-ui/core';
import { NotificationService } from '@vendure/admin-ui/core';
import { ModalService } from '@vendure/admin-ui/core';
import { getDefaultUiLanguage } from '@vendure/admin-ui/core';
import {
CreateProductOptionGroup,
CreateProductOptionInput,
CurrencyCode,
DataService,
DeactivateAware,
getDefaultUiLanguage,
GetProductVariantOptions,
LanguageCode,
ProductOptionGroupFragment,
ModalService,
NotificationService,
ProductOptionGroupWithOptionsFragment,
} from '@vendure/admin-ui/core';
import { DataService } from '@vendure/admin-ui/core';
import { normalizeString } from '@vendure/common/lib/normalize-string';
import { generateAllCombinations, notNullOrUndefined } from '@vendure/common/lib/shared-utils';
import { EMPTY, forkJoin, Observable, of } from 'rxjs';
Expand Down Expand Up @@ -290,7 +290,7 @@ export class ProductVariantsEditorComponent implements OnInit, DeactivateAware {
}
}

private fetchOptionGroups(groupsIds: string[]): Observable<ProductOptionGroupFragment[]> {
private fetchOptionGroups(groupsIds: string[]): Observable<ProductOptionGroupWithOptionsFragment[]> {
return forkJoin(
groupsIds.map((id) =>
this.dataService.product
Expand All @@ -301,7 +301,7 @@ export class ProductVariantsEditorComponent implements OnInit, DeactivateAware {
);
}

private createNewProductVariants(groups: ProductOptionGroupFragment[]) {
private createNewProductVariants(groups: ProductOptionGroupWithOptionsFragment[]) {
const options = groups
.filter(notNullOrUndefined)
.map((og) => og.options)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@
<div
class="variant-container card"
*ngFor="let variant of variants; let i = index"
[class.disabled]="!formArray.get([i, 'enabled'])!.value"
[class.disabled]="!formArray.get([i, 'enabled'])?.value"
>
<ng-container [formGroup]="formArray.at(i)">
<ng-container *ngIf="formArray.at(i)" [formGroup]="formArray.at(i)">
<div class="card-block header-row">
<div class="details">
<vdr-title-input class="sku" [readonly]="!('UpdateCatalog' | hasPermission)">
Expand Down Expand Up @@ -145,7 +145,7 @@
[icon]="('UpdateCatalog' | hasPermission) && 'pencil'"
>
<span class="option-group-name">{{ optionGroupName(option.groupId) }}</span>
{{ option.name }}
{{ optionName(option) }}
</vdr-chip>
</div>
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,20 +11,22 @@ import {
SimpleChanges,
} from '@angular/core';
import { FormArray } from '@angular/forms';
import { notNullOrUndefined } from '@vendure/common/lib/shared-utils';
import { Subscription } from 'rxjs';

import {
CustomFieldConfig,
FacetValue,
FacetWithValues,
LanguageCode,
ProductOptionFragment,
ProductVariant,
ProductWithVariants,
TaxCategory,
UpdateProductOptionInput,
} from '@vendure/admin-ui/core';
import { flattenFacetValues } from '@vendure/admin-ui/core';
import { ModalService } from '@vendure/admin-ui/core';
import { notNullOrUndefined } from '@vendure/common/lib/shared-utils';
import { Subscription } from 'rxjs';

import { AssetChange } from '../product-assets/product-assets.component';
import { VariantFormValue } from '../product-detail/product-detail.component';
import { UpdateProductOptionDialogComponent } from '../update-product-option-dialog/update-product-option-dialog.component';
Expand All @@ -46,6 +48,8 @@ export class ProductVariantsListComponent implements OnChanges, OnInit, OnDestro
@Input() facets: FacetWithValues.Fragment[];
@Input() optionGroups: ProductWithVariants.OptionGroups[];
@Input() customFields: CustomFieldConfig[];
@Input() customOptionFields: CustomFieldConfig[];
@Input() activeLanguage: LanguageCode;
@Output() assetChange = new EventEmitter<VariantAssetChange>();
@Output() selectionChange = new EventEmitter<string[]>();
@Output() selectFacetValueClick = new EventEmitter<string[]>();
Expand Down Expand Up @@ -77,7 +81,7 @@ export class ProductVariantsListComponent implements OnChanges, OnInit, OnDestro
getTaxCategoryName(index: number): string {
const control = this.formArray.at(index).get(['taxCategoryId']);
if (control && this.taxCategories) {
const match = this.taxCategories.find(t => t.id === control.value);
const match = this.taxCategories.find((t) => t.id === control.value);
return match ? match.name : '';
}
return '';
Expand All @@ -92,15 +96,15 @@ export class ProductVariantsListComponent implements OnChanges, OnInit, OnDestro
variantId,
...event,
});
const index = this.variants.findIndex(v => v.id === variantId);
const index = this.variants.findIndex((v) => v.id === variantId);
this.formArray.at(index).markAsDirty();
}

toggleSelectAll() {
if (this.areAllSelected()) {
this.selectedVariantIds = [];
} else {
this.selectedVariantIds = this.variants.map(v => v.id);
this.selectedVariantIds = this.variants.map((v) => v.id);
}
this.selectionChange.emit(this.selectedVariantIds);
}
Expand All @@ -116,17 +120,28 @@ export class ProductVariantsListComponent implements OnChanges, OnInit, OnDestro
}

optionGroupName(optionGroupId: string): string | undefined {
const group = this.optionGroups.find(g => g.id === optionGroupId);
return group && group.name;
const group = this.optionGroups.find((g) => g.id === optionGroupId);
if (group) {
const translation =
group?.translations.find((t) => t.languageCode === this.activeLanguage) ??
group.translations[0];
return translation.name;
}
}

optionName(option: ProductOptionFragment) {
const translation =
option.translations.find((t) => t.languageCode === this.activeLanguage) ?? option.translations[0];
return translation.name;
}

pendingFacetValues(index: number) {
if (this.facets) {
const formFacetValueIds = this.getFacetValueIds(index);
const variantFacetValueIds = this.variants[index].facetValues.map(fv => fv.id);
const variantFacetValueIds = this.variants[index].facetValues.map((fv) => fv.id);
return formFacetValueIds
.filter(x => !variantFacetValueIds.includes(x))
.map(id => this.facetValues.find(fv => fv.id === id))
.filter((x) => !variantFacetValueIds.includes(x))
.map((id) => this.facetValues.find((fv) => fv.id === id))
.filter(notNullOrUndefined);
} else {
return [];
Expand All @@ -136,18 +151,18 @@ export class ProductVariantsListComponent implements OnChanges, OnInit, OnDestro
existingFacetValues(index: number) {
const variant = this.variants[index];
const formFacetValueIds = this.getFacetValueIds(index);
const intersection = [...formFacetValueIds].filter(x =>
variant.facetValues.map(fv => fv.id).includes(x),
const intersection = [...formFacetValueIds].filter((x) =>
variant.facetValues.map((fv) => fv.id).includes(x),
);
return intersection
.map(id => variant.facetValues.find(fv => fv.id === id))
.map((id) => variant.facetValues.find((fv) => fv.id === id))
.filter(notNullOrUndefined);
}

removeFacetValue(index: number, facetValueId: string) {
const formGroup = this.formArray.at(index);
const newValue = (formGroup.value as VariantFormValue).facetValueIds.filter(
id => id !== facetValueId,
(id) => id !== facetValueId,
);
formGroup.patchValue({
facetValueIds: newValue,
Expand All @@ -169,9 +184,11 @@ export class ProductVariantsListComponent implements OnChanges, OnInit, OnDestro
size: 'md',
locals: {
productOption: option,
activeLanguage: this.activeLanguage,
customFields: this.customOptionFields,
},
})
.subscribe(result => {
.subscribe((result) => {
if (result) {
this.updateProductOption.emit(result);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
<ng-template vdrDialogTitle>{{ 'catalog.update-product-option' | translate }}</ng-template>

<vdr-form-field [label]="'catalog.option-name' | translate" for="name">
<input
id="name"
Expand All @@ -13,13 +12,25 @@
<vdr-form-field [label]="'common.code' | translate" for="code">
<input id="code" type="text" #codeInput="ngModel" required [(ngModel)]="code" pattern="[a-z0-9_-]+" />
</vdr-form-field>
<section *ngIf="customFields.length">
<label>{{ 'common.custom-fields' | translate }}</label>
<ng-container *ngFor="let customField of customFields">
<vdr-custom-field-control
*ngIf="customFieldsForm.get(customField.name)"
entityName="ProductOption"
[customFieldsFormGroup]="customFieldsForm"
[customField]="customField"
[readonly]="!('UpdateCatalog' | hasPermission)"
></vdr-custom-field-control>
</ng-container>
</section>

<ng-template vdrDialogButtons>
<button type="button" class="btn" (click)="cancel()">{{ 'common.cancel' | translate }}</button>
<button
type="submit"
(click)="update()"
[disabled]="nameInput.invalid || codeInput.invalid || (nameInput.pristine && codeInput.pristine)"
[disabled]="nameInput.invalid || codeInput.invalid || (nameInput.pristine && codeInput.pristine && customFieldsForm.pristine)"
class="btn btn-primary"
>
{{ 'catalog.update-product-option' | translate }}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
import { ChangeDetectionStrategy, Component, OnInit } from '@angular/core';
import { ProductVariant, UpdateProductOptionInput } from '@vendure/admin-ui/core';
import { FormControl, FormGroup } from '@angular/forms';
import {
CustomFieldConfig,
LanguageCode,
ProductVariant,
UpdateProductOptionInput,
} from '@vendure/admin-ui/core';
import { createUpdatedTranslatable } from '@vendure/admin-ui/core';
import { Dialog } from '@vendure/admin-ui/core';
import { normalizeString } from '@vendure/common/lib/normalize-string';
Expand All @@ -14,22 +20,48 @@ export class UpdateProductOptionDialogComponent implements Dialog<UpdateProductO
resolveWith: (result?: UpdateProductOptionInput) => void;
// Provided by caller
productOption: ProductVariant.Options;
activeLanguage: LanguageCode;
name: string;
code: string;
customFields: CustomFieldConfig[];
codeInputTouched = false;
customFieldsForm: FormGroup;

ngOnInit(): void {
this.name = this.productOption.name;
const currentTranslation = this.productOption.translations.find(
(t) => t.languageCode === this.activeLanguage,
);
this.name = currentTranslation?.name ?? '';
this.code = this.productOption.code;
this.customFieldsForm = new FormGroup({});
if (this.customFields) {
const cfCurrentTranslation =
(currentTranslation && (currentTranslation as any).customFields) || {};

for (const fieldDef of this.customFields) {
const key = fieldDef.name;
const value =
fieldDef.type === 'localeString'
? cfCurrentTranslation[key]
: (this.productOption as any).customFields[key];
this.customFieldsForm.addControl(fieldDef.name, new FormControl(value));
}
}
}

update() {
const result = createUpdatedTranslatable({
translatable: this.productOption,
languageCode: this.productOption.languageCode,
languageCode: this.activeLanguage,
updatedFields: {
code: this.code,
name: this.name,
customFields: this.customFieldsForm.value,
},
customFieldConfig: this.customFields,
defaultTranslation: {
languageCode: this.activeLanguage,
name: '',
},
});
this.resolveWith(result);
Expand All @@ -40,7 +72,7 @@ export class UpdateProductOptionDialogComponent implements Dialog<UpdateProductO
}

updateCode(nameValue: string) {
if (!this.codeInputTouched) {
if (!this.codeInputTouched && !this.productOption.code) {
this.code = normalizeString(nameValue, '-');
}
}
Expand Down
17 changes: 13 additions & 4 deletions packages/admin-ui/src/lib/core/src/common/base-detail.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,9 +82,18 @@ export abstract class BaseDetailComponent<Entity extends { id: string; updatedAt
}

protected setQueryParam(key: string, value: any) {
this.router.navigate(['./', { [key]: value }], {
relativeTo: this.route,
queryParamsHandling: 'merge',
});
this.router.navigate(
[
'./',
{
...this.route.snapshot.params,
[key]: value,
},
],
{
relativeTo: this.route,
queryParamsHandling: 'merge',
},
);
}
}
Loading

0 comments on commit 4678360

Please sign in to comment.