Your IP : 18.191.144.15


Current Path : /var/www/u0635749/data/www/hobbyclick.ru/public/bitrix/js/catalog/product-form/src/
Upload File :
Current File : /var/www/u0635749/data/www/hobbyclick.ru/public/bitrix/js/catalog/product-form/src/product-form.js

import {BitrixVue} from 'ui.vue';
import {VuexBuilder} from 'ui.vue.vuex';
import {Loc, Type, Text, Tag, ajax, Extension} from 'main.core';
import 'ui.notification';
import {ProductList} from './models/product-list';
import {config} from "./config";
import './templates/form';
import './component.css';
import {EventEmitter} from "main.core.events";
import {CurrencyCore} from "currency.currency-core";
import type {FormOption} from "./types/form-option";
import {FormElementPosition} from "./types/form-element-position";
import {DiscountType} from "catalog.product-calculator";
import {FormInputCode} from "./types/form-input-code";
import type {BasketItemScheme} from "./types/basket-item-scheme";
import {FormErrorCode} from "./types/form-error-code";
import {FormMode} from "./types/form-mode";
import {FormCompilationType} from "./types/form-compilation-type";

export class ProductForm
{
	constructor(options: FormOption = {})
	{
		this.options = this.prepareOptions(options);
		this.defaultOptions = Object.assign({}, this.options);

		this.editable = true;
		this.#setMode(FormMode.REGULAR);

		this.wrapper = Tag.render`<div class=""></div>`;

		if (Text.toNumber(options.iblockId) <= 0)
		{
			return;
		}

		ProductForm.initStore()
			.then((result) => this.initTemplate(result))
			.catch((error) => ProductForm.showError(error))
		;
	}

	static initStore(): VuexBuilder
	{
		const builder = new VuexBuilder();

		return builder
			.addModel(ProductList.create())
			.build();
	}

	prepareOptions(options: FormOption = {}): FormOption
	{
		const settingsCollection = Extension.getSettings('catalog.product-form');
		const defaultOptions: FormOption = {
			basket: [],
			measures: [],
			iblockId: null,
			basePriceId: settingsCollection.get('basePriceId'),
			taxList: [],
			singleProductMode: false,
			showResults: true,
			showCompilationModeSwitcher: false,
			enableEmptyProductError: true,
			pricePrecision: 2,
			currency: settingsCollection.get('currency'),
			currencySymbol: settingsCollection.get('currencySymbol'),
			taxIncluded: settingsCollection.get('taxIncluded'),
			showDiscountBlock: settingsCollection.get('showDiscountBlock'),
			showTaxBlock: settingsCollection.get('showTaxBlock'),
			allowedDiscountTypes: [DiscountType.PERCENTAGE, DiscountType.MONETARY],
			visibleBlocks: [
				FormInputCode.PRODUCT_SELECTOR, FormInputCode.PRICE,
				FormInputCode.QUANTITY, FormInputCode.RESULT,
				FormInputCode.DISCOUNT,
			],
			requiredFields: [],
			editableFields: [],
			newItemPosition: FormElementPosition.TOP,
			buttonsPosition: FormElementPosition.TOP,
			urlBuilderContext: 'SHOP',
			hideUnselectedProperties: false,
			compilationFormType: FormCompilationType.REGULAR,
			compilationFormOption: {},
		};

		if (options.visibleBlocks && !Type.isArray(options.visibleBlocks))
		{
			delete(options.visibleBlocks);
		}

		if (options.requiredFields && !Type.isArray(options.requiredFields))
		{
			delete(options.requiredFields);
		}

		options = {...defaultOptions, ...options};
		options.showTaxBlock = 'N';

		if (settingsCollection.get('isEnabledLanding'))
		{
			options.compilationFormOption = {
				type: options.compilationFormType,
				hasStore: settingsCollection.get('hasLandingStore'),
				isLimitedStore: settingsCollection.get('isLimitedLandingStore'),
				disabledSwitcher: settingsCollection.get('isLimitedLandingStore'),
				hiddenInfoMessage: settingsCollection.get('hiddenCompilationInfoMessage'),
			};
		}
		else
		{
			options.showCompilationModeSwitcher = false;
		}

		options.defaultDiscountType = '';
		if (Type.isArray(options.allowedDiscountTypes))
		{
			if (options.allowedDiscountTypes.includes(DiscountType.PERCENTAGE))
			{
				options.defaultDiscountType = DiscountType.PERCENTAGE;
			}
			else if (options.allowedDiscountTypes.includes(DiscountType.MONETARY))
			{
				options.defaultDiscountType = DiscountType.MONETARY;
			}
		}

		return options;
	}

