@snabcentr/client-ui 3.30.0 → 3.31.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,5 +1,5 @@
1
1
  import * as i0 from '@angular/core';
2
- import { inject, Injectable, Directive, ContentChildren, HostListener, NgModule, EventEmitter, signal, ChangeDetectorRef, Input, Output, Component, ChangeDetectionStrategy, Inject, HostBinding, ElementRef, Pipe, Renderer2, input, model, output, SkipSelf, DestroyRef, effect, ContentChild, ViewChild, Optional, computed, forwardRef } from '@angular/core';
2
+ import { inject, Injectable, Directive, ContentChildren, HostListener, NgModule, EventEmitter, signal, ChangeDetectorRef, Input, Output, Component, ChangeDetectionStrategy, Inject, HostBinding, ElementRef, Pipe, Renderer2, input, model, output, SkipSelf, DestroyRef, effect, ContentChild, ViewChild, Optional, computed, viewChild, forwardRef } from '@angular/core';
3
3
  import * as i1 from '@snabcentr/client-core';
4
4
  import { ScContactsService, ScUserService, ScAuthService, SEARCH_TERM, ScUnitsHelper, ScImageHelper, SC_PATH_IMAGE_NOT_FOUND, IS_RUNNING_ON_TERMINAL, ScPhoneService, ScUserMetrikaService, ScUserMetrikaGoalsEnum, ScVCardService, ScVerificationService, ScISuggestionType, SC_MIN_LENGTH_SEARCH_TERM, ScConvertersService, ScOpfList, ScLocationsService, ScWarehouseService, SEARCH_TERM_PROVIDERS, ScCartService, ScUploadedFile, ScMimeTypes, ScCatalogService, SC_URLS, ScPaginationService, SC_NEXT_PAGE_PAGINATION_CLICK, SC_PRODUCT_PAGINATION_OPTIONS, ScIconTypesEnum, ScDocumentInfoTypesEnum, ScFrequentlyAskedQuestionsService, ScFeedbackService } from '@snabcentr/client-core';
5
5
  import * as i6 from 'rxjs';
@@ -11,19 +11,19 @@ import { toSignal, outputFromObservable, takeUntilDestroyed } from '@angular/cor
11
11
  import * as i2$3 from '@angular/forms';
12
12
  import { FormControl, FormGroupDirective, NgControl, FormGroup, Validators, FormArray, FormsModule, ReactiveFormsModule } from '@angular/forms';
13
13
  import * as i1$1 from '@taiga-ui/core';
14
- import { TuiButton, TuiDialog, TuiDialogService, TuiLink, TuiFallbackSrcPipe, TuiInitialsPipe, TuiIcon, TuiAppearance, TuiWithAppearance, TuiIcons, TuiWithIcons, TUI_DATA_LIST_HOST, tuiDropdownOptionsProvider, TuiLabel, TuiError, TuiLoader, TuiDataList, TuiNotification, TuiTextfield, TuiTitle, TuiHint, TuiFormatNumberPipe, TUI_MONTHS, TuiSurface, tuiFadeIn } from '@taiga-ui/core';
14
+ import { TuiButton, TuiDialog, TuiDialogService, TuiLink, TuiFallbackSrcPipe, TuiInitialsPipe, TuiIcon, TuiAppearance, TuiWithAppearance, TuiIcons, TuiWithIcons, TUI_DATA_LIST_HOST, tuiDropdownOptionsProvider, TuiLabel, TuiError, TuiLoader, TuiDataList, TuiNotification, TuiTextfield, TuiTitle, TuiHint, TuiFormatNumberPipe, TuiNumberFormat, TUI_MONTHS, TuiSurface, tuiFadeIn } from '@taiga-ui/core';
15
15
  import { isValidPhoneNumber, getCountries } from 'libphonenumber-js';
16
16
  import * as i4 from '@taiga-ui/legacy/components/primitive-textfield';
17
17
  import * as i5 from '@taiga-ui/legacy';
18
18
  import { AbstractTuiControl, TuiInputModule, TuiTextfieldControllerModule, TuiComboBoxModule, TuiInputPhoneModule, TuiSelectModule, TuiInputPasswordModule, AbstractTuiNullableControl, TuiInputNumberComponent, TuiInputNumberModule, TuiIslandDirective, TuiTextareaModule } from '@taiga-ui/legacy';
19
- import { isNil } from 'lodash-es';
19
+ import { isNil, isObject } from 'lodash-es';
20
20
  import * as i2$2 from '@angular/common';
21
21
  import { CommonModule, NgIf, AsyncPipe, NgFor, NgClass } from '@angular/common';
22
22
  import * as i4$1 from '@taiga-ui/core/components/label';
23
23
  import * as i8 from '@maskito/angular';
24
24
  import { MaskitoDirective } from '@maskito/angular';
25
25
  import * as i2$1 from '@taiga-ui/kit';
26
- import { TuiPreview, TUI_DATE_VALUE_TRANSFORMER, TuiAvatar, TuiAccordionItem, TuiElasticContainer, TuiAccordion, TuiFieldErrorPipe, TuiFilterByInputPipe, TuiStringifyContentPipe, TuiDataListWrapper, TuiButtonLoading, TuiSortCountriesPipe, TuiCarousel, TuiPush, TuiCheckbox, TuiStepper, TuiBadge, TuiPreviewDialogService, TuiHighlight, TuiFiles, TuiLineClamp, TuiTreeService, TuiTreeItemContent, TUI_TREE_START, TUI_TREE_CONTENT, TUI_TREE_LOADING, TUI_TREE_LOADER, TuiTree, tuiItemsHandlersProvider, TuiPagination, TuiChip, tuiFilesAccepted } from '@taiga-ui/kit';
26
+ import { TuiPreview, TUI_DATE_VALUE_TRANSFORMER, TuiAvatar, TuiAccordionItem, TuiElasticContainer, TuiAccordion, TuiFieldErrorPipe, TuiFilterByInputPipe, TuiStringifyContentPipe, TuiDataListWrapper, TuiButtonLoading, TuiSortCountriesPipe, TuiCarousel, TuiPush, TuiCheckbox, TuiStepper, TuiBadge, TuiPreviewDialogService, TuiHighlight, TuiFiles, TuiChip, TuiInputNumber, TuiLineClamp, TuiTreeService, TuiTreeItemContent, TUI_TREE_START, TUI_TREE_CONTENT, TUI_TREE_LOADING, TUI_TREE_LOADER, TuiTree, tuiItemsHandlersProvider, TuiPagination, tuiFilesAccepted } from '@taiga-ui/kit';
27
27
  import * as i2$4 from '@taiga-ui/polymorpheus';
28
28
  import { POLYMORPHEUS_CONTEXT, PolymorpheusComponent, PolymorpheusTemplate, PolymorpheusOutlet, injectContext } from '@taiga-ui/polymorpheus';
29
29
  import * as i2 from 'angularx-qrcode';
@@ -36,8 +36,10 @@ import * as i3$1 from '@ng-web-apis/intersection-observer';
36
36
  import { IntersectionObserverService, WaIntersectionObserver } from '@ng-web-apis/intersection-observer';
37
37
  import * as i2$5 from '@angular/router';
38
38
  import { RouterModule, ActivatedRoute, RouterLink, Router, NavigationStart } from '@angular/router';
39
- import { TuiCurrencyPipe } from '@taiga-ui/addon-commerce';
39
+ import { TuiCurrencyPipe, TuiAmountPipe } from '@taiga-ui/addon-commerce';
40
40
  import { WA_WINDOW } from '@ng-web-apis/common';
41
+ import { TuiAppearance as TuiAppearance$1, TuiWithAppearance as TuiWithAppearance$1 } from '@taiga-ui/core/directives/appearance';
42
+ import { TuiIcons as TuiIcons$1, TuiWithIcons as TuiWithIcons$1 } from '@taiga-ui/core/directives/icons';
41
43
  import { __decorate } from 'tslib';
42
44
  import * as i5$1 from '@taiga-ui/addon-charts';
43
45
  import { TuiLineDaysChart, TuiLineDaysChartHint, TuiAxes } from '@taiga-ui/addon-charts';
@@ -832,6 +834,35 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImpo
832
834
  }]
833
835
  }] });
834
836
 
837
+ /**
838
+ * Директива для обработки события фокуса поля ввода, для последующего выделения содержимого поля ввода.
839
+ */
840
+ class ScSelectOnFocusinDirective {
841
+ /**
842
+ * Слушатель и обработчик события `focusin`.
843
+ *
844
+ * @param target Целевой объект события `focusin`.
845
+ */
846
+ // eslint-disable-next-line class-methods-use-this
847
+ onFocusIn(target) {
848
+ if (target instanceof HTMLInputElement) {
849
+ target.select();
850
+ }
851
+ }
852
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: ScSelectOnFocusinDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
853
+ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "18.2.13", type: ScSelectOnFocusinDirective, isStandalone: true, selector: "tui-input-number, tui-input, tui-input-phone, tui-input-date, tui-input-password, input[tuiInputNumber]", host: { listeners: { "focusin": "onFocusIn($event.target)" } }, ngImport: i0 }); }
854
+ }
855
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: ScSelectOnFocusinDirective, decorators: [{
856
+ type: Directive,
857
+ args: [{
858
+ standalone: true,
859
+ selector: 'tui-input-number, tui-input, tui-input-phone, tui-input-date, tui-input-password, input[tuiInputNumber]',
860
+ host: {
861
+ '(focusin)': 'onFocusIn($event.target)',
862
+ },
863
+ }]
864
+ }] });
865
+
835
866
  /**
836
867
  * Токен для обработчика QR кода менеджера.
837
868
  */
@@ -4252,6 +4283,243 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImpo
4252
4283
  args: [{ standalone: true, selector: 'sc-car-add-products-from-csv-dialog', imports: [TuiLink, TuiLoader, TuiFiles, ReactiveFormsModule, NgIf, TuiButton, TuiButtonLoading], changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"flex flex-col items-center gap-8\">\n <tui-loader\n [overlay]=\"true\"\n [showLoader]=\"isDownloadLoading()\"\n size=\"s\"\n class=\"mt-8\"\n >\n <button\n tuiLink\n [pseudo]=\"true\"\n type=\"button\"\n (click)=\"onDownloadClick.next()\"\n >\n \u041F\u0440\u0438\u043C\u0435\u0440 .csv \u0444\u0430\u0439\u043B\u0430\n </button>\n </tui-loader>\n\n <div class=\"flex w-full flex-col gap-1\">\n <label\n tuiInputFiles\n class=\"w-full\"\n >\n <input\n accept=\"text/csv\"\n tuiInputFiles\n [formControl]=\"control\"\n />\n </label>\n\n <tui-files class=\"tui-space_top-1\">\n <tui-file\n *ngIf=\"control.value as file\"\n [file]=\"file\"\n (remove)=\"removeFile()\"\n />\n </tui-files>\n </div>\n <div class=\"flex gap-2\">\n <button\n tuiButton\n [disabled]=\"!control.value\"\n [loading]=\"isSubmitLoading()\"\n iconStart=\"@tui.sc.send\"\n class=\"self-center\"\n (click)=\"onSubmit$.next()\"\n >\n \u0417\u0430\u0433\u0440\u0443\u0437\u0438\u0442\u044C\n </button>\n <button\n tuiButton\n (click)=\"context.$implicit.complete()\"\n type=\"button\"\n appearance=\"secondary\"\n >\n \u041E\u0442\u043C\u0435\u043D\u0430\n </button>\n </div>\n</div>\n" }]
4253
4284
  }] });
4254
4285
 