	layout(): HTMLElement
	{
		return this.wrapper;
	}

	initTemplate(result): Promise
	{
		return new Promise((resolve) =>
		{
			const context = this;
			this.store = result.store;

			this.templateEngine = BitrixVue.createApp({
				el: this.wrapper,
				store: this.store,
				data: {
					options: this.options,
					mode: this.mode,
				},
				created()
				{
					this.$app = context;
				},
				mounted()
				{
					resolve();
				},
				template: `<${config.templateName} :options="options" :mode="mode"/>`,
			});

			if (Type.isStringFilled(this.options.currency))
			{
				this.setData({
					currency: this.options.currency
				});
				CurrencyCore.loadCurrencyFormat(this.options.currency);
			}

			if (this.options.basket.length > 0)
			{
				this.setData(
					{basket: this.options.basket,},
					{newItemPosition: FormElementPosition.BOTTOM}
				);

				if (Type.isObject(this.options.totals))
				{
					this.store.commit('productList/setTotal', this.options.totals);
				}
				else
				{
					this.store.dispatch('productList/calculateTotal');
				}
			}
			else
			{
				const newItem = this.store.getters['productList/getBaseProduct']();
				newItem.fields.discountType = this.options.defaultDiscountType;
				this.addProduct(newItem);
			}
		});
	}

	addProduct(item = {}): void
	{
		this.store.dispatch('productList/addItem', {
			item,
			position: this.options.newItemPosition
		})
			.then(() => {
				this.#onBasketChange();
			});
	}

	#onBasketChange(): void
	{
		EventEmitter.emit(this, 'ProductForm:onBasketChange', {
			basket: this.store.getters['productList/getBasket']()
		});
	}

	changeProduct(product): void
	{
		const fields = product.fields;
		const result = this.#checkRequiredFields(fields);
		fields.errors = result?.errors || [];

		this.store.dispatch('productList/changeItem', {
			index: product.index,
			fields
		}).then(() => {
			this.#onBasketChange();
		});
	}

	#checkRequiredFields(fields: BasketItemScheme): {}
	{
		const result = {};
		if (this.options.requiredFields.length === 0)
		{
			return result;
		}

		result.errors = [];
		this.options.requiredFields.forEach((code) => {
			switch (code)
			{
				case FormInputCode.PRICE:
					if (fields.price <= 0)
					{
						result.errors.push({
							code: FormErrorCode.EMPTY_PRICE,
							message: Loc.getMessage('CATALOG_FORM_ERROR_EMPTY_PRICE'),
						});
					}
					break;
				case FormInputCode.QUANTITY:
					if (fields.quantity <= 0)
					{
						result.errors.push({
							code: FormErrorCode.EMPTY_QUANTITY,
							message: Loc.getMessage('CATALOG_FORM_ERROR_EMPTY_QUANTITY'),
						});
					}
					break;
				case FormInputCode.BRAND:
					if (!Type.isArray(fields.brands) || fields.brands.length === 0)
					{
						result.errors.push({
							code: FormErrorCode.EMPTY_BRAND,
							message: Loc.getMessage('CATALOG_FORM_ERROR_EMPTY_BRAND'),
						});
					}
					break;
			}
		});

		return result;
	}

	removeProduct(product): void
	{
		this.store.dispatch('productList/removeItem', {
			index: product.index
		}).then(() => {
			this.#onBasketChange();
		});
	}

	setData(data, option = {}): void
	{
		if (Type.isObject(data.basket))
		{
			const formBasket = this.store.getters['productList/getBasket']();
			data.basket.forEach((fields) => {
				if (!Type.isObject(fields))
				{
					return;
				}
				const itemPosition = option.newItemPosition || this.options.newItemPosition;

				const innerId = fields.selectorId;
				if (Type.isNil(innerId))
				{
					this.store.dispatch('productList/addItem', {
						item: fields,
						position: itemPosition
					});

					return;
				}

				const basketIndex = formBasket.findIndex(item => item.selectorId === innerId);
				if (basketIndex === -1)
				{
					this.store.dispatch('productList/addItem', {
						item: fields,
						position: itemPosition
					});
				}
				else
				{
					this.store.dispatch('productList/changeItem', {basketIndex, fields});
				}
			});
		}

		if (Type.isStringFilled(data.currency))
		{
			this.store.dispatch('productList/setCurrency', data.currency);
		}
		
		if (Type.isObject(data.total))
		{
			this.store.commit('productList/setTotal', {
				sum: data.total.sum,
				taxSum: data.total.taxSum,
				discount: data.total.discount,
				result: data.total.result,
			})
		}

		if (Type.isObject(data.errors))
		{
			this.store.commit('productList/setErrors', data.errors);
		}
	}