4286
+ /* eslint-disable sonarjs/no-nested-template-literals,@typescript-eslint/unbound-method */
4287
+ /**
4288
+ * Компонент добавления / изменения товара в корзине.
4289
+ */
4290
+ class ScAddOrEditingCartItemFormComponent {
4291
+ constructor() {
4292
+ /**
4293
+ * Группа полей добавления / изменения товара в корзине.
4294
+ */
4295
+ this.form = new FormGroup({
4296
+ quantity: new FormControl(null, Validators.required),
4297
+ marker: new FormControl(''),
4298
+ });
4299
+ /**
4300
+ * Данные о товаре.
4301
+ */
4302
+ this.product = input.required();
4303
+ /**
4304
+ * Данные о товаре в корзине.
4305
+ */
4306
+ this.cartItem = input();
4307
+ /**
4308
+ * Признак загрузки данных.
4309
+ */
4310
+ this.isLoading = input(false);
4311
+ /**
4312
+ * Признак возможности продажи товара на метраж.
4313
+ */
4314
+ this.productIsMeasurable = computed(() => this.unitsHelper.productIsMeasurable(this.product()));
4315
+ /**
4316
+ * Кратность количества для товара.
4317
+ */
4318
+ this.productMultiplicity = computed(() => this.unitsHelper.productMultiplicity(this.product()));
4319
+ /**
4320
+ * Минимальный метраж для товара.
4321
+ */
4322
+ this.minLength = computed(() => this.cartItem()?.length ?? this.product().properties?.minLength ?? (this.product().ignoreMinCountCheck ? 0 : this.product().minCount) ?? 1);
4323
+ /**
4324
+ * Максимальный метраж для товара.
4325
+ */
4326
+ this.maxLength = computed(() => this.product().properties?.maxLength);
4327
+ /**
4328
+ * Шаг изменения метража.
4329
+ */
4330
+ this.lengthStep = computed(() => this.product().properties?.lengthStep ?? (this.product().ignoreMinCountCheck ? 0 : this.product().minCount));
4331
+ /**
4332
+ * Подсказка по минимальному и максимальному метражу товара.
4333
+ */
4334
+ this.lengthHint = computed(() => {
4335
+ const min = this.minLength();
4336
+ const max = this.maxLength();
4337
+ // eslint-disable-next-line sonarjs/no-nested-conditional, @typescript-eslint/no-unnecessary-condition
4338
+ return (min ?? max) ? `(${min ? `от ${min}` : ''}${min && max ? ' ' : ''}${max ? `до ${max}` : ''} ${this.product().unit})` : '';
4339
+ });
4340
+ /**
4341
+ * Итоговая стоимость заказа.
4342
+ */
4343
+ this.totalCost = toSignal(combineLatest({
4344
+ // eslint-disable-next-line unicorn/explicit-length-check
4345
+ length: this.form.controls.length ? tuiControlValue(this.form.controls.length) : of(1),
4346
+ quantity: tuiControlValue(this.form.controls.quantity),
4347
+ }).pipe(debounceTime(0), // Исправляем ошибку NG0950.
4348
+ map(({ length, quantity }) => (this.product().costRub ?? 0) * length * quantity), map((sum) => Math.round(sum * 100) / 100)));
4349
+ /**
4350
+ * {@link Output} события добавления товара в корзину.
4351
+ */
4352
+ this.addToCart = output();
4353
+ /**
4354
+ * {@link Output} события редактирования товара в корзине.
4355
+ */
4356
+ this.editCartItem = output();
4357
+ /**
4358
+ * Объект-помощник для работы со значениями единиц измерения товара.
4359
+ */
4360
+ this.unitsHelper = inject(ScUnitsHelper);
4361
+ /**
4362
+ * Сервис для сбора метрик о действиях пользователей.
4363
+ */
4364
+ this.userMetrikaService = inject(ScUserMetrikaService);
4365
+ /**
4366
+ * Сервис конвертации данных.
4367
+ */
4368
+ this.convertersService = inject(ScConvertersService);
4369
+ }
4370
+ /** @inheritDoc */
4371
+ ngOnInit() {
4372
+ if (this.productIsMeasurable()) {
4373
+ this.form.addControl('length', new FormControl(this.minLength(), [Validators.required, Validators.min(0.01)]));
4374
+ }
4375
+ this.form.patchValue({
4376
+ quantity: this.cartItem()?.quantity ?? this.unitsHelper.productMultiplicity(this.product()),
4377
+ });
4378
+ this.form.controls.quantity.addValidators(stepValidator(this.unitsHelper.productStepForValidator(this.product())));
4379
+ this.form.controls.length?.addValidators(stepValidator(this.product().properties?.lengthStep ?? (this.product().ignoreMinCountCheck ? 0 : (this.product().minCount ?? 0))));
4380
+ this.userMetrikaService.emitUserMetrikaEvent({
4381
+ target: ScUserMetrikaGoalsEnum.cartItemAddShow,
4382
+ params: { product_id: this.product().id },
4383
+ });
4384
+ }
4385
+ /**
4386
+ * Обработчик события шага метража.
4387
+ *
4388
+ * @param step Шаг.
4389
+ */
4390
+ onStepLength(step) {
4391
+ // eslint-disable-next-line unicorn/explicit-length-check
4392
+ if (!this.productIsMeasurable() || !this.form.controls.length) {
4393
+ return;
4394
+ }
4395
+ const length = this.form.controls.length.value ?? 0;
4396
+ this.form.controls.length.setValue(Math.max(this.minLength(), length - (length % step) + step));
4397
+ }
4398
+ /**
4399
+ * Обработчик события шага количества.
4400
+ *
4401
+ * @param step Шаг.
4402
+ */
4403
+ onStepQuantity(step) {
4404
+ const quantity = this.form.controls.quantity.value ?? 0;
4405
+ this.form.controls.quantity.setValue(Math.max(this.productMultiplicity(), quantity - (quantity % step) + step));
4406
+ }
4407
+ /**
4408
+ * Обработчик события отправки формы.
4409
+ */
4410
+ onSubmit() {
4411
+ const value = this.convertersService.removeNull(this.form.value);
4412
+ if (!this.cartItem()) {
4413
+ value.productId = this.product().id;
4414
+ }
4415
+ const step = this.lengthStep();
4416
+ if (this.productIsMeasurable() && step) {
4417
+ value.quantity = Math.round(value.quantity * (value.length / step));
4418
+ value.length = step;
4419
+ }
4420
+ tuiMarkControlAsTouchedAndValidate(this.form);
4421
+ if (this.cartItem()) {
4422
+ this.editCartItem.emit(value);
4423
+ }
4424
+ else {
4425
+ this.addToCart.emit(value);
4426
+ }
4427
+ }
4428
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: ScAddOrEditingCartItemFormComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
4429
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "18.2.13", type: ScAddOrEditingCartItemFormComponent, isStandalone: true, selector: "sc-add-or-editing-cart-item-form", inputs: { product: { classPropertyName: "product", publicName: "product", isSignal: true, isRequired: true, transformFunction: null }, cartItem: { classPropertyName: "cartItem", publicName: "cartItem", isSignal: true, isRequired: false, transformFunction: null }, isLoading: { classPropertyName: "isLoading", publicName: "isLoading", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { addToCart: "addToCart", editCartItem: "editCartItem" }, ngImport: i0, template: "<!-- \u0424\u043E\u0440\u043C\u0430 \u0434\u043E\u0431\u0430\u0432\u043B\u0435\u043D\u0438\u044F \u0438\u0437\u043C\u0435\u0440\u044F\u0435\u043C\u043E\u0433\u043E \u0442\u043E\u0432\u0430\u0440\u0430. -->\n<form\n *ngIf=\"product\"\n [formGroup]=\"form\"\n (ngSubmit)=\"onSubmit()\"\n ScNextInputFocus\n class=\"flex flex-col gap-2\"\n>\n <!-- \u0414\u043B\u0438\u043D\u0430 \u0442\u043E\u0432\u0430\u0440\u0430 (\u043C\u0435\u0442\u0440\u0430\u0436) -->\n <label\n *ngIf=\"productIsMeasurable()\"\n tuiLabel\n >\n \u041C\u0435\u0442\u0440\u0430\u0436, {{ product().unit }} {{ lengthHint() }}\n\n <tui-textfield>\n <input\n tuiInputNumber\n formControlName=\"length\"\n [tuiNumberFormat]=\"{ precision: 2 }\"\n [max]=\"maxLength() || null\"\n [min]=\"minLength() || null\"\n (keydown.arrowDown)=\"onStepLength(-(lengthStep() ?? 0.01))\"\n (keydown.arrowUp)=\"onStepLength(lengthStep() ?? 0.01)\"\n autocomplete=\"length\"\n />\n </tui-textfield>\n <tui-error\n formControlName=\"length\"\n [error]=\"[] | tuiFieldError | async\"\n />\n <p\n *ngIf=\"lengthStep()\"\n class=\"tui-form__field-note\"\n >\n \u041C\u0435\u0442\u0440\u0430\u0436 \u0434\u043E\u043B\u0436\u0435\u043D \u0431\u044B\u0442\u044C \u043A\u0440\u0430\u0442\u0435\u043D {{ lengthStep() }}\n </p>\n </label>\n\n <!-- \u041A\u043E\u043B\u0438\u0447\u0435\u0441\u0442\u0432\u043E \u0442\u043E\u0432\u0430\u0440\u0430 -->\n <label tuiLabel>\n \u041A\u043E\u043B\u0438\u0447\u0435\u0441\u0442\u0432\u043E, \u0448\u0442.\n <tui-textfield>\n @let step = lengthStep();\n <tui-chip\n *ngIf=\"productMultiplicity() && step\"\n size=\"s\"\n appearance=\"negative\"\n class=\"font-bold\"\n >\n x {{ step }} {{ product().unit }}\n </tui-chip>\n\n <input\n placeholder=\"\u041A\u043E\u043B\u0438\u0447\u0435\u0441\u0442\u0432\u043E\"\n tuiInputNumber\n [tuiNumberFormat]=\"{ decimalMode: 'not-zero' }\"\n [min]=\"productMultiplicity()\"\n (keydown.arrowDown)=\"onStepQuantity(-productMultiplicity())\"\n (keydown.arrowUp)=\"onStepQuantity(productMultiplicity())\"\n formControlName=\"quantity\"\n autocomplete=\"quantity\"\n />\n </tui-textfield>\n <p class=\"tui-form__field-note\">\u041A\u043E\u043B\u0438\u0447\u0435\u0441\u0442\u0432\u043E \u0434\u043E\u043B\u0436\u043D\u043E \u0431\u044B\u0442\u044C \u043A\u0440\u0430\u0442\u043D\u043E {{ productMultiplicity() }}</p>\n <tui-error\n formControlName=\"quantity\"\n [error]=\"[] | tuiFieldError | async\"\n />\n </label>\n\n <!-- \u041C\u0430\u0440\u043A\u0438\u0440\u043E\u0432\u043A\u0430 -->\n <label tuiLabel>\n \u041C\u0430\u0440\u043A\u0438\u0440\u043E\u0432\u043A\u0430\n <tui-input formControlName=\"marker\">\n \u041C\u0430\u0440\u043A\u0438\u0440\u043E\u0432\u043A\u0430\n <input\n tuiTextfieldLegacy\n autocomplete=\"marker\"\n />\n </tui-input>\n <tui-error\n formControlName=\"marker\"\n [error]=\"[] | tuiFieldError | async\"\n />\n </label>\n\n <!-- \u041A\u043D\u043E\u043F\u043A\u0430 \u0434\u043E\u0431\u0430\u0432\u043B\u0435\u043D\u0438\u044F / \u0440\u0435\u0434\u0430\u043A\u0442\u0438\u0440\u043E\u0432\u0430\u043D\u0438\u044F \u0442\u043E\u0432\u0430\u0440\u0430 \u0432 \u043A\u043E\u0440\u0437\u0438\u043D\u0443 -->\n <div class=\"flex flex-col items-center\">\n @let cost = totalCost();\n\n <div *ngIf=\"cost\">\n \u0418\u0442\u043E\u0433\u043E:<span class=\"text-2xl font-bold\">\n {{ cost | tuiAmount: 'RUB' | async }}\n {{ product().currency }}</span\n >\n </div>\n\n <!-- \u041A\u043D\u043E\u043F\u043A\u0430 \u0434\u043E\u0431\u0430\u0432\u043B\u0435\u043D\u0438\u044F / \u0440\u0435\u0434\u0430\u043A\u0442\u0438\u0440\u043E\u0432\u0430\u043D\u0438\u044F \u0442\u043E\u0432\u0430\u0440\u0430 \u0432 \u043A\u043E\u0440\u0437\u0438\u043D\u0443 -->\n <button\n tuiButton\n iconStart=\"@tui.check\"\n [disabled]=\"form.invalid\"\n [loading]=\"isLoading()\"\n type=\"submit\"\n class=\"mt-2\"\n >\n {{ cartItem() ? '\u0418\u0437\u043C\u0435\u043D\u0438\u0442\u044C' : '\u0414\u043E\u0431\u0430\u0432\u0438\u0442\u044C \u0432 \u043A\u043E\u0440\u0437\u0438\u043D\u0443' }}\n </button>\n </div>\n</form>\n", dependencies: [{ kind: "directive", type: NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i2$3.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i2$3.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i2$3.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2$3.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "ngmodule", type: ScNextInputFocusModule }, { kind: "directive", type: ScNextInputFocusDirective, selector: "form[ScNextInputFocus]" }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i2$3.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i2$3.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "directive", type: TuiLabel, selector: "label[tuiLabel]" }, { kind: "directive", type: ScSelectOnFocusinDirective, selector: "tui-input-number, tui-input, tui-input-phone, tui-input-date, tui-input-password, input[tuiInputNumber]" }, { kind: "directive", type: TuiNumberFormat, selector: "[tuiNumberFormat]", inputs: ["tuiNumberFormat"] }, { kind: "component", type: TuiError, selector: "tui-error", inputs: ["error"] }, { kind: "ngmodule", type: TuiInputModule }, { kind: "component", type: i5.TuiInputComponent, selector: "tui-input" }, { kind: "directive", type: i5.TuiInputDirective, selector: "tui-input" }, { kind: "component", type: i4.TuiTextfieldComponent, selector: "input[tuiTextfieldLegacy], textarea[tuiTextfieldLegacy]" }, { kind: "directive", type: TuiButton, selector: "a[tuiButton],button[tuiButton],a[tuiIconButton],button[tuiIconButton]", inputs: ["size"] }, { kind: "component", type: TuiButtonLoading, selector: "[tuiButton][loading],[tuiIconButton][loading]", inputs: ["size", "loading"] }, { kind: "pipe", type: AsyncPipe, name: "async" }, { kind: "pipe", type: TuiFieldErrorPipe, name: "tuiFieldError" }, { kind: "directive", type: i2$1.TuiInputNumberDirective, selector: "input[tuiInputNumber]", inputs: ["min", "max", "prefix", "postfix"] }, { kind: "component", type: i1$1.TuiTextfieldComponent, selector: "tui-textfield", inputs: ["content", "filler"] }, { kind: "pipe", type: TuiAmountPipe, name: "tuiAmount" }, { kind: "directive", type: TuiChip, selector: "tui-chip,[tuiChip]", inputs: ["size"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
4430
+ }
4431
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: ScAddOrEditingCartItemFormComponent, decorators: [{
4432
+ type: Component,
4433
+ args: [{ standalone: true, selector: 'sc-add-or-editing-cart-item-form', imports: [
4434
+ NgIf,
4435
+ FormsModule,
4436
+ ScNextInputFocusModule,
4437
+ ReactiveFormsModule,
4438
+ TuiLabel,
4439
+ ScSelectOnFocusinDirective,
4440
+ TuiNumberFormat,
4441
+ TuiError,
4442
+ TuiInputModule,
4443
+ TuiAppearance$1,
4444
+ TuiWithAppearance$1,
4445
+ TuiIcons$1,
4446
+ TuiWithIcons$1,
4447
+ TuiButton,
4448
+ TuiButtonLoading,
4449
+ AsyncPipe,
4450
+ TuiFieldErrorPipe,
4451
+ TuiInputNumber,
4452
+ TuiNumberFormat,
4453
+ TuiTextfield,
4454
+ TuiAmountPipe,
4455
+ TuiChip,
4456
+ ], changeDetection: ChangeDetectionStrategy.OnPush, template: "<!-- \u0424\u043E\u0440\u043C\u0430 \u0434\u043E\u0431\u0430\u0432\u043B\u0435\u043D\u0438\u044F \u0438\u0437\u043C\u0435\u0440\u044F\u0435\u043C\u043E\u0433\u043E \u0442\u043E\u0432\u0430\u0440\u0430. -->\n<form\n *ngIf=\"product\"\n [formGroup]=\"form\"\n (ngSubmit)=\"onSubmit()\"\n ScNextInputFocus\n class=\"flex flex-col gap-2\"\n>\n <!-- \u0414\u043B\u0438\u043D\u0430 \u0442\u043E\u0432\u0430\u0440\u0430 (\u043C\u0435\u0442\u0440\u0430\u0436) -->\n <label\n *ngIf=\"productIsMeasurable()\"\n tuiLabel\n >\n \u041C\u0435\u0442\u0440\u0430\u0436, {{ product().unit }} {{ lengthHint() }}\n\n <tui-textfield>\n <input\n tuiInputNumber\n formControlName=\"length\"\n [tuiNumberFormat]=\"{ precision: 2 }\"\n [max]=\"maxLength() || null\"\n [min]=\"minLength() || null\"\n (keydown.arrowDown)=\"onStepLength(-(lengthStep() ?? 0.01))\"\n (keydown.arrowUp)=\"onStepLength(lengthStep() ?? 0.01)\"\n autocomplete=\"length\"\n />\n </tui-textfield>\n <tui-error\n formControlName=\"length\"\n [error]=\"[] | tuiFieldError | async\"\n />\n <p\n *ngIf=\"lengthStep()\"\n class=\"tui-form__field-note\"\n >\n \u041C\u0435\u0442\u0440\u0430\u0436 \u0434\u043E\u043B\u0436\u0435\u043D \u0431\u044B\u0442\u044C \u043A\u0440\u0430\u0442\u0435\u043D {{ lengthStep() }}\n </p>\n </label>\n\n <!-- \u041A\u043E\u043B\u0438\u0447\u0435\u0441\u0442\u0432\u043E \u0442\u043E\u0432\u0430\u0440\u0430 -->\n <label tuiLabel>\n \u041A\u043E\u043B\u0438\u0447\u0435\u0441\u0442\u0432\u043E, \u0448\u0442.\n <tui-textfield>\n @let step = lengthStep();\n <tui-chip\n *ngIf=\"productMultiplicity() && step\"\n size=\"s\"\n appearance=\"negative\"\n class=\"font-bold\"\n >\n x {{ step }} {{ product().unit }}\n </tui-chip>\n\n <input\n placeholder=\"\u041A\u043E\u043B\u0438\u0447\u0435\u0441\u0442\u0432\u043E\"\n tuiInputNumber\n [tuiNumberFormat]=\"{ decimalMode: 'not-zero' }\"\n [min]=\"productMultiplicity()\"\n (keydown.arrowDown)=\"onStepQuantity(-productMultiplicity())\"\n (keydown.arrowUp)=\"onStepQuantity(productMultiplicity())\"\n formControlName=\"quantity\"\n autocomplete=\"quantity\"\n />\n </tui-textfield>\n <p class=\"tui-form__field-note\">\u041A\u043E\u043B\u0438\u0447\u0435\u0441\u0442\u0432\u043E \u0434\u043E\u043B\u0436\u043D\u043E \u0431\u044B\u0442\u044C \u043A\u0440\u0430\u0442\u043D\u043E {{ productMultiplicity() }}</p>\n <tui-error\n formControlName=\"quantity\"\n [error]=\"[] | tuiFieldError | async\"\n />\n </label>\n\n <!-- \u041C\u0430\u0440\u043A\u0438\u0440\u043E\u0432\u043A\u0430 -->\n <label tuiLabel>\n \u041C\u0430\u0440\u043A\u0438\u0440\u043E\u0432\u043A\u0430\n <tui-input formControlName=\"marker\">\n \u041C\u0430\u0440\u043A\u0438\u0440\u043E\u0432\u043A\u0430\n <input\n tuiTextfieldLegacy\n autocomplete=\"marker\"\n />\n </tui-input>\n <tui-error\n formControlName=\"marker\"\n [error]=\"[] | tuiFieldError | async\"\n />\n </label>\n\n <!-- \u041A\u043D\u043E\u043F\u043A\u0430 \u0434\u043E\u0431\u0430\u0432\u043B\u0435\u043D\u0438\u044F / \u0440\u0435\u0434\u0430\u043A\u0442\u0438\u0440\u043E\u0432\u0430\u043D\u0438\u044F \u0442\u043E\u0432\u0430\u0440\u0430 \u0432 \u043A\u043E\u0440\u0437\u0438\u043D\u0443 -->\n <div class=\"flex flex-col items-center\">\n @let cost = totalCost();\n\n <div *ngIf=\"cost\">\n \u0418\u0442\u043E\u0433\u043E:<span class=\"text-2xl font-bold\">\n {{ cost | tuiAmount: 'RUB' | async }}\n {{ product().currency }}</span\n >\n </div>\n\n <!-- \u041A\u043D\u043E\u043F\u043A\u0430 \u0434\u043E\u0431\u0430\u0432\u043B\u0435\u043D\u0438\u044F / \u0440\u0435\u0434\u0430\u043A\u0442\u0438\u0440\u043E\u0432\u0430\u043D\u0438\u044F \u0442\u043E\u0432\u0430\u0440\u0430 \u0432 \u043A\u043E\u0440\u0437\u0438\u043D\u0443 -->\n <button\n tuiButton\n iconStart=\"@tui.check\"\n [disabled]=\"form.invalid\"\n [loading]=\"isLoading()\"\n type=\"submit\"\n class=\"mt-2\"\n >\n {{ cartItem() ? '\u0418\u0437\u043C\u0435\u043D\u0438\u0442\u044C' : '\u0414\u043E\u0431\u0430\u0432\u0438\u0442\u044C \u0432 \u043A\u043E\u0440\u0437\u0438\u043D\u0443' }}\n </button>\n </div>\n</form>\n" }]
4457
+ }] });
4458
+
4459
+ /* eslint-disable sonarjs/no-nested-template-literals,@typescript-eslint/unbound-method,@typescript-eslint/no-unused-vars */
4460
+ /**
4461
+ * Компонент добавления / изменения товара в корзине.
4462
+ */
4463
+ class ScAddOrEditingCartItemDialogComponent {
4464
+ constructor() {
4465
+ /**
4466
+ * Компонент формы добавления / изменения товара в корзине.
4467
+ */
4468
+ this.formComponent = viewChild.required(ScAddOrEditingCartItemFormComponent);
4469
+ /**
4470
+ * {@link Subject} события отправки формы.
4471
+ */
4472
+ this.onSubmit = new Subject();
4473
+ /**
4474
+ * {@link Observable} запроса добавления / изменения товара в корзине.
4475
+ */
4476
+ this.submit$ = this.onSubmit.pipe(switchMap((value) => (this.cartItem && !('productId' in value) ? this.cartService.patchCartItem$(this.cartItem.id, value) : this.cartService.addToCart$(value)).pipe(tap(() => {
4477
+ this.context.$implicit.complete();
4478
+ }), catchError((error) => {
4479
+ if (error instanceof HttpErrorResponse) {
4480
+ const { errors, message } = error.error;
4481
+ if (errors && isObject(errors)) {
4482
+ Object.entries(errors).forEach(([k, v]) => {
4483
+ this.formComponent().form.get(k)?.setErrors({ serverResponse: v });
4484
+ });
4485
+ }
4486
+ if (message) {
4487
+ this.formComponent().form.setErrors({ serverResponse: [message] });
4488
+ }
4489
+ this.formComponent().form.updateValueAndValidity();
4490
+ this.formComponent().form.markAsDirty();
4491
+ }
4492
+ return of();
4493
+ }), startWith(null))), startWith(), share());
4494
+ /**
4495
+ * {@link Observable} изменения состояния загрузки данных.
4496
+ */
4497
+ this.loading = toSignal(this.submit$.pipe(map(tuiIsFalsy)), { initialValue: false });
4498
+ /**
4499
+ * Контекст диалогового окна.
4500
+ */
4501
+ this.context = injectContext();
4502
+ /**
4503
+ * Данные о товаре.
4504
+ */
4505
+ this.product = this.context.data.product;
4506
+ /**
4507
+ * Данные о товаре в корзине.
4508
+ */
4509
+ this.cartItem = this.context.data.cartItem;
4510
+ /**
4511
+ * Сервис для работы с корзиной.
4512
+ */
4513
+ this.cartService = inject(ScCartService);
4514
+ }
4515
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: ScAddOrEditingCartItemDialogComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
4516
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.13", type: ScAddOrEditingCartItemDialogComponent, isStandalone: true, selector: "sc-add-or-editing-cart-item-dialog", viewQueries: [{ propertyName: "formComponent", first: true, predicate: ScAddOrEditingCartItemFormComponent, descendants: true, isSignal: true }], ngImport: i0, template: "@if (product) {\n <sc-add-or-editing-cart-item-form\n [product]=\"product\"\n [cartItem]=\"cartItem\"\n (addToCart)=\"onSubmit.next($event)\"\n (editCartItem)=\"onSubmit.next($event)\"\n [isLoading]=\"loading()\"\n ></sc-add-or-editing-cart-item-form>\n}\n", dependencies: [{ kind: "component", type: ScAddOrEditingCartItemFormComponent, selector: "sc-add-or-editing-cart-item-form", inputs: ["product", "cartItem", "isLoading"], outputs: ["addToCart", "editCartItem"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
4517
+ }
4518
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: ScAddOrEditingCartItemDialogComponent, decorators: [{
4519
+ type: Component,
4520
+ args: [{ standalone: true, selector: 'sc-add-or-editing-cart-item-dialog', imports: [ScAddOrEditingCartItemFormComponent], changeDetection: ChangeDetectionStrategy.OnPush, template: "@if (product) {\n <sc-add-or-editing-cart-item-form\n [product]=\"product\"\n [cartItem]=\"cartItem\"\n (addToCart)=\"onSubmit.next($event)\"\n (editCartItem)=\"onSubmit.next($event)\"\n [isLoading]=\"loading()\"\n ></sc-add-or-editing-cart-item-form>\n}\n" }]
4521
+ }] });
4522
+
4255
4523
  /**
4256
4524
  * Компонент скачивания каталога.
4257
4525
  */
@@ -7460,5 +7728,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImpo
7460
7728
  * Generated bundle index. Do not edit.
7461
7729
  */
7462
7730
 
7463
- export { AbstractScPriceCard, AuthMethod, CURRENT_COUNTRY_ID, FilesAndDocumentsComponent, FilesAndDocumentsModule, FinishDateTimeTransformerDirective, IS_DEFAULT_COUNTRY, MAX_FILES_IN_FORM_INPUT, SC_ALLOW_SELECT_TERMINATED, SC_DATE_FORMATTER, SC_ERROR_CHANGE_HANDLER, SC_HELP_NOTIFICATION_CLOSE, SC_HELP_NOTIFICATION_LIMIT, SC_MANAGER_QR_HANDLER, SC_PAGE_SIZE_OPTIONS$1 as SC_PAGE_SIZE_OPTIONS, SC_SHOW_HELP_NOTIFICATION_IN_PHONE_INPUT, SC_USER_CITY_INFO, SC_USER_INFO, SC_USER_PROVIDERS, SC_VERIFICATION_CODE_TIMEOUT, ScAccordionComponent, ScAccordionContentDirective, ScAccordionModule, ScAddContactDialogComponent, ScAddContragentBankAccountsDialogComponent, ScAddContragentDialogComponent, ScAddDeliveryAddressDialogComponent, ScAddressesSelectionFieldComponent, ScAuthModule, ScBannerComponent, ScBannerModule, ScBrandsListComponent, ScBrandsListModule, ScCarAddProductsFromCsvDialogComponent, ScCartItemComponent, ScCatalogModule, ScCategoryCardComponent, ScContactsAccordionComponent, ScContactsModule, ScContragentsAccordionComponent, ScContragentsAccordionItemComponent, ScContragentsModule, ScDeliveryAddressAccordionComponent, ScDeliveryAddressAccordionItemComponent, ScDeliveryAddressModule, ScDownloadPriceListComponent, ScEmailLinkDirective, ScErrorBlockStatusComponent, ScErrorHandlerComponent, ScFavoriteButtonComponent, ScFeedbackFormComponent, ScFormFieldsModule, ScFormatDatePipe, ScFrequentlyAskedQuestionsComponent, ScFrequentlyAskedQuestionsGroupSelectorComponent, ScFrequentlyAskedQuestionsWithGroupsComponent, ScGratitudeComponent, ScHelpNotificationService, ScInputQuantityComponent, ScLinks, ScManagerCardComponent, ScManagerCardPushComponent, ScNewContactFormComponent, ScNewContragentBankAccountsFormComponent, ScNewContragentFormComponent, ScNewsCardComponent, ScNewsCardSkeletonComponent, ScNewsModule, ScNextInputFocusDirective, ScNextInputFocusModule, ScNotifyWhenInStockDialogComponent, ScOrderItemMobileComponent, ScOrderModule, ScPaymentStatusComponent, ScPhoneFormatPipe, ScPreviewSampleComponent, ScPreviewSampleModule, ScPreviewSamplesMosquitoComponent, ScPriceCardComponent, ScPriceCardInlineComponent, ScPriceHistoryComponent, ScPriceListPaginationComponent, ScPriceWarehouseStockComponent, ScProductInAllWarehousesPipe, ScProfileAccordionsContentComponent, ScProfileModule, ScQRCodeDialogComponent, ScQRCodeModule, ScResetUserPasswordComponent, ScResourcePreviewComponent, ScShareButtonComponent, ScShareButtonModule, ScSignInFormByEmailComponent, ScSignInFormByPhoneComponent, ScSignInFormComponent, ScSignUpFormComponent, ScSimpleSignUpFormComponent, ScSuggestionFieldComponent, ScTelLinkDirective, ScTerminalLinkDirective, ScUpdateUserInfoDialogComponent, ScUserManagersComponent, ScUserModule, ScUserPhoneApproveDialogComponent, ScVerificationModule, ScVerificationPhoneCheckFormComponent, TreeDirective, TreeIconService, TreeLoaderService, TreeTopDirective, phoneValidator, scAtLeastOneRequiredValidator, scBicValidator, scClientUiIconsName, scCorrespondentAccountValidator, scPasswordConfirmMatchingValidator, stepValidator, tuiDateValueTransformerDefaultProvider };
7731
+ export { AbstractScPriceCard, AuthMethod, CURRENT_COUNTRY_ID, FilesAndDocumentsComponent, FilesAndDocumentsModule, FinishDateTimeTransformerDirective, IS_DEFAULT_COUNTRY, MAX_FILES_IN_FORM_INPUT, SC_ALLOW_SELECT_TERMINATED, SC_DATE_FORMATTER, SC_ERROR_CHANGE_HANDLER, SC_HELP_NOTIFICATION_CLOSE, SC_HELP_NOTIFICATION_LIMIT, SC_MANAGER_QR_HANDLER, SC_PAGE_SIZE_OPTIONS$1 as SC_PAGE_SIZE_OPTIONS, SC_SHOW_HELP_NOTIFICATION_IN_PHONE_INPUT, SC_USER_CITY_INFO, SC_USER_INFO, SC_USER_PROVIDERS, SC_VERIFICATION_CODE_TIMEOUT, ScAccordionComponent, ScAccordionContentDirective, ScAccordionModule, ScAddContactDialogComponent, ScAddContragentBankAccountsDialogComponent, ScAddContragentDialogComponent, ScAddDeliveryAddressDialogComponent, ScAddOrEditingCartItemDialogComponent, ScAddOrEditingCartItemFormComponent, ScAddressesSelectionFieldComponent, ScAuthModule, ScBannerComponent, ScBannerModule, ScBrandsListComponent, ScBrandsListModule, ScCarAddProductsFromCsvDialogComponent, ScCartItemComponent, ScCatalogModule, ScCategoryCardComponent, ScContactsAccordionComponent, ScContactsModule, ScContragentsAccordionComponent, ScContragentsAccordionItemComponent, ScContragentsModule, ScDeliveryAddressAccordionComponent, ScDeliveryAddressAccordionItemComponent, ScDeliveryAddressModule, ScDownloadPriceListComponent, ScEmailLinkDirective, ScErrorBlockStatusComponent, ScErrorHandlerComponent, ScFavoriteButtonComponent, ScFeedbackFormComponent, ScFormFieldsModule, ScFormatDatePipe, ScFrequentlyAskedQuestionsComponent, ScFrequentlyAskedQuestionsGroupSelectorComponent, ScFrequentlyAskedQuestionsWithGroupsComponent, ScGratitudeComponent, ScHelpNotificationService, ScInputQuantityComponent, ScLinks, ScManagerCardComponent, ScManagerCardPushComponent, ScNewContactFormComponent, ScNewContragentBankAccountsFormComponent, ScNewContragentFormComponent, ScNewsCardComponent, ScNewsCardSkeletonComponent, ScNewsModule, ScNextInputFocusDirective, ScNextInputFocusModule, ScNotifyWhenInStockDialogComponent, ScOrderItemMobileComponent, ScOrderModule, ScPaymentStatusComponent, ScPhoneFormatPipe, ScPreviewSampleComponent, ScPreviewSampleModule, ScPreviewSamplesMosquitoComponent, ScPriceCardComponent, ScPriceCardInlineComponent, ScPriceHistoryComponent, ScPriceListPaginationComponent, ScPriceWarehouseStockComponent, ScProductInAllWarehousesPipe, ScProfileAccordionsContentComponent, ScProfileModule, ScQRCodeDialogComponent, ScQRCodeModule, ScResetUserPasswordComponent, ScResourcePreviewComponent, ScSelectOnFocusinDirective, ScShareButtonComponent, ScShareButtonModule, ScSignInFormByEmailComponent, ScSignInFormByPhoneComponent, ScSignInFormComponent, ScSignUpFormComponent, ScSimpleSignUpFormComponent, ScSuggestionFieldComponent, ScTelLinkDirective, ScTerminalLinkDirective, ScUpdateUserInfoDialogComponent, ScUserManagersComponent, ScUserModule, ScUserPhoneApproveDialogComponent, ScVerificationModule, ScVerificationPhoneCheckFormComponent, TreeDirective, TreeIconService, TreeLoaderService, TreeTopDirective, phoneValidator, scAtLeastOneRequiredValidator, scBicValidator, scClientUiIconsName, scCorrespondentAccountValidator, scPasswordConfirmMatchingValidator, stepValidator, tuiDateValueTransformerDefaultProvider };
7464
7732
  //# sourceMappingURL=snabcentr-client-ui.mjs.map