	changeFormOption(optionName, value): void
	{
		value = (value === 'Y') ? 'Y' : 'N';

		if (optionName === 'isCompilationMode')
		{
			if (!this.options.showCompilationModeSwitcher)
			{
				return;
			}

			const mode = (value === 'Y') ? FormMode.COMPILATION : FormMode.REGULAR;
			this.#changeCompilationModeSetting(mode);

			return;
		}

		this.options[optionName] = value;
		if (optionName !== 'hiddenCompilationInfoMessage')
		{
			const basket = this.store.getters['productList/getBasket']();
			basket.forEach((item, index) => {
				if (optionName === 'showDiscountBlock')
				{
					item.showDiscountBlock = value;
				}
				else if (optionName === 'showTaxBlock')
				{
					item.showTaxBlock = value;
				}
				else if (optionName === 'taxIncluded')
				{
					item.fields.taxIncluded = value;
				}

				this.store.dispatch('productList/changeItem', {
					index,
					fields: item
				});
			});
		}

		ajax.runAction(
			'catalog.productForm.setConfig',
			{
				data: {
					configName: optionName,
					value: value
				}
			}
		);
	}

	#changeCompilationModeSetting(mode: FormMode)
	{
		this.options.requiredFields = [];
		if (mode === FormMode.COMPILATION)
		{
			const compilationRequiredFields = [
				FormInputCode.PRODUCT_SELECTOR, FormInputCode.PRICE, FormInputCode.BRAND,
			];
			this.options.requiredFields = this.options.visibleBlocks.filter(
				item => compilationRequiredFields.includes(item)
			);
		}

		this.#setMode(mode)

		const basket = this.store.getters['productList/getBasket']();

		basket.forEach((item, index) => this.changeProduct({
				index,
				fields: item.fields
			})
		);
	}

	getTotal(): void
	{
		this.store.dispatch('productList/getTotal');
	}

	setEditable(value): void
	{
		this.editable = value;
		if (!value)
		{
			this.#setMode(FormMode.READ_ONLY);
		}
		else
		{
			this.#setMode(FormMode.REGULAR);
		}
	}

	#setMode(mode: FormMode): void
	{
		this.mode = mode;
		if (mode === FormMode.READ_ONLY)
		{
			this.options.editableFields = [];
		}
		else if (mode === FormMode.COMPILATION)
		{
			this.options.editableFields = [
				FormInputCode.PRODUCT_SELECTOR, FormInputCode.PRICE, FormInputCode.BRAND,
			];

			this.options.visibleBlocks = [
				FormInputCode.PRODUCT_SELECTOR, FormInputCode.PRICE,
			];

			if (this.options.compilationFormType === FormCompilationType.FACEBOOK)
			{
				this.options.visibleBlocks.push(FormInputCode.BRAND);
			}

			this.options.showResults = false;
		}
		else
		{
			mode = FormMode.REGULAR;
			this.options.visibleBlocks = this.defaultOptions.visibleBlocks;
			this.options.showResults = this.defaultOptions.showResults;

			const visibleBlocks = this.options.visibleBlocks.slice(0);
			if (visibleBlocks.includes(FormInputCode.RESULT))
			{
				visibleBlocks.splice(visibleBlocks.indexOf(FormInputCode.RESULT), 1);
			}
			this.options.editableFields = visibleBlocks;
		}

		if (this.templateEngine)
		{
			this.templateEngine.mode = mode;
		}

		EventEmitter.emit(this, 'ProductForm:onModeChange', { mode });
	}

	hasErrors()
	{
		if (!this.store)
		{
			return false;
		}

		const basket = this.store.getters['productList/getBasket']();
		const errorItems = basket.filter(
			item => item.errors.length > 0
		);

		return errorItems.length > 0;
	}

	static showError(error): void
	{
		console.error(error);
	}
}