@snabcentr/client-ui 4.16.9 → 4.16.11-profstroy
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.
- package/alerts/confirm-alert/sc-confirm-alert.component.d.ts +22 -0
- package/alerts/index.d.ts +1 -0
- package/cart/add-or-editing-cart-item-dialog/sc-add-or-editing-cart-item-dialog.component.d.ts +1 -1
- package/cart/sc-cart-add-products-from-csv-dialog/sc-cart-add-products-from-csv-dialog.component.d.ts +3 -3
- package/catalog/input-quantity/sc-input-quantity.component.d.ts +17 -1
- package/esm2022/alerts/confirm-alert/sc-confirm-alert.component.mjs +29 -0
- package/esm2022/alerts/index.mjs +2 -0
- package/esm2022/cart/add-or-editing-cart-item-dialog/sc-add-or-editing-cart-item-dialog.component.mjs +4 -3
- package/esm2022/cart/sc-cart-add-products-from-csv-dialog/sc-cart-add-products-from-csv-dialog.component.mjs +5 -16
- package/esm2022/catalog/input-quantity/sc-input-quantity.component.mjs +24 -4
- package/esm2022/helpers/sc-api-error-helper.mjs +25 -0
- package/esm2022/helpers/sc-file-helper.mjs +29 -0
- package/esm2022/order/draft/helpers/sc-draft-list-helper.mjs +83 -0
- package/esm2022/order/draft/models/sc-i-draft-list-item.mjs +2 -0
- package/esm2022/order/draft/profstroy/actualization-panel/sc-profstroy-actualization-panel.component.mjs +73 -0
- package/esm2022/order/draft/profstroy/badge/sc-profstroy-badge.component.mjs +21 -0
- package/esm2022/order/draft/profstroy/badges/sc-profstroy-draft-badges.component.mjs +31 -0
- package/esm2022/order/draft/profstroy/create-dialog/sc-profstroy-draft-create-dialog.component.mjs +139 -0
- package/esm2022/order/draft/profstroy/enums/sc-profstroy-draft-state.mjs +32 -0
- package/esm2022/order/draft/profstroy/helpers/sc-profstroy-draft-helper.mjs +94 -0
- package/esm2022/order/draft/profstroy/index.mjs +8 -0
- package/esm2022/order/draft/profstroy/models/sc-i-profstroy-draft-state-view.mjs +2 -0
- package/esm2022/order/draft/sc-draft.component.mjs +148 -14
- package/esm2022/order/draft/services/sc-draft-expiration-clock.service.mjs +42 -0
- package/esm2022/order/draft/services/sc-draft-list-updates.service.mjs +288 -0
- package/esm2022/order/index.mjs +6 -1
- package/esm2022/order/new-order-items/sc-new-order-items.component.mjs +11 -3
- package/esm2022/order/order-form/sc-order-form.component.mjs +4 -2
- package/esm2022/order/order-item/sc-order-item.component.mjs +37 -5
- package/esm2022/order/order-items-list/sc-order-items-list.component.mjs +254 -47
- package/esm2022/order/order-items-list-by-stock/sc-order-items-list-by-stock.component.mjs +1 -1
- package/esm2022/order/order-items-list-with-actions/sc-order-items-list-with-actions.component.mjs +15 -3
- package/esm2022/pipes/sc-format-date.mjs +1 -1
- package/esm2022/providers/sc-date-formatter.mjs +1 -1
- package/esm2022/public-api.mjs +2 -1
- package/fesm2022/snabcentr-client-ui.mjs +1318 -93
- package/fesm2022/snabcentr-client-ui.mjs.map +1 -1
- package/helpers/sc-api-error-helper.d.ts +18 -0
- package/helpers/sc-file-helper.d.ts +13 -0
- package/order/draft/helpers/sc-draft-list-helper.d.ts +51 -0
- package/order/draft/models/sc-i-draft-list-item.d.ts +14 -0
- package/order/draft/profstroy/actualization-panel/sc-profstroy-actualization-panel.component.d.ts +58 -0
- package/order/draft/profstroy/badge/sc-profstroy-badge.component.d.ts +12 -0
- package/order/draft/profstroy/badges/sc-profstroy-draft-badges.component.d.ts +21 -0
- package/order/draft/profstroy/create-dialog/sc-profstroy-draft-create-dialog.component.d.ts +65 -0
- package/order/draft/profstroy/enums/sc-profstroy-draft-state.d.ts +29 -0
- package/order/draft/profstroy/helpers/sc-profstroy-draft-helper.d.ts +33 -0
- package/order/draft/profstroy/index.d.ts +7 -0
- package/order/draft/profstroy/models/sc-i-profstroy-draft-state-view.d.ts +17 -0
- package/order/draft/sc-draft.component.d.ts +73 -1
- package/order/draft/services/sc-draft-expiration-clock.service.d.ts +29 -0
- package/order/draft/services/sc-draft-list-updates.service.d.ts +158 -0
- package/order/index.d.ts +5 -0
- package/order/new-order-items/sc-new-order-items.component.d.ts +9 -1
- package/order/order-form/sc-order-form.component.d.ts +1 -0
- package/order/order-item/sc-order-item.component.d.ts +25 -1
- package/order/order-items-list/sc-order-items-list.component.d.ts +132 -9
- package/order/order-items-list-with-actions/sc-order-items-list-with-actions.component.d.ts +13 -1
- package/package.json +2 -2
- package/pipes/sc-format-date.d.ts +1 -1
- package/providers/sc-date-formatter.d.ts +1 -1
- package/public-api.d.ts +1 -0
- package/release_notes.tmp +1 -1
- package/styles/tailwind/tailwind.scss +41 -0
|
@@ -1,20 +1,20 @@
|
|
|
1
1
|
import * as i0 from '@angular/core';
|
|
2
2
|
import { InjectionToken, inject, Injectable, NgZone, signal, EventEmitter, ChangeDetectorRef, Directive, Input, Output, untracked, Pipe, ElementRef, HostListener, Renderer2, ContentChildren, NgModule, input, computed, Component, ChangeDetectionStrategy, Inject, HostBinding, model, output, SkipSelf, DestroyRef, effect, ContentChild, ViewChild, viewChild, Optional, contentChild, TemplateRef, forwardRef, ViewEncapsulation, Injector, viewChildren, afterNextRender } from '@angular/core';
|
|
3
3
|
import * as i1 from '@snabcentr/client-core';
|
|
4
|
-
import { ScContactsService, ScUserService, ScLocationsService, ScAuthService, SEARCH_TERM, ScUnitsHelper, ScImageHelper, SC_PATH_IMAGE_NOT_FOUND, ScImage, ScSuggestionAddressQuality, ScPhoneService, ScUserMetrikaService, ScUserMetrikaGoalsEnum, IS_RUNNING_ON_TERMINAL, ScVCardService, ScVerificationService, ScISuggestionType, SC_MIN_LENGTH_SEARCH_TERM, ScConvertersService, ScNumberHelper, ScOpfList, ScReferencesService, ScContragentService, ScBannerService, ScMediaImageTransformerPipe,
|
|
4
|
+
import { ScContactsService, ScUserService, ScLocationsService, ScAuthService, SEARCH_TERM, ScUnitsHelper, ScImageHelper, SC_PATH_IMAGE_NOT_FOUND, ScImage, ScSuggestionAddressQuality, ScPhoneService, ScUserMetrikaService, ScUserMetrikaGoalsEnum, IS_RUNNING_ON_TERMINAL, ScVCardService, ScVerificationService, ScISuggestionType, SC_MIN_LENGTH_SEARCH_TERM, ScConvertersService, ScNumberHelper, ScOpfList, ScReferencesService, ScContragentService, ScBannerService, ScMediaImageTransformerPipe, ScUploadedFile, ScCartService, loadingPlaceholder, SC_ORDER_LOADER, ScMimeTypes, ScCatalogService, SC_URLS, ScWarehouseService, SEARCH_TERM_PROVIDERS, ScPaginationService, SC_NEXT_PAGE_PAGINATION_CLICK, SC_PRODUCT_PAGINATION_OPTIONS, ScCatalogFilterService, ScIdOrSlugPipe, SC_CATEGORY_INFO, ScConfiguratorService, IS_SERVER, RESPONSE, ScIconTypesEnum, ScDocumentInfoTypesEnum, ScOrderItemsGroup, ScOrderItem, ScDeliveryAddressService, ScOrdersService, ScUIService, SC_USER_INFO as SC_USER_INFO$1, ScPaymentType, ScDeliveryType, TERMINAL_PROVIDERS, ScOrderDraft, SC_UPDATE_INTERVAL, ScOrderDraftsService, ScOrderDraftActualizationStatus, ScRouteKeys, ScSeoService, scOrderIsLoaded, SC_PAGE_QUERY_PARAM, ScFrequentlyAskedQuestionsService, SchemaOrgFactory, scGetPageQueryParamChanges, ScNewsService, SC_COMPANY_INFO, ScFeedbackService } from '@snabcentr/client-core';
|
|
5
5
|
import * as i5$1 from 'rxjs';
|
|
6
|
-
import { EMPTY, BehaviorSubject, switchMap, of, shareReplay, map, Subject, filter, tap, catchError, finalize, startWith, share, timer, scan, takeWhile, endWith, distinctUntilChanged, debounceTime, throwError, combineLatest, pairwise, Observable, Subscription, merge, skip, from, concatMap, noop, first, retry, fromEvent, animationFrames, take, takeUntil } from 'rxjs';
|
|
6
|
+
import { EMPTY, BehaviorSubject, switchMap, of, shareReplay, map, Subject, filter, tap, catchError, finalize, startWith, share, timer, scan, takeWhile, endWith, distinctUntilChanged, debounceTime, throwError, combineLatest, pairwise, Observable, Subscription, merge, skip, from, concatMap, noop, first, retry, defaultIfEmpty, exhaustMap, fromEvent, animationFrames, take, takeUntil } from 'rxjs';
|
|
7
7
|
import { WA_WINDOW } from '@ng-web-apis/common';
|
|
8
8
|
import { takeUntilDestroyed, toSignal, outputFromObservable, toObservable } from '@angular/core/rxjs-interop';
|
|
9
9
|
import * as i4$1 from '@taiga-ui/cdk';
|
|
10
10
|
import { TuiDay, TuiTime, TuiValueTransformer, TUI_IS_MOBILE, TuiValidationError, tuiControlValue, tuiIsPresent, tuiMarkControlAsTouchedAndValidate, tuiIsFalsy, TuiAutoFocus, TuiRepeatTimes, TuiSwipeService, TUI_SWIPE_OPTIONS, TuiHovered, TuiDayRange, TuiMonth, tuiPure, tuiProvide, TUI_WINDOW_SIZE, tuiClamp, TUI_TRUE_HANDLER } from '@taiga-ui/cdk';
|
|
11
11
|
import * as i2$1 from '@taiga-ui/kit';
|
|
12
|
-
import { TUI_DATE_VALUE_TRANSFORMER, TuiPreview, TuiAvatar, TuiAccordionItem, TuiElasticContainer, TuiAccordion, TuiFieldErrorPipe, TuiFilterByInputPipe, TuiStringifyContentPipe, TuiDataListWrapper, TuiButtonLoading, TuiSortCountriesPipe, TuiStepper, TuiCarousel, TuiPush, TuiCheckbox, TuiFiles, TuiChip, TuiInputNumber, TuiBadge, TuiTooltip, TuiHighlight, TuiLineClamp, TuiFade, TuiRange, TuiBlock, tuiInputNumberOptionsProvider, TuiDataListWrapperComponent, TuiChevron, TuiSelect, TuiInputSlider, TuiTreeService, TuiTreeItemContent, TUI_TREE_START, TUI_TREE_CONTENT, TUI_TREE_LOADING, TUI_TREE_LOADER, TuiTree, TuiPreviewDialogService, tuiFilesAccepted, TuiInputFiles, TuiInputFilesDirective, TuiFilesComponent, TuiFile, TuiFileRejectedPipe,
|
|
13
|
-
import { HttpClient, HttpErrorResponse } from '@angular/common/http';
|
|
12
|
+
import { TUI_DATE_VALUE_TRANSFORMER, TuiPreview, TuiAvatar, TuiAccordionItem, TuiElasticContainer, TuiAccordion, TuiFieldErrorPipe, TuiFilterByInputPipe, TuiStringifyContentPipe, TuiDataListWrapper, TuiButtonLoading, TuiSortCountriesPipe, TuiStepper, TuiCarousel, TuiPush, TuiCheckbox, TUI_CONFIRM_WORDS, TuiFiles, TuiChip, TuiInputNumber, TuiBadge, TuiTooltip, TuiHighlight, TuiLineClamp, TuiFade, TuiRange, TuiBlock, tuiInputNumberOptionsProvider, TuiDataListWrapperComponent, TuiChevron, TuiSelect, TuiInputSlider, TuiTreeService, TuiTreeItemContent, TUI_TREE_START, TUI_TREE_CONTENT, TUI_TREE_LOADING, TUI_TREE_LOADER, TuiTree, TuiPreviewDialogService, TUI_CONFIRM, tuiFilesAccepted, TuiInputFiles, TuiInputFilesDirective, TuiFilesComponent, TuiFile, TuiFileRejectedPipe, TUI_INPUT_FILES_OPTIONS, tuiCreateFileFormatValidator, tuiCreateFileSizeValidator, TUI_INPUT_FILE_TEXTS, tuiItemsHandlersProvider as tuiItemsHandlersProvider$1, TUI_PAGINATION_TEXTS, TuiAccordionDirective, TuiAccordionItemContent } from '@taiga-ui/kit';
|
|
13
|
+
import { HttpClient, HttpErrorResponse, HttpStatusCode } from '@angular/common/http';
|
|
14
14
|
import * as i1$1 from '@angular/forms';
|
|
15
15
|
import { FormControl, NgControl, FormGroupDirective, FormGroup, Validators, FormArray, FormsModule, ReactiveFormsModule } from '@angular/forms';
|
|
16
16
|
import * as i1$2 from '@taiga-ui/core';
|
|
17
|
-
import { TuiButton, TuiDialog, TuiDialogService, TuiLink, TuiFallbackSrcPipe, TuiInitialsPipe, TuiIcon, TuiAppearance, TuiWithAppearance, TuiIcons, TuiWithIcons, TUI_DATA_LIST_HOST, tuiDropdownOptionsProvider, TuiLabel, TuiError, TuiLoader, TuiDataList, TuiNotification, TuiTextfield, TuiTitle, TuiFormatNumberPipe, TuiNumberFormat, TuiHint, TUI_MONTHS, TuiScrollbar, tuiNumberFormatProvider, tuiItemsHandlersProvider, tuiTextfieldOptionsProvider, tuiHintOptionsProvider, TuiHintDriver, TuiHintHover, TuiHintPosition, TuiHintDirective, TuiDataListComponent, TuiOption,
|
|
17
|
+
import { TuiButton, TuiDialog, TuiDialogService, TuiLink, TuiFallbackSrcPipe, TuiInitialsPipe, TuiIcon, TuiAppearance, TuiWithAppearance, TuiIcons, TuiWithIcons, TUI_DATA_LIST_HOST, tuiDropdownOptionsProvider, TuiLabel, TuiError, TuiLoader, TuiDataList, TuiNotification, TuiTextfield, TuiTitle, TuiFormatNumberPipe, TuiNumberFormat, TuiHint, TUI_MONTHS, TuiScrollbar, tuiNumberFormatProvider, tuiItemsHandlersProvider, tuiTextfieldOptionsProvider, tuiHintOptionsProvider, TuiHintDriver, TuiHintHover, TuiHintPosition, TuiHintDirective, TuiAlertService, TuiDataListComponent, TuiOption, TuiIconPipe, TUI_BUTTON_OPTIONS, TUI_BUTTON_DEFAULT_OPTIONS, TUI_SPIN_ICONS, TuiSurface, tuiFadeIn } from '@taiga-ui/core';
|
|
18
18
|
import { isValidPhoneNumber } from 'libphonenumber-js/max';
|
|
19
19
|
import * as i4 from '@taiga-ui/legacy/components/primitive-textfield';
|
|
20
20
|
import * as i3 from '@taiga-ui/legacy';
|
|
@@ -4391,6 +4391,29 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
|
|
|
4391
4391
|
}]
|
|
4392
4392
|
}] });
|
|
4393
4393
|
|
|
4394
|
+
/**
|
|
4395
|
+
* Компонент немодального уведомления с выбором «Да» или «Нет» для событий без действия пользователя, например изменения списка в фоне.
|
|
4396
|
+
* Уведомление завершается выбором пользователя. Действие пользователя подтверждается диалогом TUI_CONFIRM.
|
|
4397
|
+
*/
|
|
4398
|
+
class ScConfirmAlertComponent {
|
|
4399
|
+
constructor() {
|
|
4400
|
+
/**
|
|
4401
|
+
* Контекст уведомления: вопрос, подписи и вид кнопки согласия, выбор пользователя.
|
|
4402
|
+
*/
|
|
4403
|
+
this.context = injectContext();
|
|
4404
|
+
/**
|
|
4405
|
+
* {@link Observable} подписей кнопок по умолчанию.
|
|
4406
|
+
*/
|
|
4407
|
+
this.words$ = inject(TUI_CONFIRM_WORDS);
|
|
4408
|
+
}
|
|
4409
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: ScConfirmAlertComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
4410
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.14", type: ScConfirmAlertComponent, isStandalone: true, selector: "sc-confirm-alert", ngImport: i0, template: "@let words = words$ | async;\n<div class=\"flex flex-col items-start gap-2\">\n <span *polymorpheusOutlet=\"context.data.content as text; context: context\">{{ text }}</span>\n <div class=\"flex gap-2\">\n <button\n tuiButton\n type=\"button\"\n size=\"s\"\n [appearance]=\"context.data.appearance || 'primary'\"\n (click)=\"context.completeWith(true)\"\n >\n {{ context.data.yes || words?.yes }}\n </button>\n <button\n tuiButton\n type=\"button\"\n size=\"s\"\n appearance=\"secondary\"\n (click)=\"context.completeWith(false)\"\n >\n {{ context.data.no || words?.no }}\n </button>\n </div>\n</div>\n", dependencies: [{ kind: "pipe", type: AsyncPipe, name: "async" }, { kind: "directive", type: PolymorpheusOutlet, selector: "[polymorpheusOutlet]", inputs: ["polymorpheusOutlet", "polymorpheusOutletContext"] }, { kind: "directive", type: TuiButton, selector: "a[tuiButton],button[tuiButton],a[tuiIconButton],button[tuiIconButton]", inputs: ["size"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
4411
|
+
}
|
|
4412
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: ScConfirmAlertComponent, decorators: [{
|
|
4413
|
+
type: Component,
|
|
4414
|
+
args: [{ standalone: true, selector: 'sc-confirm-alert', imports: [AsyncPipe, PolymorpheusOutlet, TuiButton], changeDetection: ChangeDetectionStrategy.OnPush, template: "@let words = words$ | async;\n<div class=\"flex flex-col items-start gap-2\">\n <span *polymorpheusOutlet=\"context.data.content as text; context: context\">{{ text }}</span>\n <div class=\"flex gap-2\">\n <button\n tuiButton\n type=\"button\"\n size=\"s\"\n [appearance]=\"context.data.appearance || 'primary'\"\n (click)=\"context.completeWith(true)\"\n >\n {{ context.data.yes || words?.yes }}\n </button>\n <button\n tuiButton\n type=\"button\"\n size=\"s\"\n appearance=\"secondary\"\n (click)=\"context.completeWith(false)\"\n >\n {{ context.data.no || words?.no }}\n </button>\n </div>\n</div>\n" }]
|
|
4415
|
+
}] });
|
|
4416
|
+
|
|
4394
4417
|
/**
|
|
4395
4418
|
* Баннер с прокруткой переданных {@link TemplateRef} элементов, и баннеров локации.
|
|
4396
4419
|
*/
|
|
@@ -4571,6 +4594,33 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
|
|
|
4571
4594
|
}]
|
|
4572
4595
|
}] });
|
|
4573
4596
|
|
|
4597
|
+
/**
|
|
4598
|
+
* Класс-помощник для работы с файлами, выбранными пользователем.
|
|
4599
|
+
*/
|
|
4600
|
+
class ScFileHelper {
|
|
4601
|
+
/**
|
|
4602
|
+
* Читает файл и возвращает его для отправки в API: название и содержимое в виде data-URI. При отписке чтение прерывается.
|
|
4603
|
+
*
|
|
4604
|
+
* @param file Выбранный файл.
|
|
4605
|
+
*/
|
|
4606
|
+
static readAsUploadedFile$(file) {
|
|
4607
|
+
return new Observable((observer) => {
|
|
4608
|
+
const reader = new FileReader();
|
|
4609
|
+
reader.addEventListener('load', () => {
|
|
4610
|
+
observer.next(new ScUploadedFile(file.name, reader.result));
|
|
4611
|
+
observer.complete();
|
|
4612
|
+
});
|
|
4613
|
+
reader.addEventListener('error', () => {
|
|
4614
|
+
observer.error(reader.error);
|
|
4615
|
+
});
|
|
4616
|
+
reader.readAsDataURL(file);
|
|
4617
|
+
return () => {
|
|
4618
|
+
reader.abort();
|
|
4619
|
+
};
|
|
4620
|
+
});
|
|
4621
|
+
}
|
|
4622
|
+
}
|
|
4623
|
+
|
|
4574
4624
|
/**
|
|
4575
4625
|
* Компонент диалога для добавления товаров в корзину из CSV файла.
|
|
4576
4626
|
*/
|
|
@@ -4613,19 +4663,7 @@ class ScCartAddProductsFromCsvDialogComponent {
|
|
|
4613
4663
|
/**
|
|
4614
4664
|
* {@link Observable} запроса на добавление товаров из CSV файла.
|
|
4615
4665
|
*/
|
|
4616
|
-
this.submitRequest$ = this.onSubmit$.pipe(map(() => this.control.value), filter(tuiIsPresent), switchMap((file) =>
|
|
4617
|
-
const reader = new FileReader();
|
|
4618
|
-
reader.addEventListener('load', () => {
|
|
4619
|
-
observer.next(new ScUploadedFile(file.name, reader.result));
|
|
4620
|
-
});
|
|
4621
|
-
reader.addEventListener('error', (e) => {
|
|
4622
|
-
observer.error(e);
|
|
4623
|
-
});
|
|
4624
|
-
reader.readAsDataURL(file);
|
|
4625
|
-
return () => {
|
|
4626
|
-
reader.abort();
|
|
4627
|
-
};
|
|
4628
|
-
})), switchMap((file) => this.cartService.addProductsFromCsv$(file).pipe(tap(() => {
|
|
4666
|
+
this.submitRequest$ = this.onSubmit$.pipe(map(() => this.control.value), filter(tuiIsPresent), switchMap((file) => ScFileHelper.readAsUploadedFile$(file)), switchMap((file) => this.cartService.addProductsFromCsv$(file).pipe(tap(() => {
|
|
4629
4667
|
this.context.$implicit.complete();
|
|
4630
4668
|
}), startWith(null))));
|
|
4631
4669
|
/**
|
|
@@ -4901,7 +4939,7 @@ class ScAddOrEditingCartItemDialogComponent {
|
|
|
4901
4939
|
this.context.$implicit.complete();
|
|
4902
4940
|
}), catchError((error) => {
|
|
4903
4941
|
if (error instanceof HttpErrorResponse) {
|
|
4904
|
-
const { errors, message } = error.error;
|
|
4942
|
+
const { errors, message } = (error.error ?? {});
|
|
4905
4943
|
if (errors && isObject(errors)) {
|
|
4906
4944
|
Object.entries(errors).forEach(([k, v]) => {
|
|
4907
4945
|
this.formComponent().form.get(k)?.setErrors({ serverResponse: v });
|
|
@@ -4913,7 +4951,8 @@ class ScAddOrEditingCartItemDialogComponent {
|
|
|
4913
4951
|
this.formComponent().form.updateValueAndValidity();
|
|
4914
4952
|
this.formComponent().form.markAsDirty();
|
|
4915
4953
|
}
|
|
4916
|
-
|
|
4954
|
+
// Значение вместо пустого потока снимает загрузку, чтобы форму можно было отправить снова.
|
|
4955
|
+
return of(null);
|
|
4917
4956
|
}), startWith(loadingPlaceholder))), share());
|
|
4918
4957
|
/**
|
|
4919
4958
|
* {@link Observable} изменения состояния загрузки данных.
|
|
@@ -5251,6 +5290,10 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
|
|
|
5251
5290
|
class ScInputQuantityComponent extends AbstractTuiNullableControl {
|
|
5252
5291
|
constructor() {
|
|
5253
5292
|
super(...arguments);
|
|
5293
|
+
/**
|
|
5294
|
+
* Минимальное количество знаков после запятой.
|
|
5295
|
+
*/
|
|
5296
|
+
this.minPrecision = 2;
|
|
5254
5297
|
/**
|
|
5255
5298
|
* Шаг увеличения количества. Отвечает за проверку кратности ввода.
|
|
5256
5299
|
*/
|
|
@@ -5294,6 +5337,21 @@ class ScInputQuantityComponent extends AbstractTuiNullableControl {
|
|
|
5294
5337
|
get focused() {
|
|
5295
5338
|
return !!this.numberInput?.focused;
|
|
5296
5339
|
}
|
|
5340
|
+
/**
|
|
5341
|
+
* Количество знаков после запятой: по шагу, но не меньше двух.
|
|
5342
|
+
*/
|
|
5343
|
+
get precision() {
|
|
5344
|
+
return Math.max(this.minPrecision, ScNumberHelper.getDecimalsCount(this.step));
|
|
5345
|
+
}
|
|
5346
|
+
/**
|
|
5347
|
+
* Настройки формата числа поля. Точность задаётся только для шага с тремя и более знаками после запятой, иначе действует общая настройка.
|
|
5348
|
+
*/
|
|
5349
|
+
get numberFormat() {
|
|
5350
|
+
if (this.numberFormatCache?.step !== this.step) {
|
|
5351
|
+
this.numberFormatCache = { step: this.step, format: this.precision > this.minPrecision ? { precision: this.precision, decimalMode: 'not-zero' } : {} };
|
|
5352
|
+
}
|
|
5353
|
+
return this.numberFormatCache.format;
|
|
5354
|
+
}
|
|
5297
5355
|
/**
|
|
5298
5356
|
* Элемент формы ввода количества.
|
|
5299
5357
|
*/
|
|
@@ -5366,7 +5424,7 @@ class ScInputQuantityComponent extends AbstractTuiNullableControl {
|
|
|
5366
5424
|
this.clickClearEvent.emit();
|
|
5367
5425
|
}
|
|
5368
5426
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: ScInputQuantityComponent, deps: null, target: i0.ɵɵFactoryTarget.Component }); }
|
|
5369
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.14", type: ScInputQuantityComponent, isStandalone: true, selector: "sc-input-quantity", inputs: { step: "step", showLoader: "showLoader", showCross: "showCross", ignoreStepValidators: "ignoreStepValidators", appearance: "appearance", isDisabled: "isDisabled", size: "size" }, outputs: { clickClearEvent: "clickClearEvent" }, host: { listeners: { "keydown.arrowDown": "onArrow(-step)", "keydown.arrowUp": "onArrow(step)" }, properties: { "attr.data-appearance": "this.appearance", "attr.data-disabled": "this.isDisabled", "attr.data-size": "this.size" } }, viewQueries: [{ propertyName: "numberInput", first: true, predicate: TuiInputNumberComponent, descendants: true }], usesInheritance: true, ngImport: i0, template: "<tui-loader\n *ngIf=\"formControl\"\n class=\"w-full\"\n [overlay]=\"true\"\n [showLoader]=\"showLoader\"\n [size]=\"size\"\n>\n <div class=\"flex items-center gap-1 text-center\">\n <div class=\"field-with-button flex grow rounded-xl\">\n <button\n tuiIconButton\n [appearance]=\"appearance\"\n [size]=\"size\"\n [disabled]=\"!numberInput['canDecrement']\"\n (click.prevent)=\"decrement(true)\"\n >\n <tui-icon icon=\"@tui.minus\"></tui-icon>\n </button>\n <tui-input-number\n #numberInput\n [formControl]=\"formControl\"\n [tuiHint]=\"([] | tuiFieldError | async)?.message\"\n [tuiTextfieldLabelOutside]=\"true\"\n [min]=\"ignoreStepValidators ? 1 : step\"\n [tuiTextfieldSize]=\"size\"\n [required]=\"true\"\n [style.text-align]=\"'center'\"\n [style.font-weight]=\"700\"\n (focusin)=\"$any($event.target).select()\"\n oncontextmenu=\"return false;\"\n (keydown.arrowDown)=\"(0)\"\n (keydown.arrowUp)=\"(0)\"\n class=\"grow\"\n >\n </tui-input-number>\n <button\n tuiIconButton\n [appearance]=\"appearance\"\n [size]=\"size\"\n [disabled]=\"!numberInput['canIncrement']\"\n (click.prevent)=\"incident(true)\"\n >\n <tui-icon icon=\"@tui.plus\"></tui-icon>\n </button>\n </div>\n <button\n *ngIf=\"showCross\"\n tuiIconButton\n appearance=\"secondary\"\n [size]=\"size\"\n [disabled]=\"!numberInput['canIncrement']\"\n (click.prevent)=\"clear()\"\n >\n <tui-icon icon=\"@tui.x\"></tui-icon>\n </button>\n </div>\n</tui-loader>\n", styles: [":host [data-size=l]{--tui-height-l: var(--tui-height-m);--tui-font-text-m: bold .875rem/1.25rem var(--tui-font-text);--tui-padding-l: 0}:host [data-size=m]{--tui-height-m: var(--tui-height-s);--tui-font-text-s: bold .75rem/1rem var(--tui-font-text);--tui-padding-m: 0}:host [data-size=m] tui-svg{font-size:12px!important}:host [data-size=s]{--tui-height-s: var(--tui-height-xs);--tui-font-text-s: bold .75rem/1rem var(--tui-font-text);--tui-padding-s: 0}:host [data-size=s] tui-svg{font-size:12px!important}:host[data-disabled=true]{pointer-events:none;opacity:var(--tui-disabled-opacity)}:host[data-appearance=secondary] .field-with-button{background-color:var(--tui-background-base);box-shadow:inset 0 0 0 .0625rem var(--tui-background-neutral-1-hover)}:host[data-appearance=primary] .field-with-button{background-color:var(--tui-status-warning-pale)}:host .field-with-button:has(._invalid){background-color:var(--tui-status-negative-pale)}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i2$2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "pipe", type: i2$2.AsyncPipe, name: "async" }, { kind: "ngmodule", type: RouterModule }, { kind: "directive", type: TuiButton, selector: "a[tuiButton],button[tuiButton],a[tuiIconButton],button[tuiIconButton]", inputs: ["size"] }, { kind: "component", type: TuiIcon, selector: "tui-icon", inputs: ["icon", "background"] }, { kind: "ngmodule", type: TuiInputNumberModule }, { kind: "component", type: i3.TuiInputNumberComponent, selector: "tui-input-number", inputs: ["min", "max", "step"] }, { kind: "directive", type: i3.TuiInputNumberDirective, selector: "tui-input-number" }, { kind: "ngmodule", type: TuiTextfieldControllerModule }, { kind: "directive", type: i3.TuiTextfieldLabelOutsideDirective, selector: "[tuiTextfieldLabelOutside]", inputs: ["tuiTextfieldLabelOutside"] }, { kind: "directive", type: i3.TuiTextfieldSizeDirective, selector: "[tuiTextfieldSize]", inputs: ["tuiTextfieldSize"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.RequiredValidator, selector: ":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]", inputs: ["required"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1$1.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "directive", type: i1$2.TuiHintDirective, selector: "[tuiHint]:not(ng-container):not(ng-template)", inputs: ["tuiHintContext", "tuiHintAppearance", "tuiHint"], outputs: ["tuiHintVisible"] }, { kind: "pipe", type: TuiFieldErrorPipe, name: "tuiFieldError" }, { kind: "component", type: TuiLoader, selector: "tui-loader", inputs: ["size", "inheritColor", "overlay", "textContent", "showLoader"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
5427
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.14", type: ScInputQuantityComponent, isStandalone: true, selector: "sc-input-quantity", inputs: { step: "step", showLoader: "showLoader", showCross: "showCross", ignoreStepValidators: "ignoreStepValidators", appearance: "appearance", isDisabled: "isDisabled", size: "size" }, outputs: { clickClearEvent: "clickClearEvent" }, host: { listeners: { "keydown.arrowDown": "onArrow(-step)", "keydown.arrowUp": "onArrow(step)" }, properties: { "attr.data-appearance": "this.appearance", "attr.data-disabled": "this.isDisabled", "attr.data-size": "this.size" } }, viewQueries: [{ propertyName: "numberInput", first: true, predicate: TuiInputNumberComponent, descendants: true }], usesInheritance: true, ngImport: i0, template: "<tui-loader\n *ngIf=\"formControl\"\n class=\"w-full\"\n [overlay]=\"true\"\n [showLoader]=\"showLoader\"\n [size]=\"size\"\n>\n <div class=\"flex items-center gap-1 text-center\">\n <div class=\"field-with-button flex grow rounded-xl\">\n <button\n tuiIconButton\n [appearance]=\"appearance\"\n [size]=\"size\"\n [disabled]=\"!numberInput['canDecrement']\"\n (click.prevent)=\"decrement(true)\"\n >\n <tui-icon icon=\"@tui.minus\"></tui-icon>\n </button>\n <tui-input-number\n #numberInput\n [formControl]=\"formControl\"\n [tuiHint]=\"([] | tuiFieldError | async)?.message\"\n [tuiTextfieldLabelOutside]=\"true\"\n [min]=\"ignoreStepValidators ? 1 : step\"\n [tuiNumberFormat]=\"numberFormat\"\n [tuiTextfieldSize]=\"size\"\n [required]=\"true\"\n [style.text-align]=\"'center'\"\n [style.font-weight]=\"700\"\n (focusin)=\"$any($event.target).select()\"\n oncontextmenu=\"return false;\"\n (keydown.arrowDown)=\"(0)\"\n (keydown.arrowUp)=\"(0)\"\n class=\"grow\"\n >\n </tui-input-number>\n <button\n tuiIconButton\n [appearance]=\"appearance\"\n [size]=\"size\"\n [disabled]=\"!numberInput['canIncrement']\"\n (click.prevent)=\"incident(true)\"\n >\n <tui-icon icon=\"@tui.plus\"></tui-icon>\n </button>\n </div>\n <button\n *ngIf=\"showCross\"\n tuiIconButton\n appearance=\"secondary\"\n [size]=\"size\"\n [disabled]=\"!numberInput['canIncrement']\"\n (click.prevent)=\"clear()\"\n >\n <tui-icon icon=\"@tui.x\"></tui-icon>\n </button>\n </div>\n</tui-loader>\n", styles: [":host [data-size=l]{--tui-height-l: var(--tui-height-m);--tui-font-text-m: bold .875rem/1.25rem var(--tui-font-text);--tui-padding-l: 0}:host [data-size=m]{--tui-height-m: var(--tui-height-s);--tui-font-text-s: bold .75rem/1rem var(--tui-font-text);--tui-padding-m: 0}:host [data-size=m] tui-svg{font-size:12px!important}:host [data-size=s]{--tui-height-s: var(--tui-height-xs);--tui-font-text-s: bold .75rem/1rem var(--tui-font-text);--tui-padding-s: 0}:host [data-size=s] tui-svg{font-size:12px!important}:host[data-disabled=true]{pointer-events:none;opacity:var(--tui-disabled-opacity)}:host[data-appearance=secondary] .field-with-button{background-color:var(--tui-background-base);box-shadow:inset 0 0 0 .0625rem var(--tui-background-neutral-1-hover)}:host[data-appearance=primary] .field-with-button{background-color:var(--tui-status-warning-pale)}:host .field-with-button:has(._invalid){background-color:var(--tui-status-negative-pale)}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i2$2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "pipe", type: i2$2.AsyncPipe, name: "async" }, { kind: "ngmodule", type: RouterModule }, { kind: "directive", type: TuiButton, selector: "a[tuiButton],button[tuiButton],a[tuiIconButton],button[tuiIconButton]", inputs: ["size"] }, { kind: "component", type: TuiIcon, selector: "tui-icon", inputs: ["icon", "background"] }, { kind: "ngmodule", type: TuiInputNumberModule }, { kind: "component", type: i3.TuiInputNumberComponent, selector: "tui-input-number", inputs: ["min", "max", "step"] }, { kind: "directive", type: i3.TuiInputNumberDirective, selector: "tui-input-number" }, { kind: "ngmodule", type: TuiTextfieldControllerModule }, { kind: "directive", type: i3.TuiTextfieldLabelOutsideDirective, selector: "[tuiTextfieldLabelOutside]", inputs: ["tuiTextfieldLabelOutside"] }, { kind: "directive", type: i3.TuiTextfieldSizeDirective, selector: "[tuiTextfieldSize]", inputs: ["tuiTextfieldSize"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.RequiredValidator, selector: ":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]", inputs: ["required"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1$1.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "directive", type: i1$2.TuiHintDirective, selector: "[tuiHint]:not(ng-container):not(ng-template)", inputs: ["tuiHintContext", "tuiHintAppearance", "tuiHint"], outputs: ["tuiHintVisible"] }, { kind: "pipe", type: TuiFieldErrorPipe, name: "tuiFieldError" }, { kind: "component", type: TuiLoader, selector: "tui-loader", inputs: ["size", "inheritColor", "overlay", "textContent", "showLoader"] }, { kind: "directive", type: TuiNumberFormat, selector: "[tuiNumberFormat]", inputs: ["tuiNumberFormat"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
5370
5428
|
}
|
|
5371
5429
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: ScInputQuantityComponent, decorators: [{
|
|
5372
5430
|
type: Component,
|
|
@@ -5383,7 +5441,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
|
|
|
5383
5441
|
...TuiHint,
|
|
5384
5442
|
TuiFieldErrorPipe,
|
|
5385
5443
|
TuiLoader,
|
|
5386
|
-
|
|
5444
|
+
TuiNumberFormat,
|
|
5445
|
+
], changeDetection: ChangeDetectionStrategy.OnPush, template: "<tui-loader\n *ngIf=\"formControl\"\n class=\"w-full\"\n [overlay]=\"true\"\n [showLoader]=\"showLoader\"\n [size]=\"size\"\n>\n <div class=\"flex items-center gap-1 text-center\">\n <div class=\"field-with-button flex grow rounded-xl\">\n <button\n tuiIconButton\n [appearance]=\"appearance\"\n [size]=\"size\"\n [disabled]=\"!numberInput['canDecrement']\"\n (click.prevent)=\"decrement(true)\"\n >\n <tui-icon icon=\"@tui.minus\"></tui-icon>\n </button>\n <tui-input-number\n #numberInput\n [formControl]=\"formControl\"\n [tuiHint]=\"([] | tuiFieldError | async)?.message\"\n [tuiTextfieldLabelOutside]=\"true\"\n [min]=\"ignoreStepValidators ? 1 : step\"\n [tuiNumberFormat]=\"numberFormat\"\n [tuiTextfieldSize]=\"size\"\n [required]=\"true\"\n [style.text-align]=\"'center'\"\n [style.font-weight]=\"700\"\n (focusin)=\"$any($event.target).select()\"\n oncontextmenu=\"return false;\"\n (keydown.arrowDown)=\"(0)\"\n (keydown.arrowUp)=\"(0)\"\n class=\"grow\"\n >\n </tui-input-number>\n <button\n tuiIconButton\n [appearance]=\"appearance\"\n [size]=\"size\"\n [disabled]=\"!numberInput['canIncrement']\"\n (click.prevent)=\"incident(true)\"\n >\n <tui-icon icon=\"@tui.plus\"></tui-icon>\n </button>\n </div>\n <button\n *ngIf=\"showCross\"\n tuiIconButton\n appearance=\"secondary\"\n [size]=\"size\"\n [disabled]=\"!numberInput['canIncrement']\"\n (click.prevent)=\"clear()\"\n >\n <tui-icon icon=\"@tui.x\"></tui-icon>\n </button>\n </div>\n</tui-loader>\n", styles: [":host [data-size=l]{--tui-height-l: var(--tui-height-m);--tui-font-text-m: bold .875rem/1.25rem var(--tui-font-text);--tui-padding-l: 0}:host [data-size=m]{--tui-height-m: var(--tui-height-s);--tui-font-text-s: bold .75rem/1rem var(--tui-font-text);--tui-padding-m: 0}:host [data-size=m] tui-svg{font-size:12px!important}:host [data-size=s]{--tui-height-s: var(--tui-height-xs);--tui-font-text-s: bold .75rem/1rem var(--tui-font-text);--tui-padding-s: 0}:host [data-size=s] tui-svg{font-size:12px!important}:host[data-disabled=true]{pointer-events:none;opacity:var(--tui-disabled-opacity)}:host[data-appearance=secondary] .field-with-button{background-color:var(--tui-background-base);box-shadow:inset 0 0 0 .0625rem var(--tui-background-neutral-1-hover)}:host[data-appearance=primary] .field-with-button{background-color:var(--tui-status-warning-pale)}:host .field-with-button:has(._invalid){background-color:var(--tui-status-negative-pale)}\n"] }]
|
|
5387
5446
|
}], propDecorators: { numberInput: [{
|
|
5388
5447
|
type: ViewChild,
|
|
5389
5448
|
args: [TuiInputNumberComponent]
|
|
@@ -8809,6 +8868,37 @@ class ScOrderItemComponent extends AbstractScPriceCard {
|
|
|
8809
8868
|
* Признак, что необходимо отобразить элементы редактирования.
|
|
8810
8869
|
*/
|
|
8811
8870
|
this.isShowEditControls = input(true);
|
|
8871
|
+
/**
|
|
8872
|
+
* Признак, что цена за единицу берётся из позиции, а не из текущей цены товара (например, цена, зафиксированная в заявке).
|
|
8873
|
+
*/
|
|
8874
|
+
this.useLinePrice = input(false);
|
|
8875
|
+
/**
|
|
8876
|
+
* Признак, что позиция, которая не попадёт в заказ, помечается примечанием, а неактивная позиция зачёркивается.
|
|
8877
|
+
*/
|
|
8878
|
+
this.markUnavailable = input(false);
|
|
8879
|
+
/**
|
|
8880
|
+
* Признак, что изменение позиции ждёт очереди или выполняется.
|
|
8881
|
+
*/
|
|
8882
|
+
this.isPending = input(false);
|
|
8883
|
+
/**
|
|
8884
|
+
* Признак, что параметры позиции можно изменить в диалоге, даже если они ещё не заданы.
|
|
8885
|
+
*/
|
|
8886
|
+
this.settingsAvailable = input(false);
|
|
8887
|
+
}
|
|
8888
|
+
/**
|
|
8889
|
+
* Оформление примечания к позиции: группа, которая не участвует в заказе, серым, остальные причины жёлтым.
|
|
8890
|
+
*/
|
|
8891
|
+
get unavailableReasonAppearance() {
|
|
8892
|
+
return this.orderItem?.isUnavailableReasonNeutral ? 'neutral' : 'warning';
|
|
8893
|
+
}
|
|
8894
|
+
/**
|
|
8895
|
+
* Подсказка к количеству, когда поля количества нет: где его изменить. У неактивной позиции с примечанием количество не меняется, и подсказки нет.
|
|
8896
|
+
*/
|
|
8897
|
+
get quantityHint() {
|
|
8898
|
+
if (this.settingsAvailable()) {
|
|
8899
|
+
return 'Количество и метраж меняются в окне изменения позиции';
|
|
8900
|
+
}
|
|
8901
|
+
return this.markUnavailable() && this.orderItem?.isInactive ? '' : 'Для изменения количества воспользуйтесь конфигуратором';
|
|
8812
8902
|
}
|
|
8813
8903
|
/**
|
|
8814
8904
|
* Отображает спецификацию.
|
|
@@ -8829,13 +8919,13 @@ class ScOrderItemComponent extends AbstractScPriceCard {
|
|
|
8829
8919
|
}
|
|
8830
8920
|
}
|
|
8831
8921
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: ScOrderItemComponent, deps: null, target: i0.ɵɵFactoryTarget.Component }); }
|
|
8832
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.14", type: ScOrderItemComponent, isStandalone: true, selector: "sc-order-item", inputs: { isShowEditControls: { classPropertyName: "isShowEditControls", publicName: "isShowEditControls", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { clickSettings: "clickSettings" }, providers: [
|
|
8922
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.14", type: ScOrderItemComponent, isStandalone: true, selector: "sc-order-item", inputs: { isShowEditControls: { classPropertyName: "isShowEditControls", publicName: "isShowEditControls", isSignal: true, isRequired: false, transformFunction: null }, useLinePrice: { classPropertyName: "useLinePrice", publicName: "useLinePrice", isSignal: true, isRequired: false, transformFunction: null }, markUnavailable: { classPropertyName: "markUnavailable", publicName: "markUnavailable", isSignal: true, isRequired: false, transformFunction: null }, isPending: { classPropertyName: "isPending", publicName: "isPending", isSignal: true, isRequired: false, transformFunction: null }, settingsAvailable: { classPropertyName: "settingsAvailable", publicName: "settingsAvailable", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { clickSettings: "clickSettings" }, providers: [
|
|
8833
8923
|
SEARCH_TERM_PROVIDERS,
|
|
8834
8924
|
tuiTextfieldOptionsProvider({
|
|
8835
8925
|
appearance: signal('textfield'),
|
|
8836
8926
|
}),
|
|
8837
8927
|
tuiHintOptionsProvider({ direction: 'top', showDelay: 100, hideDelay: 100 }),
|
|
8838
|
-
], usesInheritance: true, ngImport: i0, template: "@if (product && orderItem) {\n <div class=\"@container relative flex w-full gap-4 rounded-xl p-1 text-body-s hover:bg-tui-background-neutral-1\">\n <sc-noindex-wrapper>\n @let isDisabled = product.isHidden || product.isNull;\n <sc-hover-image-carousel\n (click)=\"isDisabled ? null : clickCardEvent.emit()\"\n [images]=\"!isMobile ? getCardImagePreviewList(product) : [getCardImagePreview()]\"\n [isShowActions]=\"false\"\n class=\"aspect-square w-24 shrink-0 cursor-pointer self-start\"\n />\n <div class=\"flex w-full flex-col gap-y-1 gap-x-4 @5xl:flex-row @5xl:items-center @5xl:gap-5\">\n <div class=\"flex min-w-0 flex-1 flex-col gap-1\">\n <div\n [tuiHighlight]=\"(search$ | async) ?? ''\"\n class=\"text-tui-text-02\"\n >\n \u0410\u0440\u0442\u0438\u043A\u0443\u043B: {{ product.code }}\n </div>\n <div class=\"flex flex-col gap-1\">\n <a\n tuiLink\n [attr.href]=\"href ?? null\"\n [tuiHighlight]=\"(search$ | async) ?? ''\"\n (click)=\"$event.preventDefault(); clickCardEvent.emit()\"\n [class.disabled]=\"isDisabled\"\n class=\"!text-body-m-bold\"\n >\n {{ product.name }}\n </a>\n @if (product.supplierSku) {\n <span class=\"text-tui-text-02\"> \u0410\u0440\u0442\u0438\u043A\u0443\u043B \u043F\u0440\u043E\u0438\u0437\u0432\u043E\u0434\u0438\u0442\u0435\u043B\u044F: {{ product.supplierSku }} </span>\n }\n @if (product.pack) {\n <div class=\"flex items-center gap-1 text-tui-text-02\">\n \u041D\u043E\u0440\u043C\u0430 \u0443\u043F\u0430\u043A\u043E\u0432\u043A\u0438: {{ product.pack }}\n @if (product.ignoreMinCountCheck) {\n <tui-icon\n icon=\"@tui.package\"\n [tuiHint]=\"minCountHint\"\n [tuiHintShowDelay]=\"100\"\n tuiHintDirection=\"top\"\n class=\"text-body-xl text-tui-text-01 opacity-90\"\n />\n <ng-template #minCountHint>\n \u0414\u043E\u0441\u0442\u0443\u043F\u0435\u043D \u0437\u0430\u043A\u0430\u0437 <br />\n \u043F\u0440\u043E\u0438\u0437\u0432\u043E\u043B\u044C\u043D\u043E\u0433\u043E <br />\n \u043A\u043E\u043B\u0438\u0447\u0435\u0441\u0442\u0432\u0430\n </ng-template>\n }\n </div>\n }\n\n <sc-cost-with-discount\n scPrint=\"cost\"\n [product]=\"product\"\n />\n </div>\n @if (orderItem.specificationImgUrl) {\n <a\n tuiLink\n (click)=\"showSpecification(specificationPreview)\"\n [class.disabled]=\"isDisabled\"\n >\n \u0421\u043F\u0435\u0446\u0438\u0444\u0438\u043A\u0430\u0446\u0438\u044F\n </a>\n }\n <ng-template\n #specificationPreview\n let-preview\n >\n <tui-preview\n [rotatable]=\"false\"\n [zoomable]=\"false\"\n >\n <img\n *polymorpheusOutlet=\"orderItem.specificationImgUrl as src\"\n alt=\"preview\"\n [src]=\"orderItem.specificationImgUrl\"\n />\n <button\n iconStart=\"@tui.x\"\n title=\"Close\"\n tuiIconButton\n tuiPreviewAction\n type=\"button\"\n (click)=\"preview.complete()\"\n ></button>\n </tui-preview>\n </ng-template>\n </div>\n <div class=\"grid grid-cols-1 @lg:grid-cols-3 gap-2 @2xl:gap-4 @2xl:grid-rows-1\">\n <div class=\"grid grid-cols-1 items-center @lg:gap-2 @2xl:grid-cols-2 @lg:col-span-2\">\n <!-- \u041A\u043E\u043B\u0438\u0447\u0435\u0441\u0442\u0432\u043E \u0442\u043E\u0432\u0430\u0440\u0430. -->\n @if (isShowEditControls() && showQuantityControl) {\n <!-- \u041F\u043E\u043B\u0435 \u0432\u0432\u043E\u0434\u0430 \u043A\u043E\u043B\u0438\u0447\u0435\u0441\u0442\u0432\u0430 \u0442\u043E\u0432\u0430\u0440\u0430. -->\n <sc-input-quantity\n #inputQuantity\n [tuiAutoFocus]=\"autoFocuseQuantityInput()\"\n [formControl]=\"quantityControl\"\n size=\"m\"\n [showCross]=\"false\"\n [isDisabled]=\"isDisabled\"\n [step]=\"ScUnitsHelper.getProductMultiplicity(product)\"\n [ignoreStepValidators]=\"product.ignoreMinCountCheck\"\n [showLoader]=\"showLoader()\"\n (clickClearEvent)=\"clickClearEvent.emit(orderItem)\"\n (keydown.enter)=\"inputQuantity.nativeFocusableElement?.blur()\"\n class=\"w-32 shrink-0\"\n />\n } @else {\n <button\n [appearance]=\"isShowEditControls() ? 'action-grayscale' : ''\"\n tuiTheme=\"light\"\n tuiHint=\"\u0414\u043B\u044F \u0438\u0437\u043C\u0435\u043D\u0435\u043D\u0438\u044F \u043A\u043E\u043B\u0438\u0447\u0435\u0441\u0442\u0432\u0430 \u0432\u043E\u0441\u043F\u043E\u043B\u044C\u0437\u0443\u0439\u0442\u0435\u0441\u044C \u043A\u043E\u043D\u0444\u0438\u0433\u0443\u0440\u0430\u0442\u043E\u0440\u043E\u043C\"\n (tuiHintVisible)=\"configuratorHintVisible($event)\"\n tuiLink\n [style.text-decoration-line]=\"isShowEditControls() ? 'underline' : 'none'\"\n [class.pointer-events-none]=\"!isShowEditControls()\"\n >\n <div class=\"!text-body-m text-start\">\n \u041A\u043E\u043B\u0438\u0447\u0435\u0441\u0442\u0432\u043E:\n <strong> {{ orderItem.quantity }} \u0448\u0442.</strong>\n </div>\n </button>\n }\n\n @let showAdditionalInfo = orderItem.configurator || orderItem.height || orderItem.length || orderItem.width || orderItem.marker;\n @if (showAdditionalInfo) {\n <div class=\"flex items-center gap-2 @lg:gap-3 max-w-min\">\n <!-- \u041A\u043D\u043E\u043F\u043A\u0430 \u0440\u0435\u0434\u0430\u043A\u0442\u0438\u0440\u043E\u0432\u0430\u043D\u0438\u044F \u0442\u043E\u0432\u0430\u0440\u0430. -->\n @if (isShowEditControls()) {\n <button\n tuiIconButton\n iconStart=\"@tui.settings\"\n [disabled]=\"isDisabled\"\n [loading]=\"showLoader()\"\n (click)=\"clickSettings.emit()\"\n size=\"s\"\n [appearance]=\"isConfiguratorHintVisible() ? 'primary' : 'secondary'\"\n class=\"shrink-0\"\n ></button>\n }\n\n <!-- \u0414\u043E\u043F\u043E\u043B\u043D\u0438\u0442\u0435\u043B\u044C\u043D\u0430\u044F \u0438\u043D\u0444\u043E\u0440\u043C\u0430\u0446\u0438\u044F \u043E \u0442\u043E\u0432\u0430\u0440\u0435. -->\n <div\n class=\"flex flex-col gap-x-1 text-tui-base-07\"\n [class.text-body-m]=\"!isShowEditControls()\"\n >\n @if (orderItem.marker) {\n <p>\u041C\u0430\u0440\u043A\u0438\u0440\u043E\u0432\u043A\u0430: {{ orderItem.marker }}</p>\n }\n @if (orderItem.width) {\n <button\n [appearance]=\"isShowEditControls() ? 'action-grayscale' : ''\"\n tuiTheme=\"light\"\n tuiHint=\"\u0414\u043B\u044F \u0438\u0437\u043C\u0435\u043D\u0435\u043D\u0438\u044F \u0448\u0438\u0440\u0438\u043D\u044B \u0432\u043E\u0441\u043F\u043E\u043B\u044C\u0437\u0443\u0439\u0442\u0435\u0441\u044C \u043A\u043E\u043D\u0444\u0438\u0433\u0443\u0440\u0430\u0442\u043E\u0440\u043E\u043C\"\n (tuiHintVisible)=\"configuratorHintVisible($event)\"\n tuiLink\n [style.text-decoration-line]=\"isShowEditControls() ? 'underline' : 'none'\"\n [class.pointer-events-none]=\"!isShowEditControls()\"\n class=\"text-body-s self-start whitespace-nowrap\"\n >\n \u0428\u0438\u0440\u0438\u043D\u0430: <span class=\"text-body-s-bold\">{{ orderItem.width }} \u043C.</span>\n </button>\n }\n @if (orderItem.height) {\n <button\n [appearance]=\"isShowEditControls() ? 'action-grayscale' : ''\"\n tuiTheme=\"light\"\n tuiHint=\"\u0414\u043B\u044F \u0438\u0437\u043C\u0435\u043D\u0435\u043D\u0438\u044F \u0432\u044B\u0441\u043E\u0442\u044B \u0432\u043E\u0441\u043F\u043E\u043B\u044C\u0437\u0443\u0439\u0442\u0435\u0441\u044C \u043A\u043E\u043D\u0444\u0438\u0433\u0443\u0440\u0430\u0442\u043E\u0440\u043E\u043C\"\n (tuiHintVisible)=\"configuratorHintVisible($event)\"\n tuiLink\n [style.text-decoration-line]=\"isShowEditControls() ? 'underline' : 'none'\"\n [class.pointer-events-none]=\"!isShowEditControls()\"\n class=\"text-body-s self-start whitespace-nowrap\"\n >\n \u0412\u044B\u0441\u043E\u0442\u0430: <span class=\"text-body-s-bold\">{{ orderItem.height }} \u043C.</span>\n </button>\n } @else if (orderItem.length) {\n <button\n [appearance]=\"isShowEditControls() ? 'action-grayscale' : ''\"\n tuiTheme=\"light\"\n tuiHint=\"\u0414\u043B\u044F \u0438\u0437\u043C\u0435\u043D\u0435\u043D\u0438\u044F \u0434\u043B\u0438\u043D\u044B \u0432\u043E\u0441\u043F\u043E\u043B\u044C\u0437\u0443\u0439\u0442\u0435\u0441\u044C \u043A\u043E\u043D\u0444\u0438\u0433\u0443\u0440\u0430\u0442\u043E\u0440\u043E\u043C\"\n (tuiHintVisible)=\"configuratorHintVisible($event)\"\n tuiLink\n [style.text-decoration-line]=\"isShowEditControls() ? 'underline' : 'none'\"\n [class.pointer-events-none]=\"!isShowEditControls()\"\n class=\"text-body-s self-start whitespace-nowrap\"\n >\n \u0414\u043B\u0438\u043D\u0430: <span class=\"text-body-s-bold\">{{ orderItem.length }} \u043C.</span>\n </button>\n }\n </div>\n </div>\n }\n </div>\n\n <!-- \u0421\u0443\u043C\u043C\u0430 \u0442\u043E\u0432\u0430\u0440\u0430 \u0438 \u0441\u043A\u043B\u0430\u0434\u0441\u043A\u043E\u0435 \u043D\u0430\u043B\u0438\u0447\u0438\u0435. -->\n <div\n scPrint=\"cost\"\n class=\"flex min-w-[11rem] flex-col @lg:justify-end @2xl:!justify-center text-body-m\"\n >\n <p class=\"whitespace-nowrap\">\n \u0421\u0443\u043C\u043C\u0430:\n <strong> {{ orderItem.costRub | tuiFormatNumber: moneyFormat | async }} {{ 'RUB' | tuiCurrency }}</strong>\n </p>\n <sc-price-warehouse-stock\n scPrint=\"defaultNoPrint\"\n [product]=\"product\"\n />\n </div>\n </div>\n </div>\n </sc-noindex-wrapper>\n </div>\n} @else {\n <div class=\"tui-skeleton flex h-[8.25rem] w-full overflow-hidden rounded-xl\"></div>\n}\n", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "pipe", type: i2$2.AsyncPipe, name: "async" }, { kind: "ngmodule", type: RouterModule }, { kind: "directive", type: TuiButton, selector: "a[tuiButton],button[tuiButton],a[tuiIconButton],button[tuiIconButton]", inputs: ["size"] }, { kind: "component", type: TuiIcon, selector: "tui-icon", inputs: ["icon", "background"] }, { kind: "ngmodule", type: TuiTextfieldControllerModule }, { kind: "directive", type: i2$3.PolymorpheusOutlet, selector: "[polymorpheusOutlet]", inputs: ["polymorpheusOutlet", "polymorpheusOutletContext"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1$1.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "directive", type: i1$2.TuiHintDirective, selector: "[tuiHint]:not(ng-container):not(ng-template)", inputs: ["tuiHintContext", "tuiHintAppearance", "tuiHint"], outputs: ["tuiHintVisible"] }, { kind: "directive", type: TuiLink, selector: "a[tuiLink], button[tuiLink]", inputs: ["pseudo"] }, { kind: "directive", type: TuiHighlight, selector: "[tuiHighlight]", inputs: ["tuiHighlight", "tuiHighlightColor"] }, { kind: "directive", type: TuiAutoFocus, selector: "[tuiAutoFocus]", inputs: ["tuiAutoFocus"] }, { kind: "component", type: ScHoverImageCarouselComponent, selector: "sc-hover-image-carousel", inputs: ["images", "isShowActions", "productName"] }, { kind: "component", type: CostWithDiscountComponent, selector: "sc-cost-with-discount", inputs: ["product", "size"] }, { kind: "component", type: ScPriceWarehouseStockComponent, selector: "sc-price-warehouse-stock", inputs: ["classList", "product", "withStockHint", "fromMain"] }, { kind: "component", type: ScInputQuantityComponent, selector: "sc-input-quantity", inputs: ["step", "showLoader", "showCross", "ignoreStepValidators", "appearance", "isDisabled", "size"], outputs: ["clickClearEvent"] }, { kind: "pipe", type: TuiCurrencyPipe, name: "tuiCurrency" }, { kind: "pipe", type: TuiFormatNumberPipe, name: "tuiFormatNumber" }, { kind: "component", type: i2$1.TuiPreviewComponent, selector: "tui-preview", inputs: ["zoomable", "rotatable", "initialScale"] }, { kind: "directive", type: i2$1.TuiPreviewAction, selector: "[tuiPreviewAction]" }, { kind: "component", type: ScNoindexWrapperComponent, selector: "sc-noindex-wrapper" }, { kind: "component", type: TuiButtonLoading, selector: "[tuiButton][loading],[tuiIconButton][loading]", inputs: ["size", "loading"] }, { kind: "directive", type: ScPrintDirective, selector: "[scPrint]", inputs: ["scPrint"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
8928
|
+
], usesInheritance: true, ngImport: i0, template: "@if (product && orderItem) {\n <div class=\"@container relative flex w-full gap-4 rounded-xl p-1 text-body-s hover:bg-tui-background-neutral-1\">\n <sc-noindex-wrapper>\n @let isDisabled = orderItem.isInactive;\n @let unavailableReason = markUnavailable() ? orderItem.getUnavailableReason() : '';\n <sc-hover-image-carousel\n (click)=\"isDisabled ? null : clickCardEvent.emit()\"\n [images]=\"!isMobile ? getCardImagePreviewList(product) : [getCardImagePreview()]\"\n [isShowActions]=\"false\"\n class=\"aspect-square w-24 shrink-0 cursor-pointer self-start\"\n />\n <div class=\"flex w-full flex-col gap-y-1 gap-x-4 @5xl:flex-row @5xl:items-center @5xl:gap-5\">\n <div class=\"flex min-w-0 flex-1 flex-col gap-1\">\n @if (product.code) {\n <div\n [tuiHighlight]=\"(search$ | async) ?? ''\"\n class=\"text-tui-text-02\"\n >\n \u0410\u0440\u0442\u0438\u043A\u0443\u043B: {{ product.code }}\n </div>\n }\n <div class=\"flex flex-col gap-1\">\n <a\n tuiLink\n [attr.href]=\"href ?? null\"\n [tuiHighlight]=\"(search$ | async) ?? ''\"\n (click)=\"$event.preventDefault(); clickCardEvent.emit()\"\n [class.disabled]=\"isDisabled\"\n [class.!line-through]=\"markUnavailable() && isDisabled\"\n class=\"!text-body-m-bold\"\n >\n {{ product.name }}\n </a>\n @if (unavailableReason) {\n <div class=\"flex flex-wrap items-baseline gap-1\">\n <span class=\"text-tui-text-02\">\u041F\u0440\u0438\u043C\u0435\u0447\u0430\u043D\u0438\u0435:</span>\n <tui-badge\n [appearance]=\"unavailableReasonAppearance\"\n size=\"m\"\n class=\"!h-auto max-w-full !whitespace-normal py-0.5 text-start\"\n >\n {{ unavailableReason }}\n </tui-badge>\n </div>\n }\n @if (product.supplierSku) {\n <span class=\"text-tui-text-02\"> \u0410\u0440\u0442\u0438\u043A\u0443\u043B \u043F\u0440\u043E\u0438\u0437\u0432\u043E\u0434\u0438\u0442\u0435\u043B\u044F: {{ product.supplierSku }} </span>\n }\n @if (product.pack) {\n <div class=\"flex items-center gap-1 text-tui-text-02\">\n \u041D\u043E\u0440\u043C\u0430 \u0443\u043F\u0430\u043A\u043E\u0432\u043A\u0438: {{ product.pack }}\n @if (product.ignoreMinCountCheck) {\n <tui-icon\n icon=\"@tui.package\"\n [tuiHint]=\"minCountHint\"\n [tuiHintShowDelay]=\"100\"\n tuiHintDirection=\"top\"\n class=\"text-body-xl text-tui-text-01 opacity-90\"\n />\n <ng-template #minCountHint>\n \u0414\u043E\u0441\u0442\u0443\u043F\u0435\u043D \u0437\u0430\u043A\u0430\u0437 <br />\n \u043F\u0440\u043E\u0438\u0437\u0432\u043E\u043B\u044C\u043D\u043E\u0433\u043E <br />\n \u043A\u043E\u043B\u0438\u0447\u0435\u0441\u0442\u0432\u0430\n </ng-template>\n }\n </div>\n }\n\n @if (useLinePrice()) {\n @if (orderItem.costOneRub > 0 || orderItem.isOrderable) {\n <span\n scPrint=\"cost\"\n class=\"text-body-m\"\n >\n {{ orderItem.costOneRub > 0 ? orderItem.getCostInRubDescPerItem() : orderItem.getCostMessage() }}\n </span>\n }\n } @else {\n <sc-cost-with-discount\n scPrint=\"cost\"\n [product]=\"product\"\n />\n }\n </div>\n @if (orderItem.specificationImgUrl) {\n <a\n tuiLink\n (click)=\"showSpecification(specificationPreview)\"\n [class.disabled]=\"isDisabled\"\n >\n \u0421\u043F\u0435\u0446\u0438\u0444\u0438\u043A\u0430\u0446\u0438\u044F\n </a>\n }\n <ng-template\n #specificationPreview\n let-preview\n >\n <tui-preview\n [rotatable]=\"false\"\n [zoomable]=\"false\"\n >\n <img\n *polymorpheusOutlet=\"orderItem.specificationImgUrl as src\"\n alt=\"preview\"\n [src]=\"orderItem.specificationImgUrl\"\n />\n <button\n iconStart=\"@tui.x\"\n title=\"Close\"\n tuiIconButton\n tuiPreviewAction\n type=\"button\"\n (click)=\"preview.complete()\"\n ></button>\n </tui-preview>\n </ng-template>\n </div>\n <div class=\"grid grid-cols-1 @lg:grid-cols-3 gap-2 @2xl:gap-4 @2xl:grid-rows-1\">\n <div class=\"grid grid-cols-1 items-center @lg:gap-2 @2xl:grid-cols-2 @lg:col-span-2\">\n <!-- \u041A\u043E\u043B\u0438\u0447\u0435\u0441\u0442\u0432\u043E \u0442\u043E\u0432\u0430\u0440\u0430. -->\n @if (isShowEditControls() && showQuantityControl) {\n <!-- \u041F\u043E\u043B\u0435 \u0432\u0432\u043E\u0434\u0430 \u043A\u043E\u043B\u0438\u0447\u0435\u0441\u0442\u0432\u0430 \u0442\u043E\u0432\u0430\u0440\u0430. -->\n <sc-input-quantity\n #inputQuantity\n [tuiAutoFocus]=\"autoFocuseQuantityInput()\"\n [formControl]=\"quantityControl\"\n size=\"m\"\n [showCross]=\"false\"\n [isDisabled]=\"isDisabled\"\n [attr.inert]=\"isDisabled || null\"\n [step]=\"orderItem.quantityStep\"\n [ignoreStepValidators]=\"product.ignoreMinCountCheck\"\n [showLoader]=\"isPending()\"\n (clickClearEvent)=\"clickClearEvent.emit(orderItem)\"\n (keydown.enter)=\"inputQuantity.nativeFocusableElement?.blur()\"\n class=\"w-32 shrink-0\"\n />\n } @else {\n <button\n [appearance]=\"isShowEditControls() ? 'action-grayscale' : ''\"\n tuiTheme=\"light\"\n [tuiHint]=\"quantityHint\"\n (tuiHintVisible)=\"configuratorHintVisible($event)\"\n tuiLink\n [style.text-decoration-line]=\"isShowEditControls() ? 'underline' : 'none'\"\n [class.pointer-events-none]=\"!isShowEditControls()\"\n >\n <div class=\"!text-body-m text-start\">\n \u041A\u043E\u043B\u0438\u0447\u0435\u0441\u0442\u0432\u043E:\n <strong> {{ orderItem.quantity }} \u0448\u0442.</strong>\n </div>\n </button>\n }\n\n @let showAdditionalInfo = orderItem.configurator || orderItem.height || orderItem.length || orderItem.width || orderItem.marker || settingsAvailable();\n @if (showAdditionalInfo) {\n <div class=\"flex items-center gap-2 @lg:gap-3 max-w-min\">\n <!-- \u041A\u043D\u043E\u043F\u043A\u0430 \u0440\u0435\u0434\u0430\u043A\u0442\u0438\u0440\u043E\u0432\u0430\u043D\u0438\u044F \u0442\u043E\u0432\u0430\u0440\u0430. -->\n @if (isShowEditControls()) {\n <button\n tuiIconButton\n iconStart=\"@tui.settings\"\n [disabled]=\"isDisabled\"\n [loading]=\"isPending()\"\n (click)=\"clickSettings.emit()\"\n size=\"s\"\n [appearance]=\"isConfiguratorHintVisible() ? 'primary' : 'secondary'\"\n class=\"shrink-0\"\n ></button>\n }\n\n <!-- \u0414\u043E\u043F\u043E\u043B\u043D\u0438\u0442\u0435\u043B\u044C\u043D\u0430\u044F \u0438\u043D\u0444\u043E\u0440\u043C\u0430\u0446\u0438\u044F \u043E \u0442\u043E\u0432\u0430\u0440\u0435. -->\n <div\n class=\"flex flex-col gap-x-1 text-tui-base-07\"\n [class.text-body-m]=\"!isShowEditControls()\"\n >\n @if (orderItem.marker) {\n <p>\u041C\u0430\u0440\u043A\u0438\u0440\u043E\u0432\u043A\u0430: {{ orderItem.marker }}</p>\n }\n @if (orderItem.width) {\n <button\n [appearance]=\"isShowEditControls() ? 'action-grayscale' : ''\"\n tuiTheme=\"light\"\n tuiHint=\"\u0414\u043B\u044F \u0438\u0437\u043C\u0435\u043D\u0435\u043D\u0438\u044F \u0448\u0438\u0440\u0438\u043D\u044B \u0432\u043E\u0441\u043F\u043E\u043B\u044C\u0437\u0443\u0439\u0442\u0435\u0441\u044C \u043A\u043E\u043D\u0444\u0438\u0433\u0443\u0440\u0430\u0442\u043E\u0440\u043E\u043C\"\n (tuiHintVisible)=\"configuratorHintVisible($event)\"\n tuiLink\n [style.text-decoration-line]=\"isShowEditControls() ? 'underline' : 'none'\"\n [class.pointer-events-none]=\"!isShowEditControls()\"\n class=\"text-body-s self-start whitespace-nowrap\"\n >\n \u0428\u0438\u0440\u0438\u043D\u0430: <span class=\"text-body-s-bold\">{{ orderItem.width }} \u043C.</span>\n </button>\n }\n @if (orderItem.height) {\n <button\n [appearance]=\"isShowEditControls() ? 'action-grayscale' : ''\"\n tuiTheme=\"light\"\n tuiHint=\"\u0414\u043B\u044F \u0438\u0437\u043C\u0435\u043D\u0435\u043D\u0438\u044F \u0432\u044B\u0441\u043E\u0442\u044B \u0432\u043E\u0441\u043F\u043E\u043B\u044C\u0437\u0443\u0439\u0442\u0435\u0441\u044C \u043A\u043E\u043D\u0444\u0438\u0433\u0443\u0440\u0430\u0442\u043E\u0440\u043E\u043C\"\n (tuiHintVisible)=\"configuratorHintVisible($event)\"\n tuiLink\n [style.text-decoration-line]=\"isShowEditControls() ? 'underline' : 'none'\"\n [class.pointer-events-none]=\"!isShowEditControls()\"\n class=\"text-body-s self-start whitespace-nowrap\"\n >\n \u0412\u044B\u0441\u043E\u0442\u0430: <span class=\"text-body-s-bold\">{{ orderItem.height }} \u043C.</span>\n </button>\n } @else if (orderItem.length) {\n <button\n [appearance]=\"isShowEditControls() ? 'action-grayscale' : ''\"\n tuiTheme=\"light\"\n tuiHint=\"\u0414\u043B\u044F \u0438\u0437\u043C\u0435\u043D\u0435\u043D\u0438\u044F \u0434\u043B\u0438\u043D\u044B \u0432\u043E\u0441\u043F\u043E\u043B\u044C\u0437\u0443\u0439\u0442\u0435\u0441\u044C \u043A\u043E\u043D\u0444\u0438\u0433\u0443\u0440\u0430\u0442\u043E\u0440\u043E\u043C\"\n (tuiHintVisible)=\"configuratorHintVisible($event)\"\n tuiLink\n [style.text-decoration-line]=\"isShowEditControls() ? 'underline' : 'none'\"\n [class.pointer-events-none]=\"!isShowEditControls()\"\n class=\"text-body-s self-start whitespace-nowrap\"\n >\n \u0414\u043B\u0438\u043D\u0430: <span class=\"text-body-s-bold\">{{ orderItem.length }} \u043C.</span>\n </button>\n }\n </div>\n </div>\n }\n </div>\n\n <!-- \u0421\u0443\u043C\u043C\u0430 \u0442\u043E\u0432\u0430\u0440\u0430 \u0438 \u0441\u043A\u043B\u0430\u0434\u0441\u043A\u043E\u0435 \u043D\u0430\u043B\u0438\u0447\u0438\u0435. -->\n <div\n scPrint=\"cost\"\n class=\"flex min-w-[11rem] flex-col @lg:justify-end @2xl:!justify-center text-body-m\"\n >\n <p class=\"whitespace-nowrap\">\n \u0421\u0443\u043C\u043C\u0430:\n <strong> {{ orderItem.costRub | tuiFormatNumber: moneyFormat | async }} {{ 'RUB' | tuiCurrency }}</strong>\n </p>\n @if (!(markUnavailable() && product.isNull)) {\n <sc-price-warehouse-stock\n scPrint=\"defaultNoPrint\"\n [product]=\"product\"\n />\n }\n </div>\n </div>\n </div>\n </sc-noindex-wrapper>\n </div>\n} @else {\n <div class=\"tui-skeleton flex h-[8.25rem] w-full overflow-hidden rounded-xl\"></div>\n}\n", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "pipe", type: i2$2.AsyncPipe, name: "async" }, { kind: "ngmodule", type: RouterModule }, { kind: "directive", type: TuiButton, selector: "a[tuiButton],button[tuiButton],a[tuiIconButton],button[tuiIconButton]", inputs: ["size"] }, { kind: "component", type: TuiIcon, selector: "tui-icon", inputs: ["icon", "background"] }, { kind: "ngmodule", type: TuiTextfieldControllerModule }, { kind: "directive", type: i2$3.PolymorpheusOutlet, selector: "[polymorpheusOutlet]", inputs: ["polymorpheusOutlet", "polymorpheusOutletContext"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1$1.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "directive", type: i1$2.TuiHintDirective, selector: "[tuiHint]:not(ng-container):not(ng-template)", inputs: ["tuiHintContext", "tuiHintAppearance", "tuiHint"], outputs: ["tuiHintVisible"] }, { kind: "directive", type: TuiLink, selector: "a[tuiLink], button[tuiLink]", inputs: ["pseudo"] }, { kind: "directive", type: TuiHighlight, selector: "[tuiHighlight]", inputs: ["tuiHighlight", "tuiHighlightColor"] }, { kind: "directive", type: TuiAutoFocus, selector: "[tuiAutoFocus]", inputs: ["tuiAutoFocus"] }, { kind: "component", type: ScHoverImageCarouselComponent, selector: "sc-hover-image-carousel", inputs: ["images", "isShowActions", "productName"] }, { kind: "component", type: CostWithDiscountComponent, selector: "sc-cost-with-discount", inputs: ["product", "size"] }, { kind: "component", type: ScPriceWarehouseStockComponent, selector: "sc-price-warehouse-stock", inputs: ["classList", "product", "withStockHint", "fromMain"] }, { kind: "component", type: ScInputQuantityComponent, selector: "sc-input-quantity", inputs: ["step", "showLoader", "showCross", "ignoreStepValidators", "appearance", "isDisabled", "size"], outputs: ["clickClearEvent"] }, { kind: "pipe", type: TuiCurrencyPipe, name: "tuiCurrency" }, { kind: "pipe", type: TuiFormatNumberPipe, name: "tuiFormatNumber" }, { kind: "component", type: i2$1.TuiPreviewComponent, selector: "tui-preview", inputs: ["zoomable", "rotatable", "initialScale"] }, { kind: "directive", type: i2$1.TuiPreviewAction, selector: "[tuiPreviewAction]" }, { kind: "directive", type: TuiBadge, selector: "tui-badge,[tuiBadge]", inputs: ["size"] }, { kind: "component", type: ScNoindexWrapperComponent, selector: "sc-noindex-wrapper" }, { kind: "component", type: TuiButtonLoading, selector: "[tuiButton][loading],[tuiIconButton][loading]", inputs: ["size", "loading"] }, { kind: "directive", type: ScPrintDirective, selector: "[scPrint]", inputs: ["scPrint"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
8839
8929
|
}
|
|
8840
8930
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: ScOrderItemComponent, decorators: [{
|
|
8841
8931
|
type: Component,
|
|
@@ -8862,6 +8952,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
|
|
|
8862
8952
|
TuiCurrencyPipe,
|
|
8863
8953
|
TuiFormatNumberPipe,
|
|
8864
8954
|
TuiPreview,
|
|
8955
|
+
TuiBadge,
|
|
8865
8956
|
ScNoindexWrapperComponent,
|
|
8866
8957
|
TuiAppearance,
|
|
8867
8958
|
TuiButtonLoading,
|
|
@@ -8872,7 +8963,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
|
|
|
8872
8963
|
appearance: signal('textfield'),
|
|
8873
8964
|
}),
|
|
8874
8965
|
tuiHintOptionsProvider({ direction: 'top', showDelay: 100, hideDelay: 100 }),
|
|
8875
|
-
], changeDetection: ChangeDetectionStrategy.OnPush, template: "@if (product && orderItem) {\n <div class=\"@container relative flex w-full gap-4 rounded-xl p-1 text-body-s hover:bg-tui-background-neutral-1\">\n <sc-noindex-wrapper>\n @let isDisabled = product.isHidden || product.isNull;\n <sc-hover-image-carousel\n (click)=\"isDisabled ? null : clickCardEvent.emit()\"\n [images]=\"!isMobile ? getCardImagePreviewList(product) : [getCardImagePreview()]\"\n [isShowActions]=\"false\"\n class=\"aspect-square w-24 shrink-0 cursor-pointer self-start\"\n />\n <div class=\"flex w-full flex-col gap-y-1 gap-x-4 @5xl:flex-row @5xl:items-center @5xl:gap-5\">\n <div class=\"flex min-w-0 flex-1 flex-col gap-1\">\n <div\n [tuiHighlight]=\"(search$ | async) ?? ''\"\n class=\"text-tui-text-02\"\n >\n \u0410\u0440\u0442\u0438\u043A\u0443\u043B: {{ product.code }}\n </div>\n <div class=\"flex flex-col gap-1\">\n <a\n tuiLink\n [attr.href]=\"href ?? null\"\n [tuiHighlight]=\"(search$ | async) ?? ''\"\n (click)=\"$event.preventDefault(); clickCardEvent.emit()\"\n [class.disabled]=\"isDisabled\"\n class=\"!text-body-m-bold\"\n >\n {{ product.name }}\n </a>\n @if (product.supplierSku) {\n <span class=\"text-tui-text-02\"> \u0410\u0440\u0442\u0438\u043A\u0443\u043B \u043F\u0440\u043E\u0438\u0437\u0432\u043E\u0434\u0438\u0442\u0435\u043B\u044F: {{ product.supplierSku }} </span>\n }\n @if (product.pack) {\n <div class=\"flex items-center gap-1 text-tui-text-02\">\n \u041D\u043E\u0440\u043C\u0430 \u0443\u043F\u0430\u043A\u043E\u0432\u043A\u0438: {{ product.pack }}\n @if (product.ignoreMinCountCheck) {\n <tui-icon\n icon=\"@tui.package\"\n [tuiHint]=\"minCountHint\"\n [tuiHintShowDelay]=\"100\"\n tuiHintDirection=\"top\"\n class=\"text-body-xl text-tui-text-01 opacity-90\"\n />\n <ng-template #minCountHint>\n \u0414\u043E\u0441\u0442\u0443\u043F\u0435\u043D \u0437\u0430\u043A\u0430\u0437 <br />\n \u043F\u0440\u043E\u0438\u0437\u0432\u043E\u043B\u044C\u043D\u043E\u0433\u043E <br />\n \u043A\u043E\u043B\u0438\u0447\u0435\u0441\u0442\u0432\u0430\n </ng-template>\n }\n </div>\n }\n\n <sc-cost-with-discount\n scPrint=\"cost\"\n [product]=\"product\"\n />\n </div>\n @if (orderItem.specificationImgUrl) {\n <a\n tuiLink\n (click)=\"showSpecification(specificationPreview)\"\n [class.disabled]=\"isDisabled\"\n >\n \u0421\u043F\u0435\u0446\u0438\u0444\u0438\u043A\u0430\u0446\u0438\u044F\n </a>\n }\n <ng-template\n #specificationPreview\n let-preview\n >\n <tui-preview\n [rotatable]=\"false\"\n [zoomable]=\"false\"\n >\n <img\n *polymorpheusOutlet=\"orderItem.specificationImgUrl as src\"\n alt=\"preview\"\n [src]=\"orderItem.specificationImgUrl\"\n />\n <button\n iconStart=\"@tui.x\"\n title=\"Close\"\n tuiIconButton\n tuiPreviewAction\n type=\"button\"\n (click)=\"preview.complete()\"\n ></button>\n </tui-preview>\n </ng-template>\n </div>\n <div class=\"grid grid-cols-1 @lg:grid-cols-3 gap-2 @2xl:gap-4 @2xl:grid-rows-1\">\n <div class=\"grid grid-cols-1 items-center @lg:gap-2 @2xl:grid-cols-2 @lg:col-span-2\">\n <!-- \u041A\u043E\u043B\u0438\u0447\u0435\u0441\u0442\u0432\u043E \u0442\u043E\u0432\u0430\u0440\u0430. -->\n @if (isShowEditControls() && showQuantityControl) {\n <!-- \u041F\u043E\u043B\u0435 \u0432\u0432\u043E\u0434\u0430 \u043A\u043E\u043B\u0438\u0447\u0435\u0441\u0442\u0432\u0430 \u0442\u043E\u0432\u0430\u0440\u0430. -->\n <sc-input-quantity\n #inputQuantity\n [tuiAutoFocus]=\"autoFocuseQuantityInput()\"\n [formControl]=\"quantityControl\"\n size=\"m\"\n [showCross]=\"false\"\n [isDisabled]=\"isDisabled\"\n [step]=\"ScUnitsHelper.getProductMultiplicity(product)\"\n [ignoreStepValidators]=\"product.ignoreMinCountCheck\"\n [showLoader]=\"showLoader()\"\n (clickClearEvent)=\"clickClearEvent.emit(orderItem)\"\n (keydown.enter)=\"inputQuantity.nativeFocusableElement?.blur()\"\n class=\"w-32 shrink-0\"\n />\n } @else {\n <button\n [appearance]=\"isShowEditControls() ? 'action-grayscale' : ''\"\n tuiTheme=\"light\"\n tuiHint=\"\u0414\u043B\u044F \u0438\u0437\u043C\u0435\u043D\u0435\u043D\u0438\u044F \u043A\u043E\u043B\u0438\u0447\u0435\u0441\u0442\u0432\u0430 \u0432\u043E\u0441\u043F\u043E\u043B\u044C\u0437\u0443\u0439\u0442\u0435\u0441\u044C \u043A\u043E\u043D\u0444\u0438\u0433\u0443\u0440\u0430\u0442\u043E\u0440\u043E\u043C\"\n (tuiHintVisible)=\"configuratorHintVisible($event)\"\n tuiLink\n [style.text-decoration-line]=\"isShowEditControls() ? 'underline' : 'none'\"\n [class.pointer-events-none]=\"!isShowEditControls()\"\n >\n <div class=\"!text-body-m text-start\">\n \u041A\u043E\u043B\u0438\u0447\u0435\u0441\u0442\u0432\u043E:\n <strong> {{ orderItem.quantity }} \u0448\u0442.</strong>\n </div>\n </button>\n }\n\n @let showAdditionalInfo = orderItem.configurator || orderItem.height || orderItem.length || orderItem.width || orderItem.marker;\n @if (showAdditionalInfo) {\n <div class=\"flex items-center gap-2 @lg:gap-3 max-w-min\">\n <!-- \u041A\u043D\u043E\u043F\u043A\u0430 \u0440\u0435\u0434\u0430\u043A\u0442\u0438\u0440\u043E\u0432\u0430\u043D\u0438\u044F \u0442\u043E\u0432\u0430\u0440\u0430. -->\n @if (isShowEditControls()) {\n <button\n tuiIconButton\n iconStart=\"@tui.settings\"\n [disabled]=\"isDisabled\"\n [loading]=\"showLoader()\"\n (click)=\"clickSettings.emit()\"\n size=\"s\"\n [appearance]=\"isConfiguratorHintVisible() ? 'primary' : 'secondary'\"\n class=\"shrink-0\"\n ></button>\n }\n\n <!-- \u0414\u043E\u043F\u043E\u043B\u043D\u0438\u0442\u0435\u043B\u044C\u043D\u0430\u044F \u0438\u043D\u0444\u043E\u0440\u043C\u0430\u0446\u0438\u044F \u043E \u0442\u043E\u0432\u0430\u0440\u0435. -->\n <div\n class=\"flex flex-col gap-x-1 text-tui-base-07\"\n [class.text-body-m]=\"!isShowEditControls()\"\n >\n @if (orderItem.marker) {\n <p>\u041C\u0430\u0440\u043A\u0438\u0440\u043E\u0432\u043A\u0430: {{ orderItem.marker }}</p>\n }\n @if (orderItem.width) {\n <button\n [appearance]=\"isShowEditControls() ? 'action-grayscale' : ''\"\n tuiTheme=\"light\"\n tuiHint=\"\u0414\u043B\u044F \u0438\u0437\u043C\u0435\u043D\u0435\u043D\u0438\u044F \u0448\u0438\u0440\u0438\u043D\u044B \u0432\u043E\u0441\u043F\u043E\u043B\u044C\u0437\u0443\u0439\u0442\u0435\u0441\u044C \u043A\u043E\u043D\u0444\u0438\u0433\u0443\u0440\u0430\u0442\u043E\u0440\u043E\u043C\"\n (tuiHintVisible)=\"configuratorHintVisible($event)\"\n tuiLink\n [style.text-decoration-line]=\"isShowEditControls() ? 'underline' : 'none'\"\n [class.pointer-events-none]=\"!isShowEditControls()\"\n class=\"text-body-s self-start whitespace-nowrap\"\n >\n \u0428\u0438\u0440\u0438\u043D\u0430: <span class=\"text-body-s-bold\">{{ orderItem.width }} \u043C.</span>\n </button>\n }\n @if (orderItem.height) {\n <button\n [appearance]=\"isShowEditControls() ? 'action-grayscale' : ''\"\n tuiTheme=\"light\"\n tuiHint=\"\u0414\u043B\u044F \u0438\u0437\u043C\u0435\u043D\u0435\u043D\u0438\u044F \u0432\u044B\u0441\u043E\u0442\u044B \u0432\u043E\u0441\u043F\u043E\u043B\u044C\u0437\u0443\u0439\u0442\u0435\u0441\u044C \u043A\u043E\u043D\u0444\u0438\u0433\u0443\u0440\u0430\u0442\u043E\u0440\u043E\u043C\"\n (tuiHintVisible)=\"configuratorHintVisible($event)\"\n tuiLink\n [style.text-decoration-line]=\"isShowEditControls() ? 'underline' : 'none'\"\n [class.pointer-events-none]=\"!isShowEditControls()\"\n class=\"text-body-s self-start whitespace-nowrap\"\n >\n \u0412\u044B\u0441\u043E\u0442\u0430: <span class=\"text-body-s-bold\">{{ orderItem.height }} \u043C.</span>\n </button>\n } @else if (orderItem.length) {\n <button\n [appearance]=\"isShowEditControls() ? 'action-grayscale' : ''\"\n tuiTheme=\"light\"\n tuiHint=\"\u0414\u043B\u044F \u0438\u0437\u043C\u0435\u043D\u0435\u043D\u0438\u044F \u0434\u043B\u0438\u043D\u044B \u0432\u043E\u0441\u043F\u043E\u043B\u044C\u0437\u0443\u0439\u0442\u0435\u0441\u044C \u043A\u043E\u043D\u0444\u0438\u0433\u0443\u0440\u0430\u0442\u043E\u0440\u043E\u043C\"\n (tuiHintVisible)=\"configuratorHintVisible($event)\"\n tuiLink\n [style.text-decoration-line]=\"isShowEditControls() ? 'underline' : 'none'\"\n [class.pointer-events-none]=\"!isShowEditControls()\"\n class=\"text-body-s self-start whitespace-nowrap\"\n >\n \u0414\u043B\u0438\u043D\u0430: <span class=\"text-body-s-bold\">{{ orderItem.length }} \u043C.</span>\n </button>\n }\n </div>\n </div>\n }\n </div>\n\n <!-- \u0421\u0443\u043C\u043C\u0430 \u0442\u043E\u0432\u0430\u0440\u0430 \u0438 \u0441\u043A\u043B\u0430\u0434\u0441\u043A\u043E\u0435 \u043D\u0430\u043B\u0438\u0447\u0438\u0435. -->\n <div\n scPrint=\"cost\"\n class=\"flex min-w-[11rem] flex-col @lg:justify-end @2xl:!justify-center text-body-m\"\n >\n <p class=\"whitespace-nowrap\">\n \u0421\u0443\u043C\u043C\u0430:\n <strong> {{ orderItem.costRub | tuiFormatNumber: moneyFormat | async }} {{ 'RUB' | tuiCurrency }}</strong>\n </p>\n <sc-price-warehouse-stock\n scPrint=\"defaultNoPrint\"\n [product]=\"product\"\n />\n </div>\n </div>\n </div>\n </sc-noindex-wrapper>\n </div>\n} @else {\n <div class=\"tui-skeleton flex h-[8.25rem] w-full overflow-hidden rounded-xl\"></div>\n}\n" }]
|
|
8966
|
+
], changeDetection: ChangeDetectionStrategy.OnPush, template: "@if (product && orderItem) {\n <div class=\"@container relative flex w-full gap-4 rounded-xl p-1 text-body-s hover:bg-tui-background-neutral-1\">\n <sc-noindex-wrapper>\n @let isDisabled = orderItem.isInactive;\n @let unavailableReason = markUnavailable() ? orderItem.getUnavailableReason() : '';\n <sc-hover-image-carousel\n (click)=\"isDisabled ? null : clickCardEvent.emit()\"\n [images]=\"!isMobile ? getCardImagePreviewList(product) : [getCardImagePreview()]\"\n [isShowActions]=\"false\"\n class=\"aspect-square w-24 shrink-0 cursor-pointer self-start\"\n />\n <div class=\"flex w-full flex-col gap-y-1 gap-x-4 @5xl:flex-row @5xl:items-center @5xl:gap-5\">\n <div class=\"flex min-w-0 flex-1 flex-col gap-1\">\n @if (product.code) {\n <div\n [tuiHighlight]=\"(search$ | async) ?? ''\"\n class=\"text-tui-text-02\"\n >\n \u0410\u0440\u0442\u0438\u043A\u0443\u043B: {{ product.code }}\n </div>\n }\n <div class=\"flex flex-col gap-1\">\n <a\n tuiLink\n [attr.href]=\"href ?? null\"\n [tuiHighlight]=\"(search$ | async) ?? ''\"\n (click)=\"$event.preventDefault(); clickCardEvent.emit()\"\n [class.disabled]=\"isDisabled\"\n [class.!line-through]=\"markUnavailable() && isDisabled\"\n class=\"!text-body-m-bold\"\n >\n {{ product.name }}\n </a>\n @if (unavailableReason) {\n <div class=\"flex flex-wrap items-baseline gap-1\">\n <span class=\"text-tui-text-02\">\u041F\u0440\u0438\u043C\u0435\u0447\u0430\u043D\u0438\u0435:</span>\n <tui-badge\n [appearance]=\"unavailableReasonAppearance\"\n size=\"m\"\n class=\"!h-auto max-w-full !whitespace-normal py-0.5 text-start\"\n >\n {{ unavailableReason }}\n </tui-badge>\n </div>\n }\n @if (product.supplierSku) {\n <span class=\"text-tui-text-02\"> \u0410\u0440\u0442\u0438\u043A\u0443\u043B \u043F\u0440\u043E\u0438\u0437\u0432\u043E\u0434\u0438\u0442\u0435\u043B\u044F: {{ product.supplierSku }} </span>\n }\n @if (product.pack) {\n <div class=\"flex items-center gap-1 text-tui-text-02\">\n \u041D\u043E\u0440\u043C\u0430 \u0443\u043F\u0430\u043A\u043E\u0432\u043A\u0438: {{ product.pack }}\n @if (product.ignoreMinCountCheck) {\n <tui-icon\n icon=\"@tui.package\"\n [tuiHint]=\"minCountHint\"\n [tuiHintShowDelay]=\"100\"\n tuiHintDirection=\"top\"\n class=\"text-body-xl text-tui-text-01 opacity-90\"\n />\n <ng-template #minCountHint>\n \u0414\u043E\u0441\u0442\u0443\u043F\u0435\u043D \u0437\u0430\u043A\u0430\u0437 <br />\n \u043F\u0440\u043E\u0438\u0437\u0432\u043E\u043B\u044C\u043D\u043E\u0433\u043E <br />\n \u043A\u043E\u043B\u0438\u0447\u0435\u0441\u0442\u0432\u0430\n </ng-template>\n }\n </div>\n }\n\n @if (useLinePrice()) {\n @if (orderItem.costOneRub > 0 || orderItem.isOrderable) {\n <span\n scPrint=\"cost\"\n class=\"text-body-m\"\n >\n {{ orderItem.costOneRub > 0 ? orderItem.getCostInRubDescPerItem() : orderItem.getCostMessage() }}\n </span>\n }\n } @else {\n <sc-cost-with-discount\n scPrint=\"cost\"\n [product]=\"product\"\n />\n }\n </div>\n @if (orderItem.specificationImgUrl) {\n <a\n tuiLink\n (click)=\"showSpecification(specificationPreview)\"\n [class.disabled]=\"isDisabled\"\n >\n \u0421\u043F\u0435\u0446\u0438\u0444\u0438\u043A\u0430\u0446\u0438\u044F\n </a>\n }\n <ng-template\n #specificationPreview\n let-preview\n >\n <tui-preview\n [rotatable]=\"false\"\n [zoomable]=\"false\"\n >\n <img\n *polymorpheusOutlet=\"orderItem.specificationImgUrl as src\"\n alt=\"preview\"\n [src]=\"orderItem.specificationImgUrl\"\n />\n <button\n iconStart=\"@tui.x\"\n title=\"Close\"\n tuiIconButton\n tuiPreviewAction\n type=\"button\"\n (click)=\"preview.complete()\"\n ></button>\n </tui-preview>\n </ng-template>\n </div>\n <div class=\"grid grid-cols-1 @lg:grid-cols-3 gap-2 @2xl:gap-4 @2xl:grid-rows-1\">\n <div class=\"grid grid-cols-1 items-center @lg:gap-2 @2xl:grid-cols-2 @lg:col-span-2\">\n <!-- \u041A\u043E\u043B\u0438\u0447\u0435\u0441\u0442\u0432\u043E \u0442\u043E\u0432\u0430\u0440\u0430. -->\n @if (isShowEditControls() && showQuantityControl) {\n <!-- \u041F\u043E\u043B\u0435 \u0432\u0432\u043E\u0434\u0430 \u043A\u043E\u043B\u0438\u0447\u0435\u0441\u0442\u0432\u0430 \u0442\u043E\u0432\u0430\u0440\u0430. -->\n <sc-input-quantity\n #inputQuantity\n [tuiAutoFocus]=\"autoFocuseQuantityInput()\"\n [formControl]=\"quantityControl\"\n size=\"m\"\n [showCross]=\"false\"\n [isDisabled]=\"isDisabled\"\n [attr.inert]=\"isDisabled || null\"\n [step]=\"orderItem.quantityStep\"\n [ignoreStepValidators]=\"product.ignoreMinCountCheck\"\n [showLoader]=\"isPending()\"\n (clickClearEvent)=\"clickClearEvent.emit(orderItem)\"\n (keydown.enter)=\"inputQuantity.nativeFocusableElement?.blur()\"\n class=\"w-32 shrink-0\"\n />\n } @else {\n <button\n [appearance]=\"isShowEditControls() ? 'action-grayscale' : ''\"\n tuiTheme=\"light\"\n [tuiHint]=\"quantityHint\"\n (tuiHintVisible)=\"configuratorHintVisible($event)\"\n tuiLink\n [style.text-decoration-line]=\"isShowEditControls() ? 'underline' : 'none'\"\n [class.pointer-events-none]=\"!isShowEditControls()\"\n >\n <div class=\"!text-body-m text-start\">\n \u041A\u043E\u043B\u0438\u0447\u0435\u0441\u0442\u0432\u043E:\n <strong> {{ orderItem.quantity }} \u0448\u0442.</strong>\n </div>\n </button>\n }\n\n @let showAdditionalInfo = orderItem.configurator || orderItem.height || orderItem.length || orderItem.width || orderItem.marker || settingsAvailable();\n @if (showAdditionalInfo) {\n <div class=\"flex items-center gap-2 @lg:gap-3 max-w-min\">\n <!-- \u041A\u043D\u043E\u043F\u043A\u0430 \u0440\u0435\u0434\u0430\u043A\u0442\u0438\u0440\u043E\u0432\u0430\u043D\u0438\u044F \u0442\u043E\u0432\u0430\u0440\u0430. -->\n @if (isShowEditControls()) {\n <button\n tuiIconButton\n iconStart=\"@tui.settings\"\n [disabled]=\"isDisabled\"\n [loading]=\"isPending()\"\n (click)=\"clickSettings.emit()\"\n size=\"s\"\n [appearance]=\"isConfiguratorHintVisible() ? 'primary' : 'secondary'\"\n class=\"shrink-0\"\n ></button>\n }\n\n <!-- \u0414\u043E\u043F\u043E\u043B\u043D\u0438\u0442\u0435\u043B\u044C\u043D\u0430\u044F \u0438\u043D\u0444\u043E\u0440\u043C\u0430\u0446\u0438\u044F \u043E \u0442\u043E\u0432\u0430\u0440\u0435. -->\n <div\n class=\"flex flex-col gap-x-1 text-tui-base-07\"\n [class.text-body-m]=\"!isShowEditControls()\"\n >\n @if (orderItem.marker) {\n <p>\u041C\u0430\u0440\u043A\u0438\u0440\u043E\u0432\u043A\u0430: {{ orderItem.marker }}</p>\n }\n @if (orderItem.width) {\n <button\n [appearance]=\"isShowEditControls() ? 'action-grayscale' : ''\"\n tuiTheme=\"light\"\n tuiHint=\"\u0414\u043B\u044F \u0438\u0437\u043C\u0435\u043D\u0435\u043D\u0438\u044F \u0448\u0438\u0440\u0438\u043D\u044B \u0432\u043E\u0441\u043F\u043E\u043B\u044C\u0437\u0443\u0439\u0442\u0435\u0441\u044C \u043A\u043E\u043D\u0444\u0438\u0433\u0443\u0440\u0430\u0442\u043E\u0440\u043E\u043C\"\n (tuiHintVisible)=\"configuratorHintVisible($event)\"\n tuiLink\n [style.text-decoration-line]=\"isShowEditControls() ? 'underline' : 'none'\"\n [class.pointer-events-none]=\"!isShowEditControls()\"\n class=\"text-body-s self-start whitespace-nowrap\"\n >\n \u0428\u0438\u0440\u0438\u043D\u0430: <span class=\"text-body-s-bold\">{{ orderItem.width }} \u043C.</span>\n </button>\n }\n @if (orderItem.height) {\n <button\n [appearance]=\"isShowEditControls() ? 'action-grayscale' : ''\"\n tuiTheme=\"light\"\n tuiHint=\"\u0414\u043B\u044F \u0438\u0437\u043C\u0435\u043D\u0435\u043D\u0438\u044F \u0432\u044B\u0441\u043E\u0442\u044B \u0432\u043E\u0441\u043F\u043E\u043B\u044C\u0437\u0443\u0439\u0442\u0435\u0441\u044C \u043A\u043E\u043D\u0444\u0438\u0433\u0443\u0440\u0430\u0442\u043E\u0440\u043E\u043C\"\n (tuiHintVisible)=\"configuratorHintVisible($event)\"\n tuiLink\n [style.text-decoration-line]=\"isShowEditControls() ? 'underline' : 'none'\"\n [class.pointer-events-none]=\"!isShowEditControls()\"\n class=\"text-body-s self-start whitespace-nowrap\"\n >\n \u0412\u044B\u0441\u043E\u0442\u0430: <span class=\"text-body-s-bold\">{{ orderItem.height }} \u043C.</span>\n </button>\n } @else if (orderItem.length) {\n <button\n [appearance]=\"isShowEditControls() ? 'action-grayscale' : ''\"\n tuiTheme=\"light\"\n tuiHint=\"\u0414\u043B\u044F \u0438\u0437\u043C\u0435\u043D\u0435\u043D\u0438\u044F \u0434\u043B\u0438\u043D\u044B \u0432\u043E\u0441\u043F\u043E\u043B\u044C\u0437\u0443\u0439\u0442\u0435\u0441\u044C \u043A\u043E\u043D\u0444\u0438\u0433\u0443\u0440\u0430\u0442\u043E\u0440\u043E\u043C\"\n (tuiHintVisible)=\"configuratorHintVisible($event)\"\n tuiLink\n [style.text-decoration-line]=\"isShowEditControls() ? 'underline' : 'none'\"\n [class.pointer-events-none]=\"!isShowEditControls()\"\n class=\"text-body-s self-start whitespace-nowrap\"\n >\n \u0414\u043B\u0438\u043D\u0430: <span class=\"text-body-s-bold\">{{ orderItem.length }} \u043C.</span>\n </button>\n }\n </div>\n </div>\n }\n </div>\n\n <!-- \u0421\u0443\u043C\u043C\u0430 \u0442\u043E\u0432\u0430\u0440\u0430 \u0438 \u0441\u043A\u043B\u0430\u0434\u0441\u043A\u043E\u0435 \u043D\u0430\u043B\u0438\u0447\u0438\u0435. -->\n <div\n scPrint=\"cost\"\n class=\"flex min-w-[11rem] flex-col @lg:justify-end @2xl:!justify-center text-body-m\"\n >\n <p class=\"whitespace-nowrap\">\n \u0421\u0443\u043C\u043C\u0430:\n <strong> {{ orderItem.costRub | tuiFormatNumber: moneyFormat | async }} {{ 'RUB' | tuiCurrency }}</strong>\n </p>\n @if (!(markUnavailable() && product.isNull)) {\n <sc-price-warehouse-stock\n scPrint=\"defaultNoPrint\"\n [product]=\"product\"\n />\n }\n </div>\n </div>\n </div>\n </sc-noindex-wrapper>\n </div>\n} @else {\n <div class=\"tui-skeleton flex h-[8.25rem] w-full overflow-hidden rounded-xl\"></div>\n}\n" }]
|
|
8876
8967
|
}] });
|
|
8877
8968
|
|
|
8878
8969
|
/* eslint-disable no-param-reassign */
|
|
@@ -8880,6 +8971,29 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
|
|
|
8880
8971
|
* Компонент списка товаров корзины, заказа или черновика.
|
|
8881
8972
|
*/
|
|
8882
8973
|
class ScOrderItemsListComponent {
|
|
8974
|
+
/**
|
|
8975
|
+
* Возвращает копию набора с добавленными или убранными ключами.
|
|
8976
|
+
*
|
|
8977
|
+
* @param keys Набор.
|
|
8978
|
+
* @param changed Ключи.
|
|
8979
|
+
* @param isIncluded Признак добавления.
|
|
8980
|
+
*/
|
|
8981
|
+
static withKeys(keys, changed, isIncluded) {
|
|
8982
|
+
const next = new Set(keys);
|
|
8983
|
+
changed.forEach((key) => {
|
|
8984
|
+
if (isIncluded) {
|
|
8985
|
+
next.add(key);
|
|
8986
|
+
}
|
|
8987
|
+
else {
|
|
8988
|
+
next.delete(key);
|
|
8989
|
+
}
|
|
8990
|
+
});
|
|
8991
|
+
return next;
|
|
8992
|
+
}
|
|
8993
|
+
/**
|
|
8994
|
+
* Инициализирует экземпляр класса {@link ScOrderItemsListComponent}.
|
|
8995
|
+
* Входы выбора и пометки недоступных позиций могут прийти после списка позиций, поэтому при их изменении выбор синхронизируется заново.
|
|
8996
|
+
*/
|
|
8883
8997
|
constructor() {
|
|
8884
8998
|
/**
|
|
8885
8999
|
* Внутренний сигнал списка элементов корзины (товаров).
|
|
@@ -8893,6 +9007,56 @@ class ScOrderItemsListComponent {
|
|
|
8893
9007
|
* Включить редактирование позиций.
|
|
8894
9008
|
*/
|
|
8895
9009
|
this.editable = input(false);
|
|
9010
|
+
/**
|
|
9011
|
+
* Признак вывода позиций по группам (группа позиции или «Без группы») с заголовками и итогами. Без признака позиции выводятся одним списком без заголовков.
|
|
9012
|
+
*/
|
|
9013
|
+
this.grouped = input(false);
|
|
9014
|
+
/**
|
|
9015
|
+
* Признак, что позиции, которые не попадут в заказ, помечаются примечанием и не выбираются. Неактивные позиции приглушаются и зачёркиваются.
|
|
9016
|
+
*/
|
|
9017
|
+
this.markUnavailable = input(false);
|
|
9018
|
+
/**
|
|
9019
|
+
* Признак, что цена за единицу берётся из позиции, а не из текущей цены товара.
|
|
9020
|
+
*/
|
|
9021
|
+
this.useLinePrice = input(false);
|
|
9022
|
+
/**
|
|
9023
|
+
* Признак, что параметры позиции меняются в диалоге товара: диалог доступен у любой активной позиции, а количество и метраж измеряемых товаров
|
|
9024
|
+
* меняются только в нём, даже если метраж ещё не задан.
|
|
9025
|
+
*/
|
|
9026
|
+
this.measuredInDialog = input(false);
|
|
9027
|
+
/**
|
|
9028
|
+
* Откуда удаляется позиция для текста подтверждения, например «из черновика». Если не задано, позиция удаляется без подтверждения.
|
|
9029
|
+
* Тот же текст используется в предупреждении о нулевом количестве.
|
|
9030
|
+
*/
|
|
9031
|
+
this.deleteConfirmationTarget = input(null);
|
|
9032
|
+
/**
|
|
9033
|
+
* Подпись итоговой суммы.
|
|
9034
|
+
*/
|
|
9035
|
+
this.totalLabel = input('Итого');
|
|
9036
|
+
/**
|
|
9037
|
+
* Позиции, разбитые на группы. Без группировки все позиции идут одной группой без заголовка.
|
|
9038
|
+
*/
|
|
9039
|
+
this.groups = computed(() => {
|
|
9040
|
+
const items = this.itemsSignal();
|
|
9041
|
+
return this.grouped() ? ScOrderItemsGroup.fromItems(items) : [new ScOrderItemsGroup('', items)];
|
|
9042
|
+
});
|
|
9043
|
+
/**
|
|
9044
|
+
* Идентификаторы позиций, изменение или удаление которых ждёт очереди или выполняется.
|
|
9045
|
+
*/
|
|
9046
|
+
this.pendingItemsIds = signal(new Set());
|
|
9047
|
+
/**
|
|
9048
|
+
* Названия удаляемых групп.
|
|
9049
|
+
*/
|
|
9050
|
+
this.deletingGroupsNames = signal(new Set());
|
|
9051
|
+
/**
|
|
9052
|
+
* Названия групп с позициями, ждущими ответа: такие группы удалить нельзя.
|
|
9053
|
+
*/
|
|
9054
|
+
this.pendingGroupsNames = computed(() => {
|
|
9055
|
+
const pendingIds = this.pendingItemsIds();
|
|
9056
|
+
return new Set(this.groups()
|
|
9057
|
+
.filter((group) => group.items.some((item) => pendingIds.has(item.id)))
|
|
9058
|
+
.map((group) => group.name));
|
|
9059
|
+
});
|
|
8896
9060
|
/**
|
|
8897
9061
|
* Внутренняя группа формы для выбора позиций.
|
|
8898
9062
|
*/
|
|
@@ -8908,11 +9072,12 @@ class ScOrderItemsListComponent {
|
|
|
8908
9072
|
initialValue: [],
|
|
8909
9073
|
});
|
|
8910
9074
|
/**
|
|
8911
|
-
* Состояние флага «Выбрать все»: `true`, если выбраны все позиции, `false`, если ни
|
|
9075
|
+
* Состояние флага «Выбрать все»: `true`, если выбраны все доступные для выбора позиции, `false`, если ни одной или выбирать нечего,
|
|
9076
|
+
* и `null`, если только часть.
|
|
8912
9077
|
*/
|
|
8913
9078
|
this.allItemsSelected = computed(() => {
|
|
8914
9079
|
const checkedList = this.itemsIds().map(([, checked]) => checked);
|
|
8915
|
-
if (checkedList.every(Boolean)) {
|
|
9080
|
+
if (checkedList.length > 0 && checkedList.every(Boolean)) {
|
|
8916
9081
|
return true;
|
|
8917
9082
|
}
|
|
8918
9083
|
return checkedList.some(Boolean) ? null : false;
|
|
@@ -8937,7 +9102,7 @@ class ScOrderItemsListComponent {
|
|
|
8937
9102
|
const items = this.itemsSignal();
|
|
8938
9103
|
const selectedItemsIds = this.selectedItemsIds();
|
|
8939
9104
|
const selectable = this.selectable();
|
|
8940
|
-
return
|
|
9105
|
+
return ScOrderItem.getCostSum(items.filter((item) => !selectable || selectedItemsIds.includes(item.id)));
|
|
8941
9106
|
});
|
|
8942
9107
|
/**
|
|
8943
9108
|
* Сервис получения и редактирования списка товаров заказа (корзина / заказ / черновик = заказ).
|
|
@@ -8948,6 +9113,10 @@ class ScOrderItemsListComponent {
|
|
|
8948
9113
|
* Признак, что можно редактировать позиции.
|
|
8949
9114
|
*/
|
|
8950
9115
|
this.canEdit = computed(() => this.editable() && 'updateProduct$' in this.orderItemsListLoader);
|
|
9116
|
+
/**
|
|
9117
|
+
* Признак, что группу можно удалить целиком.
|
|
9118
|
+
*/
|
|
9119
|
+
this.canDeleteGroups = computed(() => this.canEdit() && 'deleteProducts$' in this.orderItemsListLoader);
|
|
8951
9120
|
/**
|
|
8952
9121
|
* Настройки вывода сумм в рублях.
|
|
8953
9122
|
*/
|
|
@@ -8961,6 +9130,21 @@ class ScOrderItemsListComponent {
|
|
|
8961
9130
|
* Предоставляется родителем через токен {@link SC_DIALOG_SERVICE_TOKEN}.
|
|
8962
9131
|
*/
|
|
8963
9132
|
this.scDialogService = inject(SC_DIALOG_SERVICE_TOKEN);
|
|
9133
|
+
/**
|
|
9134
|
+
* Сервис диалоговых окон Taiga для подтверждений.
|
|
9135
|
+
*/
|
|
9136
|
+
this.dialogService = inject(TuiDialogService);
|
|
9137
|
+
/**
|
|
9138
|
+
* Сервис уведомлений.
|
|
9139
|
+
*/
|
|
9140
|
+
this.alerts = inject(TuiAlertService);
|
|
9141
|
+
effect(() => {
|
|
9142
|
+
this.markUnavailable();
|
|
9143
|
+
this.selectable();
|
|
9144
|
+
untracked(() => {
|
|
9145
|
+
this.syncSelectionControls();
|
|
9146
|
+
});
|
|
9147
|
+
});
|
|
8964
9148
|
}
|
|
8965
9149
|
/**
|
|
8966
9150
|
* Список элементов корзины (товаров). При установке синхронно обновляет контролы выбора в форме.
|
|
@@ -8971,6 +9155,7 @@ class ScOrderItemsListComponent {
|
|
|
8971
9155
|
}
|
|
8972
9156
|
/**
|
|
8973
9157
|
* Синхронизирует контролы формы выбора с текущим списком товаров: удаляет контролы для отсутствующих id, добавляет для новых.
|
|
9158
|
+
* Контролы меняются без событий, а значение группы пересчитывается один раз в конце: на заявках в сотни позиций это заметно.
|
|
8974
9159
|
*/
|
|
8975
9160
|
syncSelectionControls() {
|
|
8976
9161
|
if (!this.selectable()) {
|
|
@@ -8980,70 +9165,125 @@ class ScOrderItemsListComponent {
|
|
|
8980
9165
|
const currentIds = new Set(currentItems.map((item) => item.id));
|
|
8981
9166
|
Object.keys(this.selectionFormGroup.controls).forEach((name) => {
|
|
8982
9167
|
if (!currentIds.has(name)) {
|
|
8983
|
-
this.selectionFormGroup.removeControl(name);
|
|
9168
|
+
this.selectionFormGroup.removeControl(name, { emitEvent: false });
|
|
8984
9169
|
}
|
|
8985
9170
|
});
|
|
8986
9171
|
currentItems.forEach((item) => {
|
|
8987
|
-
|
|
8988
|
-
|
|
9172
|
+
const control = this.selectionFormGroup.get(item.id);
|
|
9173
|
+
const isSelectable = !this.markUnavailable() || item.isOrderable;
|
|
9174
|
+
if (!control) {
|
|
9175
|
+
this.selectionFormGroup.addControl(item.id, new FormControl({ value: isSelectable, disabled: !isSelectable }, { nonNullable: true }), { emitEvent: false });
|
|
9176
|
+
}
|
|
9177
|
+
else if (!isSelectable && control.enabled) {
|
|
9178
|
+
control.setValue(false, { emitEvent: false });
|
|
9179
|
+
control.disable({ emitEvent: false });
|
|
9180
|
+
}
|
|
9181
|
+
else if (isSelectable && control.disabled) {
|
|
9182
|
+
control.enable({ emitEvent: false });
|
|
9183
|
+
control.setValue(true, { emitEvent: false });
|
|
8989
9184
|
}
|
|
8990
9185
|
});
|
|
9186
|
+
this.selectionFormGroup.updateValueAndValidity();
|
|
8991
9187
|
}
|
|
8992
9188
|
/**
|
|
8993
|
-
* Выбирает все позиции или снимает выбор со всех.
|
|
9189
|
+
* Выбирает все доступные для выбора позиции или снимает выбор со всех.
|
|
8994
9190
|
*
|
|
8995
9191
|
* @param event Событие изменения флага выбора всех позиций.
|
|
8996
9192
|
*/
|
|
8997
9193
|
selectAllChangeHandler(event) {
|
|
8998
9194
|
const { checked } = event.target;
|
|
8999
|
-
|
|
9195
|
+
const enabledIds = Object.entries(this.selectionFormGroup.controls)
|
|
9196
|
+
.filter(([, control]) => control.enabled)
|
|
9197
|
+
.map(([id]) => id);
|
|
9198
|
+
this.selectionFormGroup.patchValue(Object.fromEntries(enabledIds.map((id) => [id, checked])));
|
|
9199
|
+
}
|
|
9200
|
+
/**
|
|
9201
|
+
* Возвращает признак, что позиция приглушена: она неактивна или не выбрана для заказа.
|
|
9202
|
+
*
|
|
9203
|
+
* @param item Позиция.
|
|
9204
|
+
*/
|
|
9205
|
+
isItemDimmed(item) {
|
|
9206
|
+
const isUnselected = this.selectable() && !this.selectionFormGroup.get(item.id)?.value;
|
|
9207
|
+
return this.markUnavailable() ? item.isInactive || (item.isOrderable && isUnselected) : isUnselected;
|
|
9208
|
+
}
|
|
9209
|
+
/**
|
|
9210
|
+
* Возвращает признак, что количество и метраж товара позиции меняются в диалоге товара.
|
|
9211
|
+
*
|
|
9212
|
+
* @param item Позиция.
|
|
9213
|
+
*/
|
|
9214
|
+
isEditedInDialog(item) {
|
|
9215
|
+
return this.measuredInDialog() && ScUnitsHelper.productIsMeasurable(item.product);
|
|
9000
9216
|
}
|
|
9001
9217
|
/**
|
|
9002
|
-
* Удаляет
|
|
9218
|
+
* Удаляет позицию, при необходимости после подтверждения. Сообщение об ошибке показывает перехватчик приложения.
|
|
9003
9219
|
*
|
|
9004
9220
|
* @param item Позиция товара для удаления.
|
|
9005
|
-
* @param priceCard Компонент карточки товара.
|
|
9006
9221
|
*/
|
|
9007
|
-
deleteItem(item
|
|
9008
|
-
if (!this.canEdit()) {
|
|
9222
|
+
deleteItem(item) {
|
|
9223
|
+
if (!this.canEdit() || this.pendingItemsIds().has(item.id)) {
|
|
9009
9224
|
return;
|
|
9010
9225
|
}
|
|
9011
|
-
|
|
9012
|
-
|
|
9013
|
-
.
|
|
9014
|
-
.pipe(
|
|
9015
|
-
|
|
9016
|
-
|
|
9017
|
-
});
|
|
9018
|
-
return of();
|
|
9019
|
-
}), finalize(() => {
|
|
9020
|
-
priceCard.showLoader.set(false);
|
|
9226
|
+
this.confirmDelete$(item)
|
|
9227
|
+
.pipe(switchMap(() => {
|
|
9228
|
+
this.setItemPending(item.id, true);
|
|
9229
|
+
return this.orderItemsListLoader.deleteProduct$(item, this.orderOptions?.orderId()).pipe(finalize(() => {
|
|
9230
|
+
this.setItemPending(item.id, false);
|
|
9231
|
+
}));
|
|
9021
9232
|
}), takeUntilDestroyed(this.destroyRef))
|
|
9022
|
-
.subscribe();
|
|
9233
|
+
.subscribe({ error: noop });
|
|
9234
|
+
}
|
|
9235
|
+
/**
|
|
9236
|
+
* Удаляет все позиции группы после подтверждения. Состав берётся на момент подтверждения.
|
|
9237
|
+
*
|
|
9238
|
+
* @param group Группа позиций.
|
|
9239
|
+
*/
|
|
9240
|
+
deleteGroup(group) {
|
|
9241
|
+
if (!this.canDeleteGroups() || this.pendingGroupsNames().has(group.name)) {
|
|
9242
|
+
return;
|
|
9243
|
+
}
|
|
9244
|
+
this.confirmGroupDelete$(group)
|
|
9245
|
+
.pipe(switchMap(() => {
|
|
9246
|
+
const items = this.groups().find(({ name }) => name === group.name)?.items ?? [];
|
|
9247
|
+
const ids = items.map((item) => item.id);
|
|
9248
|
+
if (ids.length === 0) {
|
|
9249
|
+
return EMPTY;
|
|
9250
|
+
}
|
|
9251
|
+
this.setItemsPending(ids, true);
|
|
9252
|
+
this.deletingGroupsNames.update((names) => ScOrderItemsListComponent.withKeys(names, [group.name], true));
|
|
9253
|
+
return this.orderItemsListLoader.deleteProducts$(items, this.orderOptions?.orderId()).pipe(finalize(() => {
|
|
9254
|
+
this.setItemsPending(ids, false);
|
|
9255
|
+
this.deletingGroupsNames.update((names) => ScOrderItemsListComponent.withKeys(names, [group.name], false));
|
|
9256
|
+
}));
|
|
9257
|
+
}), takeUntilDestroyed(this.destroyRef))
|
|
9258
|
+
.subscribe({ error: noop });
|
|
9023
9259
|
}
|
|
9024
9260
|
/**
|
|
9025
|
-
* Обновляет количество товара.
|
|
9261
|
+
* Обновляет количество товара. Количество неактивной позиции не меняется. Пустое или нулевое значение возвращается к сохранённому количеству.
|
|
9026
9262
|
*
|
|
9027
9263
|
* @param newQuantity Новое значение количества товара.
|
|
9028
9264
|
* @param priceCard Компонент карточки товара.
|
|
9029
9265
|
*/
|
|
9030
9266
|
patchItem(newQuantity, priceCard) {
|
|
9031
|
-
|
|
9267
|
+
const { orderItem } = priceCard;
|
|
9268
|
+
if (!this.canEdit() || !orderItem || !priceCard.product || orderItem.isInactive) {
|
|
9269
|
+
return;
|
|
9270
|
+
}
|
|
9271
|
+
if (!newQuantity) {
|
|
9272
|
+
priceCard.quantityControl.patchValue(orderItem.quantity, { emitEvent: false });
|
|
9273
|
+
this.warnEmptyQuantity();
|
|
9032
9274
|
return;
|
|
9033
9275
|
}
|
|
9034
|
-
|
|
9276
|
+
this.setItemPending(orderItem.id, true);
|
|
9035
9277
|
this.orderItemsListLoader
|
|
9036
|
-
.updateProduct$(
|
|
9037
|
-
.pipe(
|
|
9038
|
-
|
|
9039
|
-
priceCard.quantityControl.patchValue(priceCard.orderItem.quantity, {
|
|
9040
|
-
emitEvent: false,
|
|
9041
|
-
});
|
|
9042
|
-
return of();
|
|
9043
|
-
}), finalize(() => {
|
|
9044
|
-
priceCard.showLoader.set(false);
|
|
9278
|
+
.updateProduct$(orderItem.id, { quantity: newQuantity }, this.orderOptions?.orderId())
|
|
9279
|
+
.pipe(finalize(() => {
|
|
9280
|
+
this.setItemPending(orderItem.id, false);
|
|
9045
9281
|
}), takeUntilDestroyed(this.destroyRef))
|
|
9046
|
-
.subscribe(
|
|
9282
|
+
.subscribe({
|
|
9283
|
+
error: () => {
|
|
9284
|
+
priceCard.quantityControl.patchValue((priceCard.orderItem ?? orderItem).quantity, { emitEvent: false });
|
|
9285
|
+
},
|
|
9286
|
+
});
|
|
9047
9287
|
}
|
|
9048
9288
|
/**
|
|
9049
9289
|
* Открывает модальное окно описания товара.
|
|
@@ -9058,7 +9298,7 @@ class ScOrderItemsListComponent {
|
|
|
9058
9298
|
}
|
|
9059
9299
|
}
|
|
9060
9300
|
/**
|
|
9061
|
-
* Открывает модальное окно изменения товара или его конфигуратора.
|
|
9301
|
+
* Открывает модальное окно изменения товара или его конфигуратора. Если параметры позиции меняются в диалоге, окно товара открывается и у штучного товара.
|
|
9062
9302
|
*
|
|
9063
9303
|
* @param item Позиция товара.
|
|
9064
9304
|
*/
|
|
@@ -9067,7 +9307,7 @@ class ScOrderItemsListComponent {
|
|
|
9067
9307
|
if (isConfigurator) {
|
|
9068
9308
|
this.scDialogService.openConfiguratorDialog(item.product, item, this.orderOptions?.orderId());
|
|
9069
9309
|
}
|
|
9070
|
-
else if (ScUnitsHelper.productIsMeasurable(item.product)) {
|
|
9310
|
+
else if (this.measuredInDialog() || ScUnitsHelper.productIsMeasurable(item.product)) {
|
|
9071
9311
|
this.scDialogService.openAddOrEditingCartItemDialog(item.product, item, this.orderOptions?.orderId(), true);
|
|
9072
9312
|
}
|
|
9073
9313
|
}
|
|
@@ -9079,8 +9319,66 @@ class ScOrderItemsListComponent {
|
|
|
9079
9319
|
openPriceHistoryChartDialog(product) {
|
|
9080
9320
|
this.scDialogService.openPriceHistoryChartDialog(product);
|
|
9081
9321
|
}
|
|
9322
|
+
/**
|
|
9323
|
+
* Возвращает {@link Observable}, который выдаёт значение, когда удаление позиции подтверждено или подтверждение не требуется.
|
|
9324
|
+
*
|
|
9325
|
+
* @param item Удаляемая позиция.
|
|
9326
|
+
*/
|
|
9327
|
+
confirmDelete$(item) {
|
|
9328
|
+
const target = this.deleteConfirmationTarget();
|
|
9329
|
+
if (!target) {
|
|
9330
|
+
return of(true);
|
|
9331
|
+
}
|
|
9332
|
+
return this.confirmRemoval$(`Удалить позицию «${item.product.name}» ${target}?`);
|
|
9333
|
+
}
|
|
9334
|
+
/**
|
|
9335
|
+
* Возвращает {@link Observable}, который выдаёт значение, когда удаление группы подтверждено.
|
|
9336
|
+
*
|
|
9337
|
+
* @param group Группа позиций.
|
|
9338
|
+
*/
|
|
9339
|
+
confirmGroupDelete$(group) {
|
|
9340
|
+
const target = this.deleteConfirmationTarget();
|
|
9341
|
+
const source = target ? ` ${target}` : '';
|
|
9342
|
+
return this.confirmRemoval$(`Удалить все позиции группы «${group.name}»${source}?`);
|
|
9343
|
+
}
|
|
9344
|
+
/**
|
|
9345
|
+
* Возвращает {@link Observable}, который выдаёт значение, когда удаление подтверждено.
|
|
9346
|
+
*
|
|
9347
|
+
* @param label Вопрос.
|
|
9348
|
+
*/
|
|
9349
|
+
confirmRemoval$(label) {
|
|
9350
|
+
return this.dialogService.open(TUI_CONFIRM, { label, data: { yes: 'Да, удалить', no: 'Нет, оставить' } }).pipe(filter(Boolean));
|
|
9351
|
+
}
|
|
9352
|
+
/**
|
|
9353
|
+
* Отмечает позицию как ожидающую ответа или снимает отметку.
|
|
9354
|
+
*
|
|
9355
|
+
* @param id Идентификатор позиции.
|
|
9356
|
+
* @param isPending Признак ожидания.
|
|
9357
|
+
*/
|
|
9358
|
+
setItemPending(id, isPending) {
|
|
9359
|
+
this.setItemsPending([id], isPending);
|
|
9360
|
+
}
|
|
9361
|
+
/**
|
|
9362
|
+
* Ставит или снимает отметку ожидания у позиций.
|
|
9363
|
+
*
|
|
9364
|
+
* @param itemsIds Идентификаторы позиций.
|
|
9365
|
+
* @param isPending Признак ожидания.
|
|
9366
|
+
*/
|
|
9367
|
+
setItemsPending(itemsIds, isPending) {
|
|
9368
|
+
this.pendingItemsIds.update((ids) => ScOrderItemsListComponent.withKeys(ids, itemsIds, isPending));
|
|
9369
|
+
}
|
|
9370
|
+
/**
|
|
9371
|
+
* Предупреждает, что количество не может быть пустым или нулевым, а убрать позицию можно удалением. Поле возвращается к сохранённому количеству
|
|
9372
|
+
* в любом списке, предупреждение показывается только в списках с подтверждением удаления.
|
|
9373
|
+
*/
|
|
9374
|
+
warnEmptyQuantity() {
|
|
9375
|
+
const target = this.deleteConfirmationTarget();
|
|
9376
|
+
if (target) {
|
|
9377
|
+
this.alerts.open(`Количество должно быть больше нуля. Чтобы убрать позицию ${target}, удалите её.`, { appearance: 'warning' }).subscribe();
|
|
9378
|
+
}
|
|
9379
|
+
}
|
|
9082
9380
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: ScOrderItemsListComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
9083
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.14", type: ScOrderItemsListComponent, isStandalone: true, selector: "sc-order-items-list", inputs: { selectable: { classPropertyName: "selectable", publicName: "selectable", isSignal: true, isRequired: false, transformFunction: null }, editable: { classPropertyName: "editable", publicName: "editable", isSignal: true, isRequired: false, transformFunction: null }, items: { classPropertyName: "items", publicName: "items", isSignal: false, isRequired: true, transformFunction: null } }, outputs: { selectedItemsIdsChange: "selectedItemsIdsChange" }, ngImport: i0, template: "<div\n [formGroup]=\"selectionFormGroup\"\n class=\"flex flex-col gap-5\"\n>\n <!-- \u0424\u043B\u0430\u0433 \u0432\u044B\u0431\u043E\u0440\u0430 \u0432\u0441\u0435\u0445 \u043F\u043E\u0437\u0438\u0446\u0438\u0439. -->\n @if (selectable() && itemsSignal().length) {\n <label\n tuiLabel\n scPrint=\"defaultNoPrint\"\n class=\"self-start\"\n >\n <input\n tuiCheckbox\n type=\"checkbox\"\n size=\"m\"\n [ngModel]=\"allItemsSelected()\"\n [ngModelOptions]=\"{ standalone: true }\"\n (change)=\"selectAllChangeHandler($event)\"\n />\n \u0412\u044B\u0431\u0440\u0430\u0442\u044C \u0432\u0441\u0435\n </label>\n }\n\n @for (
|
|
9381
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.14", type: ScOrderItemsListComponent, isStandalone: true, selector: "sc-order-items-list", inputs: { selectable: { classPropertyName: "selectable", publicName: "selectable", isSignal: true, isRequired: false, transformFunction: null }, editable: { classPropertyName: "editable", publicName: "editable", isSignal: true, isRequired: false, transformFunction: null }, grouped: { classPropertyName: "grouped", publicName: "grouped", isSignal: true, isRequired: false, transformFunction: null }, markUnavailable: { classPropertyName: "markUnavailable", publicName: "markUnavailable", isSignal: true, isRequired: false, transformFunction: null }, useLinePrice: { classPropertyName: "useLinePrice", publicName: "useLinePrice", isSignal: true, isRequired: false, transformFunction: null }, measuredInDialog: { classPropertyName: "measuredInDialog", publicName: "measuredInDialog", isSignal: true, isRequired: false, transformFunction: null }, deleteConfirmationTarget: { classPropertyName: "deleteConfirmationTarget", publicName: "deleteConfirmationTarget", isSignal: true, isRequired: false, transformFunction: null }, totalLabel: { classPropertyName: "totalLabel", publicName: "totalLabel", isSignal: true, isRequired: false, transformFunction: null }, items: { classPropertyName: "items", publicName: "items", isSignal: false, isRequired: true, transformFunction: null } }, outputs: { selectedItemsIdsChange: "selectedItemsIdsChange" }, ngImport: i0, template: "<div\n [formGroup]=\"selectionFormGroup\"\n class=\"flex flex-col gap-5\"\n>\n <!-- \u0424\u043B\u0430\u0433 \u0432\u044B\u0431\u043E\u0440\u0430 \u0432\u0441\u0435\u0445 \u043F\u043E\u0437\u0438\u0446\u0438\u0439. -->\n @if (selectable() && itemsSignal().length) {\n <label\n tuiLabel\n scPrint=\"defaultNoPrint\"\n class=\"self-start\"\n >\n <input\n tuiCheckbox\n type=\"checkbox\"\n size=\"m\"\n [ngModel]=\"allItemsSelected()\"\n [ngModelOptions]=\"{ standalone: true }\"\n [disabled]=\"selectionFormGroup.disabled\"\n (change)=\"selectAllChangeHandler($event)\"\n />\n \u0412\u044B\u0431\u0440\u0430\u0442\u044C \u0432\u0441\u0435\n </label>\n }\n\n @for (group of groups(); track group.name) {\n <!-- \u0417\u0430\u0433\u043E\u043B\u043E\u0432\u043E\u043A \u0433\u0440\u0443\u043F\u043F\u044B \u043F\u043E\u0437\u0438\u0446\u0438\u0439. -->\n @if (group.name) {\n <div class=\"flex flex-wrap items-end justify-between gap-x-4 gap-y-1 border-b border-tui-base-03 pb-2\">\n <div class=\"flex min-w-0 flex-col\">\n <span class=\"break-words text-body-l-bold\">{{ group.name }}</span>\n <span class=\"text-body-s text-tui-text-02\">\u043F\u043E\u0437\u0438\u0446\u0438\u0439: {{ group.items.length }}, \u0432 \u0437\u0430\u043A\u0430\u0437: {{ group.orderableCount }}</span>\n </div>\n <div class=\"flex flex-col items-end gap-1\">\n <!-- \u041A\u043D\u043E\u043F\u043A\u0430 \u0443\u0434\u0430\u043B\u0435\u043D\u0438\u044F \u0433\u0440\u0443\u043F\u043F\u044B. -->\n @if (canDeleteGroups()) {\n <button\n tuiButton\n type=\"button\"\n appearance=\"flat\"\n size=\"xs\"\n scPrint=\"defaultNoPrint\"\n [loading]=\"deletingGroupsNames().has(group.name)\"\n [disabled]=\"pendingGroupsNames().has(group.name)\"\n (click)=\"deleteGroup(group)\"\n >\n \u0423\u0434\u0430\u043B\u0438\u0442\u044C\n </button>\n }\n <span class=\"whitespace-nowrap text-body-m-bold\">{{ group.orderableSum | tuiFormatNumber: moneyFormat | async }} {{ 'RUB' | tuiCurrency }}</span>\n </div>\n </div>\n }\n\n @for (item of group.items; track item.id) {\n @if ($index !== 0) {\n <hr class=\"mt-auto h-px w-full bg-tui-clear\" />\n }\n\n <div class=\"flex items-center gap-4 relative\">\n @let isActive = !selectable() || selectionFormGroup.get(item.id)?.value || (markUnavailable() && !item.isOrderable);\n @let isPending = pendingItemsIds().has(item.id);\n @let editedInDialog = isEditedInDialog(item);\n\n <!-- \u0427\u0435\u043A\u0431\u043E\u043A\u0441 \u0432\u044B\u0431\u043E\u0440\u0430 \u0442\u043E\u0432\u0430\u0440\u0430. -->\n @if (selectable()) {\n <input\n tuiCheckbox\n type=\"checkbox\"\n size=\"m\"\n [formControlName]=\"item.id\"\n />\n }\n\n <!-- \u041A\u0430\u0440\u0442\u043E\u0447\u043A\u0430 \u0442\u043E\u0432\u0430\u0440\u0430. -->\n <sc-order-item\n #itemComponent\n [product]=\"item.product\"\n [orderItem]=\"item\"\n [showQuantityControl]=\"canEdit() && !item.configurator && !editedInDialog\"\n [isShowEditControls]=\"canEdit()\"\n [useLinePrice]=\"useLinePrice()\"\n [markUnavailable]=\"markUnavailable()\"\n [isPending]=\"isPending\"\n [settingsAvailable]=\"canEdit() && measuredInDialog() && !item.configurator && !item.isInactive\"\n class=\"w-full duration-200\"\n [class.opacity-60]=\"isItemDimmed(item)\"\n (clickCardEvent)=\"openProductInfoDialog(item.product, item, orderOptions?.orderId())\"\n (quantityValueChanges)=\"patchItem($event, itemComponent)\"\n (clickPriceHistoryEvent)=\"openPriceHistoryChartDialog(item.product)\"\n (clickSettings)=\"openSettingsDialog(item)\"\n [class.pointer-events-none]=\"!isActive\"\n />\n\n <!-- \u041A\u043D\u043E\u043F\u043A\u0430 \u0443\u0434\u0430\u043B\u0435\u043D\u0438\u044F \u0442\u043E\u0432\u0430\u0440\u0430. -->\n @if (canEdit()) {\n <button\n tuiIconButton\n iconStart=\"@tui.trash\"\n (click)=\"deleteItem(item)\"\n [loading]=\"isPending\"\n size=\"s\"\n appearance=\"secondary\"\n class=\"!absolute left-1 top-1 lg:!relative lg:left-0 lg:top-0 lg:!self-center\"\n ></button>\n }\n </div>\n }\n }\n\n <!-- \u0424\u0443\u0442\u0435\u0440 \u0441\u043F\u0438\u0441\u043A\u0430. -->\n <div\n scPrint=\"cost\"\n class=\"flex flex-col items-end gap-2.5\"\n >\n @let totalSum = sumSelectedItems();\n <!-- \u0418\u0442\u043E\u0433\u043E\u0432\u0430\u044F \u0441\u0443\u043C\u043C\u0430. -->\n <div class=\"text-base font-bold\">{{ totalLabel() }}: {{ totalSum | tuiFormatNumber: moneyFormat | async }} {{ 'RUB' | tuiCurrency }}</div>\n </div>\n</div>\n", dependencies: [{ kind: "pipe", type: AsyncPipe, name: "async" }, { kind: "pipe", type: TuiFormatNumberPipe, name: "tuiFormatNumber" }, { kind: "component", type: ScOrderItemComponent, selector: "sc-order-item", inputs: ["isShowEditControls", "useLinePrice", "markUnavailable", "isPending", "settingsAvailable"], outputs: ["clickSettings"] }, { kind: "directive", type: ScPrintDirective, selector: "[scPrint]", inputs: ["scPrint"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$1.CheckboxControlValueAccessor, selector: "input[type=checkbox][formControlName],input[type=checkbox][formControl],input[type=checkbox][ngModel]" }, { kind: "directive", type: i1$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1$1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1$1.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "component", type: TuiCheckbox, selector: "input[type=\"checkbox\"][tuiCheckbox]", inputs: ["size"] }, { kind: "directive", type: TuiLabel, selector: "label[tuiLabel]" }, { 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: TuiCurrencyPipe, name: "tuiCurrency" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
9084
9382
|
}
|
|
9085
9383
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: ScOrderItemsListComponent, decorators: [{
|
|
9086
9384
|
type: Component,
|
|
@@ -9096,8 +9394,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
|
|
|
9096
9394
|
TuiButton,
|
|
9097
9395
|
TuiButtonLoading,
|
|
9098
9396
|
TuiCurrencyPipe,
|
|
9099
|
-
], changeDetection: ChangeDetectionStrategy.OnPush, template: "<div\n [formGroup]=\"selectionFormGroup\"\n class=\"flex flex-col gap-5\"\n>\n <!-- \u0424\u043B\u0430\u0433 \u0432\u044B\u0431\u043E\u0440\u0430 \u0432\u0441\u0435\u0445 \u043F\u043E\u0437\u0438\u0446\u0438\u0439. -->\n @if (selectable() && itemsSignal().length) {\n <label\n tuiLabel\n scPrint=\"defaultNoPrint\"\n class=\"self-start\"\n >\n <input\n tuiCheckbox\n type=\"checkbox\"\n size=\"m\"\n [ngModel]=\"allItemsSelected()\"\n [ngModelOptions]=\"{ standalone: true }\"\n (change)=\"selectAllChangeHandler($event)\"\n />\n \u0412\u044B\u0431\u0440\u0430\u0442\u044C \u0432\u0441\u0435\n </label>\n }\n\n @for (
|
|
9100
|
-
}], propDecorators: { items: [{
|
|
9397
|
+
], changeDetection: ChangeDetectionStrategy.OnPush, template: "<div\n [formGroup]=\"selectionFormGroup\"\n class=\"flex flex-col gap-5\"\n>\n <!-- \u0424\u043B\u0430\u0433 \u0432\u044B\u0431\u043E\u0440\u0430 \u0432\u0441\u0435\u0445 \u043F\u043E\u0437\u0438\u0446\u0438\u0439. -->\n @if (selectable() && itemsSignal().length) {\n <label\n tuiLabel\n scPrint=\"defaultNoPrint\"\n class=\"self-start\"\n >\n <input\n tuiCheckbox\n type=\"checkbox\"\n size=\"m\"\n [ngModel]=\"allItemsSelected()\"\n [ngModelOptions]=\"{ standalone: true }\"\n [disabled]=\"selectionFormGroup.disabled\"\n (change)=\"selectAllChangeHandler($event)\"\n />\n \u0412\u044B\u0431\u0440\u0430\u0442\u044C \u0432\u0441\u0435\n </label>\n }\n\n @for (group of groups(); track group.name) {\n <!-- \u0417\u0430\u0433\u043E\u043B\u043E\u0432\u043E\u043A \u0433\u0440\u0443\u043F\u043F\u044B \u043F\u043E\u0437\u0438\u0446\u0438\u0439. -->\n @if (group.name) {\n <div class=\"flex flex-wrap items-end justify-between gap-x-4 gap-y-1 border-b border-tui-base-03 pb-2\">\n <div class=\"flex min-w-0 flex-col\">\n <span class=\"break-words text-body-l-bold\">{{ group.name }}</span>\n <span class=\"text-body-s text-tui-text-02\">\u043F\u043E\u0437\u0438\u0446\u0438\u0439: {{ group.items.length }}, \u0432 \u0437\u0430\u043A\u0430\u0437: {{ group.orderableCount }}</span>\n </div>\n <div class=\"flex flex-col items-end gap-1\">\n <!-- \u041A\u043D\u043E\u043F\u043A\u0430 \u0443\u0434\u0430\u043B\u0435\u043D\u0438\u044F \u0433\u0440\u0443\u043F\u043F\u044B. -->\n @if (canDeleteGroups()) {\n <button\n tuiButton\n type=\"button\"\n appearance=\"flat\"\n size=\"xs\"\n scPrint=\"defaultNoPrint\"\n [loading]=\"deletingGroupsNames().has(group.name)\"\n [disabled]=\"pendingGroupsNames().has(group.name)\"\n (click)=\"deleteGroup(group)\"\n >\n \u0423\u0434\u0430\u043B\u0438\u0442\u044C\n </button>\n }\n <span class=\"whitespace-nowrap text-body-m-bold\">{{ group.orderableSum | tuiFormatNumber: moneyFormat | async }} {{ 'RUB' | tuiCurrency }}</span>\n </div>\n </div>\n }\n\n @for (item of group.items; track item.id) {\n @if ($index !== 0) {\n <hr class=\"mt-auto h-px w-full bg-tui-clear\" />\n }\n\n <div class=\"flex items-center gap-4 relative\">\n @let isActive = !selectable() || selectionFormGroup.get(item.id)?.value || (markUnavailable() && !item.isOrderable);\n @let isPending = pendingItemsIds().has(item.id);\n @let editedInDialog = isEditedInDialog(item);\n\n <!-- \u0427\u0435\u043A\u0431\u043E\u043A\u0441 \u0432\u044B\u0431\u043E\u0440\u0430 \u0442\u043E\u0432\u0430\u0440\u0430. -->\n @if (selectable()) {\n <input\n tuiCheckbox\n type=\"checkbox\"\n size=\"m\"\n [formControlName]=\"item.id\"\n />\n }\n\n <!-- \u041A\u0430\u0440\u0442\u043E\u0447\u043A\u0430 \u0442\u043E\u0432\u0430\u0440\u0430. -->\n <sc-order-item\n #itemComponent\n [product]=\"item.product\"\n [orderItem]=\"item\"\n [showQuantityControl]=\"canEdit() && !item.configurator && !editedInDialog\"\n [isShowEditControls]=\"canEdit()\"\n [useLinePrice]=\"useLinePrice()\"\n [markUnavailable]=\"markUnavailable()\"\n [isPending]=\"isPending\"\n [settingsAvailable]=\"canEdit() && measuredInDialog() && !item.configurator && !item.isInactive\"\n class=\"w-full duration-200\"\n [class.opacity-60]=\"isItemDimmed(item)\"\n (clickCardEvent)=\"openProductInfoDialog(item.product, item, orderOptions?.orderId())\"\n (quantityValueChanges)=\"patchItem($event, itemComponent)\"\n (clickPriceHistoryEvent)=\"openPriceHistoryChartDialog(item.product)\"\n (clickSettings)=\"openSettingsDialog(item)\"\n [class.pointer-events-none]=\"!isActive\"\n />\n\n <!-- \u041A\u043D\u043E\u043F\u043A\u0430 \u0443\u0434\u0430\u043B\u0435\u043D\u0438\u044F \u0442\u043E\u0432\u0430\u0440\u0430. -->\n @if (canEdit()) {\n <button\n tuiIconButton\n iconStart=\"@tui.trash\"\n (click)=\"deleteItem(item)\"\n [loading]=\"isPending\"\n size=\"s\"\n appearance=\"secondary\"\n class=\"!absolute left-1 top-1 lg:!relative lg:left-0 lg:top-0 lg:!self-center\"\n ></button>\n }\n </div>\n }\n }\n\n <!-- \u0424\u0443\u0442\u0435\u0440 \u0441\u043F\u0438\u0441\u043A\u0430. -->\n <div\n scPrint=\"cost\"\n class=\"flex flex-col items-end gap-2.5\"\n >\n @let totalSum = sumSelectedItems();\n <!-- \u0418\u0442\u043E\u0433\u043E\u0432\u0430\u044F \u0441\u0443\u043C\u043C\u0430. -->\n <div class=\"text-base font-bold\">{{ totalLabel() }}: {{ totalSum | tuiFormatNumber: moneyFormat | async }} {{ 'RUB' | tuiCurrency }}</div>\n </div>\n</div>\n" }]
|
|
9398
|
+
}], ctorParameters: () => [], propDecorators: { items: [{
|
|
9101
9399
|
type: Input,
|
|
9102
9400
|
args: [{ required: true }]
|
|
9103
9401
|
}] } });
|
|
@@ -9122,6 +9420,18 @@ class ScOrderItemsListWithActionsComponent {
|
|
|
9122
9420
|
* Включить редактирование позиций.
|
|
9123
9421
|
*/
|
|
9124
9422
|
this.editable = input(false);
|
|
9423
|
+
/**
|
|
9424
|
+
* Признак, что цена за единицу берётся из позиции, а не из текущей цены товара.
|
|
9425
|
+
*/
|
|
9426
|
+
this.useLinePrice = input(false);
|
|
9427
|
+
/**
|
|
9428
|
+
* Откуда удаляется позиция для текста подтверждения, например «из черновика». Если не задано, позиция удаляется без подтверждения.
|
|
9429
|
+
*/
|
|
9430
|
+
this.deleteConfirmationTarget = input(null);
|
|
9431
|
+
/**
|
|
9432
|
+
* Подпись итоговой суммы.
|
|
9433
|
+
*/
|
|
9434
|
+
this.totalLabel = input('Итого');
|
|
9125
9435
|
/**
|
|
9126
9436
|
* Позиции, доступные для заказа.
|
|
9127
9437
|
*/
|
|
@@ -9145,11 +9455,11 @@ class ScOrderItemsListWithActionsComponent {
|
|
|
9145
9455
|
}, { allowSignalWrites: true });
|
|
9146
9456
|
}
|
|
9147
9457
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: ScOrderItemsListWithActionsComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
9148
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.14", type: ScOrderItemsListWithActionsComponent, isStandalone: true, selector: "sc-order-items-list-with-actions", inputs: { items: { classPropertyName: "items", publicName: "items", isSignal: true, isRequired: true, transformFunction: null }, selectable: { classPropertyName: "selectable", publicName: "selectable", isSignal: true, isRequired: false, transformFunction: null }, editable: { classPropertyName: "editable", publicName: "editable", isSignal: true, isRequired: false, transformFunction: null }, selectedItemsIds: { classPropertyName: "selectedItemsIds", publicName: "selectedItemsIds", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { selectedItemsIds: "selectedItemsIdsChange" }, ngImport: i0, template: "<div class=\"flex flex-col gap-5\">\n <div class=\"flex flex-col gap-2.5\">\n @if (availableItems().length) {\n <sc-order-items-list\n [items]=\"availableItems()\"\n [selectable]=\"selectable()\"\n [editable]=\"editable()\"\n (selectedItemsIdsChange)=\"selectedItemsIds.set($event)\"\n />\n }\n\n <!-- \u0424\u0443\u0442\u0435\u0440 \u0441\u043F\u0438\u0441\u043A\u0430 \u0442\u043E\u0432\u0430\u0440\u043E\u0432. -->\n <div class=\"flex flex-col items-center sm:flex-row sm:items-start sm:flex-wrap-reverse grow gap-x-2.5 gap-y-4\">\n <ng-content select=\"[footerActions]\" />\n </div>\n </div>\n\n @if (nullItems().length) {\n <div class=\"flex flex-col gap-2.5\">\n <div class=\"text-base font-bold\">\u0422\u043E\u0432\u0430\u0440\u044B \u0438 \u0443\u0441\u043B\u0443\u0433\u0438, \u043A\u043E\u0442\u043E\u0440\u044B\u0435 \u0431\u043E\u043B\u044C\u0448\u0435 \u043D\u0435 \u043F\u0440\u0435\u0434\u043E\u0441\u0442\u0430\u0432\u043B\u044F\u044E\u0442\u0441\u044F.</div>\n\n <sc-order-items-list\n [items]=\"nullItems()\"\n [selectable]=\"false\"\n [editable]=\"editable()\"\n />\n </div>\n }\n</div>\n", dependencies: [{ kind: "component", type: ScOrderItemsListComponent, selector: "sc-order-items-list", inputs: ["selectable", "editable", "items"], outputs: ["selectedItemsIdsChange"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
9458
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.14", type: ScOrderItemsListWithActionsComponent, isStandalone: true, selector: "sc-order-items-list-with-actions", inputs: { items: { classPropertyName: "items", publicName: "items", isSignal: true, isRequired: true, transformFunction: null }, selectable: { classPropertyName: "selectable", publicName: "selectable", isSignal: true, isRequired: false, transformFunction: null }, editable: { classPropertyName: "editable", publicName: "editable", isSignal: true, isRequired: false, transformFunction: null }, useLinePrice: { classPropertyName: "useLinePrice", publicName: "useLinePrice", isSignal: true, isRequired: false, transformFunction: null }, deleteConfirmationTarget: { classPropertyName: "deleteConfirmationTarget", publicName: "deleteConfirmationTarget", isSignal: true, isRequired: false, transformFunction: null }, totalLabel: { classPropertyName: "totalLabel", publicName: "totalLabel", isSignal: true, isRequired: false, transformFunction: null }, selectedItemsIds: { classPropertyName: "selectedItemsIds", publicName: "selectedItemsIds", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { selectedItemsIds: "selectedItemsIdsChange" }, ngImport: i0, template: "<div class=\"flex flex-col gap-5\">\n <div class=\"flex flex-col gap-2.5\">\n @if (availableItems().length) {\n <sc-order-items-list\n [items]=\"availableItems()\"\n [selectable]=\"selectable()\"\n [editable]=\"editable()\"\n [useLinePrice]=\"useLinePrice()\"\n [deleteConfirmationTarget]=\"deleteConfirmationTarget()\"\n [totalLabel]=\"totalLabel()\"\n (selectedItemsIdsChange)=\"selectedItemsIds.set($event)\"\n />\n }\n\n <!-- \u0424\u0443\u0442\u0435\u0440 \u0441\u043F\u0438\u0441\u043A\u0430 \u0442\u043E\u0432\u0430\u0440\u043E\u0432. -->\n <div class=\"flex flex-col items-center sm:flex-row sm:items-start sm:flex-wrap-reverse grow gap-x-2.5 gap-y-4\">\n <ng-content select=\"[footerActions]\" />\n </div>\n </div>\n\n @if (nullItems().length) {\n <div class=\"flex flex-col gap-2.5\">\n <div class=\"text-base font-bold\">\u0422\u043E\u0432\u0430\u0440\u044B \u0438 \u0443\u0441\u043B\u0443\u0433\u0438, \u043A\u043E\u0442\u043E\u0440\u044B\u0435 \u0431\u043E\u043B\u044C\u0448\u0435 \u043D\u0435 \u043F\u0440\u0435\u0434\u043E\u0441\u0442\u0430\u0432\u043B\u044F\u044E\u0442\u0441\u044F.</div>\n\n <sc-order-items-list\n [items]=\"nullItems()\"\n [selectable]=\"false\"\n [editable]=\"editable()\"\n [useLinePrice]=\"useLinePrice()\"\n [deleteConfirmationTarget]=\"deleteConfirmationTarget()\"\n />\n </div>\n }\n</div>\n", dependencies: [{ kind: "component", type: ScOrderItemsListComponent, selector: "sc-order-items-list", inputs: ["selectable", "editable", "grouped", "markUnavailable", "useLinePrice", "measuredInDialog", "deleteConfirmationTarget", "totalLabel", "items"], outputs: ["selectedItemsIdsChange"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
9149
9459
|
}
|
|
9150
9460
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: ScOrderItemsListWithActionsComponent, decorators: [{
|
|
9151
9461
|
type: Component,
|
|
9152
|
-
args: [{ standalone: true, selector: 'sc-order-items-list-with-actions', imports: [ScOrderItemsListComponent], changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"flex flex-col gap-5\">\n <div class=\"flex flex-col gap-2.5\">\n @if (availableItems().length) {\n <sc-order-items-list\n [items]=\"availableItems()\"\n [selectable]=\"selectable()\"\n [editable]=\"editable()\"\n (selectedItemsIdsChange)=\"selectedItemsIds.set($event)\"\n />\n }\n\n <!-- \u0424\u0443\u0442\u0435\u0440 \u0441\u043F\u0438\u0441\u043A\u0430 \u0442\u043E\u0432\u0430\u0440\u043E\u0432. -->\n <div class=\"flex flex-col items-center sm:flex-row sm:items-start sm:flex-wrap-reverse grow gap-x-2.5 gap-y-4\">\n <ng-content select=\"[footerActions]\" />\n </div>\n </div>\n\n @if (nullItems().length) {\n <div class=\"flex flex-col gap-2.5\">\n <div class=\"text-base font-bold\">\u0422\u043E\u0432\u0430\u0440\u044B \u0438 \u0443\u0441\u043B\u0443\u0433\u0438, \u043A\u043E\u0442\u043E\u0440\u044B\u0435 \u0431\u043E\u043B\u044C\u0448\u0435 \u043D\u0435 \u043F\u0440\u0435\u0434\u043E\u0441\u0442\u0430\u0432\u043B\u044F\u044E\u0442\u0441\u044F.</div>\n\n <sc-order-items-list\n [items]=\"nullItems()\"\n [selectable]=\"false\"\n [editable]=\"editable()\"\n />\n </div>\n }\n</div>\n" }]
|
|
9462
|
+
args: [{ standalone: true, selector: 'sc-order-items-list-with-actions', imports: [ScOrderItemsListComponent], changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"flex flex-col gap-5\">\n <div class=\"flex flex-col gap-2.5\">\n @if (availableItems().length) {\n <sc-order-items-list\n [items]=\"availableItems()\"\n [selectable]=\"selectable()\"\n [editable]=\"editable()\"\n [useLinePrice]=\"useLinePrice()\"\n [deleteConfirmationTarget]=\"deleteConfirmationTarget()\"\n [totalLabel]=\"totalLabel()\"\n (selectedItemsIdsChange)=\"selectedItemsIds.set($event)\"\n />\n }\n\n <!-- \u0424\u0443\u0442\u0435\u0440 \u0441\u043F\u0438\u0441\u043A\u0430 \u0442\u043E\u0432\u0430\u0440\u043E\u0432. -->\n <div class=\"flex flex-col items-center sm:flex-row sm:items-start sm:flex-wrap-reverse grow gap-x-2.5 gap-y-4\">\n <ng-content select=\"[footerActions]\" />\n </div>\n </div>\n\n @if (nullItems().length) {\n <div class=\"flex flex-col gap-2.5\">\n <div class=\"text-base font-bold\">\u0422\u043E\u0432\u0430\u0440\u044B \u0438 \u0443\u0441\u043B\u0443\u0433\u0438, \u043A\u043E\u0442\u043E\u0440\u044B\u0435 \u0431\u043E\u043B\u044C\u0448\u0435 \u043D\u0435 \u043F\u0440\u0435\u0434\u043E\u0441\u0442\u0430\u0432\u043B\u044F\u044E\u0442\u0441\u044F.</div>\n\n <sc-order-items-list\n [items]=\"nullItems()\"\n [selectable]=\"false\"\n [editable]=\"editable()\"\n [useLinePrice]=\"useLinePrice()\"\n [deleteConfirmationTarget]=\"deleteConfirmationTarget()\"\n />\n </div>\n }\n</div>\n" }]
|
|
9153
9463
|
}], ctorParameters: () => [] });
|
|
9154
9464
|
|
|
9155
9465
|
/**
|
|
@@ -9169,6 +9479,14 @@ class ScNewOrderItemsComponent {
|
|
|
9169
9479
|
* Включить выбор позиций чекбоксами.
|
|
9170
9480
|
*/
|
|
9171
9481
|
this.selectable = input(true);
|
|
9482
|
+
/**
|
|
9483
|
+
* Признак, что цена за единицу берётся из позиции, а не из текущей цены товара.
|
|
9484
|
+
*/
|
|
9485
|
+
this.useLinePrice = input(false);
|
|
9486
|
+
/**
|
|
9487
|
+
* Подпись итоговой суммы.
|
|
9488
|
+
*/
|
|
9489
|
+
this.totalLabel = input('Итого');
|
|
9172
9490
|
/**
|
|
9173
9491
|
* Время обработки и принятия заказов (из настроек окружения приложения).
|
|
9174
9492
|
*/
|
|
@@ -9179,11 +9497,11 @@ class ScNewOrderItemsComponent {
|
|
|
9179
9497
|
this.selectedItemsIdsChange = output();
|
|
9180
9498
|
}
|
|
9181
9499
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: ScNewOrderItemsComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
9182
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "18.2.14", type: ScNewOrderItemsComponent, isStandalone: true, selector: "sc-new-order-items", inputs: { items: { classPropertyName: "items", publicName: "items", isSignal: true, isRequired: true, transformFunction: null }, editable: { classPropertyName: "editable", publicName: "editable", isSignal: true, isRequired: false, transformFunction: null }, selectable: { classPropertyName: "selectable", publicName: "selectable", isSignal: true, isRequired: false, transformFunction: null }, orderProcessingAcceptanceTime: { classPropertyName: "orderProcessingAcceptanceTime", publicName: "orderProcessingAcceptanceTime", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { selectedItemsIdsChange: "selectedItemsIdsChange" }, ngImport: i0, template: "<div class=\"flex flex-col gap-5\">\n <ul class=\"ml-4 list-disc text-tui-success-fill\">\n <li>\u0417\u0430\u044F\u0432\u043A\u0438 \u043F\u0440\u0438\u043D\u0438\u043C\u0430\u044E\u0442\u0441\u044F \u043D\u0435 \u043F\u043E\u0437\u0434\u043D\u0435\u0435 {{ orderProcessingAcceptanceTime() }} \u043F\u043B\u0430\u043D\u0438\u0440\u0443\u0435\u043C\u043E\u0433\u043E \u0434\u043D\u044F \u043F\u043E\u0441\u0442\u0430\u0432\u043A\u0438.</li>\n <li>\u0417\u0430\u043A\u0430\u0437\u044B \u0441 \u0434\u043E\u0441\u0442\u0430\u0432\u043A\u043E\u0439 \u043D\u0430 \u043F\u043E\u043D\u0435\u0434\u0435\u043B\u044C\u043D\u0438\u043A \u043F\u0440\u0438\u043D\u0438\u043C\u0430\u044E\u0442\u0441\u044F \u0434\u043E {{ orderProcessingAcceptanceTime() }} \u043F\u044F\u0442\u043D\u0438\u0446\u044B.</li>\n <li>\u0417\u0430\u043A\u0430\u0437\u044B, \u0441\u043E\u0437\u0434\u0430\u043D\u043D\u044B\u0435 \u043F\u043E\u0437\u0434\u043D\u0435\u0435 {{ orderProcessingAcceptanceTime() }} \u043F\u044F\u0442\u043D\u0438\u0446\u044B \u0438\u043B\u0438 \u0432 \u0441\u0443\u0431\u0431\u043E\u0442\u0443/\u0432\u043E\u0441\u043A\u0440\u0435\u0441\u0435\u043D\u044C\u0435 \u0431\u0443\u0434\u0443\u0442 \u043E\u0431\u0440\u0430\u0431\u043E\u0442\u0430\u043D\u044B \u0432 \u0431\u0443\u0434\u043D\u0438\u0435 \u0434\u043D\u0438.</li>\n <li>\n \u0423\u043F\u0440\u0430\u0432\u043B\u044F\u0442\u044C \u0441\u043F\u0438\u0441\u043A\u043E\u043C \u043A\u043E\u043D\u0442\u0440\u0430\u0433\u0435\u043D\u0442\u043E\u0432 \u0432\u044B \u043C\u043E\u0436\u0435\u0442\u0435 \u0432\n <a\n tuiLink\n routerLink=\"/profile\"\n >\n \u043B\u0438\u0447\u043D\u043E\u043C \u043A\u0430\u0431\u0438\u043D\u0435\u0442\u0435\n </a>\n .\n </li>\n </ul>\n\n <div class=\"sub-title\">\u0421\u043E\u0441\u0442\u0430\u0432 \u0437\u0430\u043A\u0430\u0437\u0430</div>\n\n <sc-order-items-list-with-actions\n [items]=\"items()\"\n [editable]=\"editable()\"\n [selectable]=\"selectable()\"\n (selectedItemsIdsChange)=\"selectedItemsIdsChange.emit($event)\"\n >\n <ng-content\n select=\"[footerActions]\"\n ngProjectAs=\"[footerActions]\"\n />\n </sc-order-items-list-with-actions>\n</div>\n", dependencies: [{ kind: "component", type: ScOrderItemsListWithActionsComponent, selector: "sc-order-items-list-with-actions", inputs: ["items", "selectable", "editable", "selectedItemsIds"], outputs: ["selectedItemsIdsChange"] }, { kind: "directive", type: RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }, { kind: "directive", type: TuiLink, selector: "a[tuiLink], button[tuiLink]", inputs: ["pseudo"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
9500
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "18.2.14", type: ScNewOrderItemsComponent, isStandalone: true, selector: "sc-new-order-items", inputs: { items: { classPropertyName: "items", publicName: "items", isSignal: true, isRequired: true, transformFunction: null }, editable: { classPropertyName: "editable", publicName: "editable", isSignal: true, isRequired: false, transformFunction: null }, selectable: { classPropertyName: "selectable", publicName: "selectable", isSignal: true, isRequired: false, transformFunction: null }, useLinePrice: { classPropertyName: "useLinePrice", publicName: "useLinePrice", isSignal: true, isRequired: false, transformFunction: null }, totalLabel: { classPropertyName: "totalLabel", publicName: "totalLabel", isSignal: true, isRequired: false, transformFunction: null }, orderProcessingAcceptanceTime: { classPropertyName: "orderProcessingAcceptanceTime", publicName: "orderProcessingAcceptanceTime", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { selectedItemsIdsChange: "selectedItemsIdsChange" }, ngImport: i0, template: "<div class=\"flex flex-col gap-5\">\n <ul class=\"ml-4 list-disc text-tui-success-fill\">\n <li>\u0417\u0430\u044F\u0432\u043A\u0438 \u043F\u0440\u0438\u043D\u0438\u043C\u0430\u044E\u0442\u0441\u044F \u043D\u0435 \u043F\u043E\u0437\u0434\u043D\u0435\u0435 {{ orderProcessingAcceptanceTime() }} \u043F\u043B\u0430\u043D\u0438\u0440\u0443\u0435\u043C\u043E\u0433\u043E \u0434\u043D\u044F \u043F\u043E\u0441\u0442\u0430\u0432\u043A\u0438.</li>\n <li>\u0417\u0430\u043A\u0430\u0437\u044B \u0441 \u0434\u043E\u0441\u0442\u0430\u0432\u043A\u043E\u0439 \u043D\u0430 \u043F\u043E\u043D\u0435\u0434\u0435\u043B\u044C\u043D\u0438\u043A \u043F\u0440\u0438\u043D\u0438\u043C\u0430\u044E\u0442\u0441\u044F \u0434\u043E {{ orderProcessingAcceptanceTime() }} \u043F\u044F\u0442\u043D\u0438\u0446\u044B.</li>\n <li>\u0417\u0430\u043A\u0430\u0437\u044B, \u0441\u043E\u0437\u0434\u0430\u043D\u043D\u044B\u0435 \u043F\u043E\u0437\u0434\u043D\u0435\u0435 {{ orderProcessingAcceptanceTime() }} \u043F\u044F\u0442\u043D\u0438\u0446\u044B \u0438\u043B\u0438 \u0432 \u0441\u0443\u0431\u0431\u043E\u0442\u0443/\u0432\u043E\u0441\u043A\u0440\u0435\u0441\u0435\u043D\u044C\u0435 \u0431\u0443\u0434\u0443\u0442 \u043E\u0431\u0440\u0430\u0431\u043E\u0442\u0430\u043D\u044B \u0432 \u0431\u0443\u0434\u043D\u0438\u0435 \u0434\u043D\u0438.</li>\n <li>\n \u0423\u043F\u0440\u0430\u0432\u043B\u044F\u0442\u044C \u0441\u043F\u0438\u0441\u043A\u043E\u043C \u043A\u043E\u043D\u0442\u0440\u0430\u0433\u0435\u043D\u0442\u043E\u0432 \u0432\u044B \u043C\u043E\u0436\u0435\u0442\u0435 \u0432\n <a\n tuiLink\n routerLink=\"/profile\"\n >\n \u043B\u0438\u0447\u043D\u043E\u043C \u043A\u0430\u0431\u0438\u043D\u0435\u0442\u0435\n </a>\n .\n </li>\n </ul>\n\n <div class=\"sub-title\">\u0421\u043E\u0441\u0442\u0430\u0432 \u0437\u0430\u043A\u0430\u0437\u0430</div>\n\n <sc-order-items-list-with-actions\n [items]=\"items()\"\n [editable]=\"editable()\"\n [selectable]=\"selectable()\"\n [useLinePrice]=\"useLinePrice()\"\n [totalLabel]=\"totalLabel()\"\n (selectedItemsIdsChange)=\"selectedItemsIdsChange.emit($event)\"\n >\n <ng-content\n select=\"[footerActions]\"\n ngProjectAs=\"[footerActions]\"\n />\n </sc-order-items-list-with-actions>\n</div>\n", dependencies: [{ kind: "component", type: ScOrderItemsListWithActionsComponent, selector: "sc-order-items-list-with-actions", inputs: ["items", "selectable", "editable", "useLinePrice", "deleteConfirmationTarget", "totalLabel", "selectedItemsIds"], outputs: ["selectedItemsIdsChange"] }, { kind: "directive", type: RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }, { kind: "directive", type: TuiLink, selector: "a[tuiLink], button[tuiLink]", inputs: ["pseudo"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
9183
9501
|
}
|
|
9184
9502
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: ScNewOrderItemsComponent, decorators: [{
|
|
9185
9503
|
type: Component,
|
|
9186
|
-
args: [{ standalone: true, selector: 'sc-new-order-items', imports: [ScOrderItemsListWithActionsComponent, RouterLink, TuiLink], changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"flex flex-col gap-5\">\n <ul class=\"ml-4 list-disc text-tui-success-fill\">\n <li>\u0417\u0430\u044F\u0432\u043A\u0438 \u043F\u0440\u0438\u043D\u0438\u043C\u0430\u044E\u0442\u0441\u044F \u043D\u0435 \u043F\u043E\u0437\u0434\u043D\u0435\u0435 {{ orderProcessingAcceptanceTime() }} \u043F\u043B\u0430\u043D\u0438\u0440\u0443\u0435\u043C\u043E\u0433\u043E \u0434\u043D\u044F \u043F\u043E\u0441\u0442\u0430\u0432\u043A\u0438.</li>\n <li>\u0417\u0430\u043A\u0430\u0437\u044B \u0441 \u0434\u043E\u0441\u0442\u0430\u0432\u043A\u043E\u0439 \u043D\u0430 \u043F\u043E\u043D\u0435\u0434\u0435\u043B\u044C\u043D\u0438\u043A \u043F\u0440\u0438\u043D\u0438\u043C\u0430\u044E\u0442\u0441\u044F \u0434\u043E {{ orderProcessingAcceptanceTime() }} \u043F\u044F\u0442\u043D\u0438\u0446\u044B.</li>\n <li>\u0417\u0430\u043A\u0430\u0437\u044B, \u0441\u043E\u0437\u0434\u0430\u043D\u043D\u044B\u0435 \u043F\u043E\u0437\u0434\u043D\u0435\u0435 {{ orderProcessingAcceptanceTime() }} \u043F\u044F\u0442\u043D\u0438\u0446\u044B \u0438\u043B\u0438 \u0432 \u0441\u0443\u0431\u0431\u043E\u0442\u0443/\u0432\u043E\u0441\u043A\u0440\u0435\u0441\u0435\u043D\u044C\u0435 \u0431\u0443\u0434\u0443\u0442 \u043E\u0431\u0440\u0430\u0431\u043E\u0442\u0430\u043D\u044B \u0432 \u0431\u0443\u0434\u043D\u0438\u0435 \u0434\u043D\u0438.</li>\n <li>\n \u0423\u043F\u0440\u0430\u0432\u043B\u044F\u0442\u044C \u0441\u043F\u0438\u0441\u043A\u043E\u043C \u043A\u043E\u043D\u0442\u0440\u0430\u0433\u0435\u043D\u0442\u043E\u0432 \u0432\u044B \u043C\u043E\u0436\u0435\u0442\u0435 \u0432\n <a\n tuiLink\n routerLink=\"/profile\"\n >\n \u043B\u0438\u0447\u043D\u043E\u043C \u043A\u0430\u0431\u0438\u043D\u0435\u0442\u0435\n </a>\n .\n </li>\n </ul>\n\n <div class=\"sub-title\">\u0421\u043E\u0441\u0442\u0430\u0432 \u0437\u0430\u043A\u0430\u0437\u0430</div>\n\n <sc-order-items-list-with-actions\n [items]=\"items()\"\n [editable]=\"editable()\"\n [selectable]=\"selectable()\"\n (selectedItemsIdsChange)=\"selectedItemsIdsChange.emit($event)\"\n >\n <ng-content\n select=\"[footerActions]\"\n ngProjectAs=\"[footerActions]\"\n />\n </sc-order-items-list-with-actions>\n</div>\n" }]
|
|
9504
|
+
args: [{ standalone: true, selector: 'sc-new-order-items', imports: [ScOrderItemsListWithActionsComponent, RouterLink, TuiLink], changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"flex flex-col gap-5\">\n <ul class=\"ml-4 list-disc text-tui-success-fill\">\n <li>\u0417\u0430\u044F\u0432\u043A\u0438 \u043F\u0440\u0438\u043D\u0438\u043C\u0430\u044E\u0442\u0441\u044F \u043D\u0435 \u043F\u043E\u0437\u0434\u043D\u0435\u0435 {{ orderProcessingAcceptanceTime() }} \u043F\u043B\u0430\u043D\u0438\u0440\u0443\u0435\u043C\u043E\u0433\u043E \u0434\u043D\u044F \u043F\u043E\u0441\u0442\u0430\u0432\u043A\u0438.</li>\n <li>\u0417\u0430\u043A\u0430\u0437\u044B \u0441 \u0434\u043E\u0441\u0442\u0430\u0432\u043A\u043E\u0439 \u043D\u0430 \u043F\u043E\u043D\u0435\u0434\u0435\u043B\u044C\u043D\u0438\u043A \u043F\u0440\u0438\u043D\u0438\u043C\u0430\u044E\u0442\u0441\u044F \u0434\u043E {{ orderProcessingAcceptanceTime() }} \u043F\u044F\u0442\u043D\u0438\u0446\u044B.</li>\n <li>\u0417\u0430\u043A\u0430\u0437\u044B, \u0441\u043E\u0437\u0434\u0430\u043D\u043D\u044B\u0435 \u043F\u043E\u0437\u0434\u043D\u0435\u0435 {{ orderProcessingAcceptanceTime() }} \u043F\u044F\u0442\u043D\u0438\u0446\u044B \u0438\u043B\u0438 \u0432 \u0441\u0443\u0431\u0431\u043E\u0442\u0443/\u0432\u043E\u0441\u043A\u0440\u0435\u0441\u0435\u043D\u044C\u0435 \u0431\u0443\u0434\u0443\u0442 \u043E\u0431\u0440\u0430\u0431\u043E\u0442\u0430\u043D\u044B \u0432 \u0431\u0443\u0434\u043D\u0438\u0435 \u0434\u043D\u0438.</li>\n <li>\n \u0423\u043F\u0440\u0430\u0432\u043B\u044F\u0442\u044C \u0441\u043F\u0438\u0441\u043A\u043E\u043C \u043A\u043E\u043D\u0442\u0440\u0430\u0433\u0435\u043D\u0442\u043E\u0432 \u0432\u044B \u043C\u043E\u0436\u0435\u0442\u0435 \u0432\n <a\n tuiLink\n routerLink=\"/profile\"\n >\n \u043B\u0438\u0447\u043D\u043E\u043C \u043A\u0430\u0431\u0438\u043D\u0435\u0442\u0435\n </a>\n .\n </li>\n </ul>\n\n <div class=\"sub-title\">\u0421\u043E\u0441\u0442\u0430\u0432 \u0437\u0430\u043A\u0430\u0437\u0430</div>\n\n <sc-order-items-list-with-actions\n [items]=\"items()\"\n [editable]=\"editable()\"\n [selectable]=\"selectable()\"\n [useLinePrice]=\"useLinePrice()\"\n [totalLabel]=\"totalLabel()\"\n (selectedItemsIdsChange)=\"selectedItemsIdsChange.emit($event)\"\n >\n <ng-content\n select=\"[footerActions]\"\n ngProjectAs=\"[footerActions]\"\n />\n </sc-order-items-list-with-actions>\n</div>\n" }]
|
|
9187
9505
|
}] });
|
|
9188
9506
|
|
|
9189
9507
|
/**
|
|
@@ -9451,8 +9769,10 @@ class ScOrderFormComponent {
|
|
|
9451
9769
|
}
|
|
9452
9770
|
/**
|
|
9453
9771
|
* Обрабатывает отправку формы: валидирует и эмитит данные заказа родителю.
|
|
9772
|
+
* Общая ошибка сервера с прошлой отправки (например, 409) не мешает отправить форму снова, ошибки полей остаются до их исправления.
|
|
9454
9773
|
*/
|
|
9455
9774
|
submit() {
|
|
9775
|
+
this.form.updateValueAndValidity({ emitEvent: false });
|
|
9456
9776
|
this.form.markAllAsTouched();
|
|
9457
9777
|
if (!this.form.valid) {
|
|
9458
9778
|
return;
|
|
@@ -9470,7 +9790,7 @@ class ScOrderFormComponent {
|
|
|
9470
9790
|
* @param error Ответ сервера с ошибками.
|
|
9471
9791
|
*/
|
|
9472
9792
|
applyServerErrors(error) {
|
|
9473
|
-
const { errors, message } = error.error;
|
|
9793
|
+
const { errors, message } = (error.error ?? {});
|
|
9474
9794
|
if (errors) {
|
|
9475
9795
|
Object.keys(errors).forEach((key) => {
|
|
9476
9796
|
if (key === 'deliveryDate' && isArray(errors[key])) {
|
|
@@ -9787,7 +10107,7 @@ class ScOrderItemsListByStockComponent {
|
|
|
9787
10107
|
this.selectedItemsIds.update((previous) => [...previous.filter((id) => !items.some((item) => item.id === id) && this.items().some((item) => item.id === id)), ...itemsIds]);
|
|
9788
10108
|
}
|
|
9789
10109
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: ScOrderItemsListByStockComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
9790
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.14", type: ScOrderItemsListByStockComponent, isStandalone: true, selector: "sc-order-items-list-by-stock", inputs: { items: { classPropertyName: "items", publicName: "items", isSignal: true, isRequired: true, transformFunction: null }, warehouseId: { classPropertyName: "warehouseId", publicName: "warehouseId", isSignal: true, isRequired: true, transformFunction: null }, selectable: { classPropertyName: "selectable", publicName: "selectable", isSignal: true, isRequired: false, transformFunction: null }, editable: { classPropertyName: "editable", publicName: "editable", isSignal: true, isRequired: false, transformFunction: null }, sectionHeader: { classPropertyName: "sectionHeader", publicName: "sectionHeader", isSignal: true, isRequired: false, transformFunction: null }, selectedItemsIds: { classPropertyName: "selectedItemsIds", publicName: "selectedItemsIds", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { selectedItemsIds: "selectedItemsIdsChange" }, ngImport: i0, template: "<div class=\"flex flex-col gap-5\">\n @if (itemsInWarehouseStock().length > 0) {\n <sc-order-items-list\n [items]=\"itemsInWarehouseStock()\"\n [selectable]=\"selectable()\"\n [editable]=\"editable()\"\n (selectedItemsIdsChange)=\"selectedItemsHandler($event, itemsInWarehouseStock())\"\n />\n }\n\n @if (itemsOnOrder().length > 0) {\n <div class=\"flex flex-col gap-2.5\">\n @if (sectionHeader()) {\n <div class=\"text-base font-bold text-sc-dark-grey\">{{ sectionHeader() }}</div>\n }\n <sc-order-items-list\n [items]=\"itemsOnOrder()\"\n [selectable]=\"selectable()\"\n [editable]=\"editable()\"\n (selectedItemsIdsChange)=\"selectedItemsHandler($event, itemsOnOrder())\"\n />\n </div>\n }\n\n @if (itemsNotInWarehouseStock().length > 0) {\n <div class=\"flex flex-col gap-2.5\">\n @if (sectionHeader()) {\n <div class=\"text-base font-bold text-sc-dark-grey\">{{ sectionHeader() }}</div>\n }\n <sc-order-items-list\n [items]=\"itemsNotInWarehouseStock()\"\n [selectable]=\"selectable()\"\n [editable]=\"editable()\"\n (selectedItemsIdsChange)=\"selectedItemsHandler($event, itemsNotInWarehouseStock())\"\n />\n </div>\n }\n</div>\n", dependencies: [{ kind: "component", type: ScOrderItemsListComponent, selector: "sc-order-items-list", inputs: ["selectable", "editable", "items"], outputs: ["selectedItemsIdsChange"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
10110
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.14", type: ScOrderItemsListByStockComponent, isStandalone: true, selector: "sc-order-items-list-by-stock", inputs: { items: { classPropertyName: "items", publicName: "items", isSignal: true, isRequired: true, transformFunction: null }, warehouseId: { classPropertyName: "warehouseId", publicName: "warehouseId", isSignal: true, isRequired: true, transformFunction: null }, selectable: { classPropertyName: "selectable", publicName: "selectable", isSignal: true, isRequired: false, transformFunction: null }, editable: { classPropertyName: "editable", publicName: "editable", isSignal: true, isRequired: false, transformFunction: null }, sectionHeader: { classPropertyName: "sectionHeader", publicName: "sectionHeader", isSignal: true, isRequired: false, transformFunction: null }, selectedItemsIds: { classPropertyName: "selectedItemsIds", publicName: "selectedItemsIds", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { selectedItemsIds: "selectedItemsIdsChange" }, ngImport: i0, template: "<div class=\"flex flex-col gap-5\">\n @if (itemsInWarehouseStock().length > 0) {\n <sc-order-items-list\n [items]=\"itemsInWarehouseStock()\"\n [selectable]=\"selectable()\"\n [editable]=\"editable()\"\n (selectedItemsIdsChange)=\"selectedItemsHandler($event, itemsInWarehouseStock())\"\n />\n }\n\n @if (itemsOnOrder().length > 0) {\n <div class=\"flex flex-col gap-2.5\">\n @if (sectionHeader()) {\n <div class=\"text-base font-bold text-sc-dark-grey\">{{ sectionHeader() }}</div>\n }\n <sc-order-items-list\n [items]=\"itemsOnOrder()\"\n [selectable]=\"selectable()\"\n [editable]=\"editable()\"\n (selectedItemsIdsChange)=\"selectedItemsHandler($event, itemsOnOrder())\"\n />\n </div>\n }\n\n @if (itemsNotInWarehouseStock().length > 0) {\n <div class=\"flex flex-col gap-2.5\">\n @if (sectionHeader()) {\n <div class=\"text-base font-bold text-sc-dark-grey\">{{ sectionHeader() }}</div>\n }\n <sc-order-items-list\n [items]=\"itemsNotInWarehouseStock()\"\n [selectable]=\"selectable()\"\n [editable]=\"editable()\"\n (selectedItemsIdsChange)=\"selectedItemsHandler($event, itemsNotInWarehouseStock())\"\n />\n </div>\n }\n</div>\n", dependencies: [{ kind: "component", type: ScOrderItemsListComponent, selector: "sc-order-items-list", inputs: ["selectable", "editable", "grouped", "markUnavailable", "useLinePrice", "measuredInDialog", "deleteConfirmationTarget", "totalLabel", "items"], outputs: ["selectedItemsIdsChange"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
9791
10111
|
}
|
|
9792
10112
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: ScOrderItemsListByStockComponent, decorators: [{
|
|
9793
10113
|
type: Component,
|
|
@@ -9795,32 +10115,815 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
|
|
|
9795
10115
|
}] });
|
|
9796
10116
|
|
|
9797
10117
|
/**
|
|
9798
|
-
*
|
|
10118
|
+
* Класс-помощник для работы со списком черновиков.
|
|
9799
10119
|
*/
|
|
9800
|
-
class
|
|
10120
|
+
class ScDraftListHelper {
|
|
10121
|
+
/**
|
|
10122
|
+
* Возвращает признак, что состав страницы не изменился: то же общее количество черновиков и те же черновики в том же порядке.
|
|
10123
|
+
*
|
|
10124
|
+
* @param items Текущие элементы списка.
|
|
10125
|
+
* @param total Текущее общее количество черновиков.
|
|
10126
|
+
* @param drafts Черновики полученной страницы.
|
|
10127
|
+
* @param receivedTotal Общее количество черновиков в полученных данных.
|
|
10128
|
+
*/
|
|
10129
|
+
static isSamePage(items, total, drafts, receivedTotal) {
|
|
10130
|
+
return total === receivedTotal && items.length === drafts.length && drafts.every((draft, index) => draft.id === items.at(index)?.order.id);
|
|
10131
|
+
}
|
|
10132
|
+
/**
|
|
10133
|
+
* Возвращает подпись состава страницы: общее количество черновиков и номера черновиков страницы по порядку.
|
|
10134
|
+
*
|
|
10135
|
+
* @param page Страница списка.
|
|
10136
|
+
*/
|
|
10137
|
+
static getSignature(page) {
|
|
10138
|
+
return `${page.meta.total}:${page.data.map(({ id }) => id).join(',')}`;
|
|
10139
|
+
}
|
|
10140
|
+
/**
|
|
10141
|
+
* Возвращает элементы новой страницы списка. Черновики, которые уже есть в списке, сохраняют раскрытие и полные данные.
|
|
10142
|
+
*
|
|
10143
|
+
* @param items Текущие элементы списка.
|
|
10144
|
+
* @param drafts Черновики новой страницы.
|
|
10145
|
+
*/
|
|
10146
|
+
static fromPage(items, drafts) {
|
|
10147
|
+
return drafts.map((short) => {
|
|
10148
|
+
const item = items.find(({ order }) => order.id === short.id);
|
|
10149
|
+
return item ? ScDraftListHelper.update(item, short) : { order: short, isExpanded: false };
|
|
10150
|
+
});
|
|
10151
|
+
}
|
|
10152
|
+
/**
|
|
10153
|
+
* Возвращает список с обновлёнными краткими данными полученных черновиков, состав и порядок списка не меняются.
|
|
10154
|
+
* Полные данные раскрытых черновиков не меняются: их обновляет кэш черновика.
|
|
10155
|
+
*
|
|
10156
|
+
* @param items Текущие элементы списка.
|
|
10157
|
+
* @param drafts Полученные краткие данные черновиков.
|
|
10158
|
+
*/
|
|
10159
|
+
static merge(items, drafts) {
|
|
10160
|
+
return items.map((item) => {
|
|
10161
|
+
const short = drafts.find(({ id }) => id === item.order.id);
|
|
10162
|
+
return short ? ScDraftListHelper.update(item, short) : item;
|
|
10163
|
+
});
|
|
10164
|
+
}
|
|
10165
|
+
/**
|
|
10166
|
+
* Возвращает номера черновиков с полными данными, у которых краткие данные отличаются: название или данные актуализации.
|
|
10167
|
+
*
|
|
10168
|
+
* @param items Текущие элементы списка.
|
|
10169
|
+
* @param drafts Полученные краткие данные черновиков.
|
|
10170
|
+
*/
|
|
10171
|
+
static getChangedFullDraftIds(items, drafts) {
|
|
10172
|
+
return items
|
|
10173
|
+
.filter(({ order }) => order instanceof ScOrderDraft)
|
|
10174
|
+
.filter(({ order }) => {
|
|
10175
|
+
const short = drafts.find(({ id }) => id === order.id);
|
|
10176
|
+
if (!short) {
|
|
10177
|
+
return false;
|
|
10178
|
+
}
|
|
10179
|
+
const isSameActualization = short.actualization ? short.actualization.isSameAs(order.actualization) : order.actualization === undefined;
|
|
10180
|
+
return short.name !== order.name || !isSameActualization;
|
|
10181
|
+
})
|
|
10182
|
+
.map(({ order }) => order.id);
|
|
10183
|
+
}
|
|
10184
|
+
/**
|
|
10185
|
+
* Обновляет элемент списка краткими данными черновика. Полные данные черновика не заменяются.
|
|
10186
|
+
*
|
|
10187
|
+
* @param item Элемент списка.
|
|
10188
|
+
* @param short Краткие данные черновика.
|
|
10189
|
+
*/
|
|
10190
|
+
static update(item, short) {
|
|
10191
|
+
if (!(item.order instanceof ScOrderDraft)) {
|
|
10192
|
+
// eslint-disable-next-line no-param-reassign
|
|
10193
|
+
item.order = short;
|
|
10194
|
+
}
|
|
10195
|
+
return item;
|
|
10196
|
+
}
|
|
10197
|
+
}
|
|
10198
|
+
|
|
10199
|
+
/**
|
|
10200
|
+
* Сервис текущего момента для меток и проверок срока актуализации черновиков Профстрой. Момент обновляется с интервалом проверки срока
|
|
10201
|
+
* актуализации одним таймером на всех подписчиков, поэтому список черновиков, страница черновика и оформление заказа видят один момент.
|
|
10202
|
+
* Таймер работает, только пока есть подписчики.
|
|
10203
|
+
*/
|
|
10204
|
+
class ScDraftExpirationClockService {
|
|
9801
10205
|
constructor() {
|
|
9802
10206
|
/**
|
|
9803
|
-
*
|
|
10207
|
+
* Интервалы обновления данных.
|
|
9804
10208
|
*/
|
|
9805
|
-
this.
|
|
10209
|
+
this.updateInterval = inject(SC_UPDATE_INTERVAL);
|
|
9806
10210
|
/**
|
|
9807
|
-
*
|
|
10211
|
+
* {@link Observable} текущего момента. Первый подписчик запускает таймер, после отписки всех подписчиков таймер останавливается.
|
|
10212
|
+
* Следующие подписчики сразу получают последний момент, а не новый: он может отставать от текущего времени не больше чем
|
|
10213
|
+
* на интервал проверки срока, и метки срока у нового подписчика обновятся на ближайшем тике общего таймера.
|
|
9808
10214
|
*/
|
|
9809
|
-
this.
|
|
10215
|
+
this.now$ = timer(0, this.updateInterval.orderDraftExpiration).pipe(map(() => new Date()), shareReplay({ bufferSize: 1, refCount: true }));
|
|
10216
|
+
}
|
|
10217
|
+
/**
|
|
10218
|
+
* Возвращает сигнал текущего момента, который обновляется общим таймером, только пока признак истинен. Вызывается в контексте
|
|
10219
|
+
* внедрения зависимостей, например при объявлении поля компонента: подписка живёт, пока жив компонент.
|
|
10220
|
+
*
|
|
10221
|
+
* @param active Признак, пока он истинен, момент обновляется.
|
|
10222
|
+
*/
|
|
10223
|
+
nowWhile(active) {
|
|
10224
|
+
return toSignal(toObservable(active).pipe(switchMap((isActive) => (isActive ? this.now$ : EMPTY))), { initialValue: new Date() });
|
|
10225
|
+
}
|
|
10226
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: ScDraftExpirationClockService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
10227
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: ScDraftExpirationClockService, providedIn: 'root' }); }
|
|
10228
|
+
}
|
|
10229
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: ScDraftExpirationClockService, decorators: [{
|
|
10230
|
+
type: Injectable,
|
|
10231
|
+
args: [{
|
|
10232
|
+
providedIn: 'root',
|
|
10233
|
+
}]
|
|
10234
|
+
}] });
|
|
10235
|
+
|
|
10236
|
+
/**
|
|
10237
|
+
* Сервис загрузки и фонового обновления страницы списка черновиков. Предоставляется компонентом списка.
|
|
10238
|
+
*
|
|
10239
|
+
* Страница загружается по параметрам из {@link connect} с индикатором загрузки, после показа загруженной страницы сервис сообщает о её загрузке.
|
|
10240
|
+
*
|
|
10241
|
+
* Если состав страницы не изменился, черновики обновляются на месте с сохранением раскрытых строк. Если черновики появились, пропали
|
|
10242
|
+
* или поменялся порядок, полученная страница откладывается и показывается уведомление с предложением перезагрузить список.
|
|
10243
|
+
* После отказа уведомление о том же составе не повторяется. Черновики Профстрой в обработке дополнительно опрашиваются лёгким запросом,
|
|
10244
|
+
* текущий момент для меток срока берётся из общего источника {@link ScDraftExpirationClockService}.
|
|
10245
|
+
*/
|
|
10246
|
+
class ScDraftListUpdatesService {
|
|
10247
|
+
/**
|
|
10248
|
+
* Инициализирует экземпляр класса {@link ScDraftListUpdatesService}.
|
|
10249
|
+
*/
|
|
10250
|
+
constructor() {
|
|
9810
10251
|
/**
|
|
9811
10252
|
* Сервис для работы с черновиками заказов.
|
|
9812
10253
|
*/
|
|
9813
10254
|
this.draftsService = inject(ScOrderDraftsService);
|
|
9814
10255
|
/**
|
|
9815
|
-
*
|
|
10256
|
+
* Сервис уведомлений.
|
|
9816
10257
|
*/
|
|
9817
|
-
this.
|
|
10258
|
+
this.alerts = inject(TuiAlertService);
|
|
9818
10259
|
/**
|
|
9819
|
-
*
|
|
10260
|
+
* Сервис текущего момента для меток срока актуализации.
|
|
9820
10261
|
*/
|
|
9821
|
-
this.
|
|
10262
|
+
this.expirationClock = inject(ScDraftExpirationClockService);
|
|
9822
10263
|
/**
|
|
9823
|
-
*
|
|
10264
|
+
* Ссылка для автоматического управления уничтожением зависимостей.
|
|
10265
|
+
*/
|
|
10266
|
+
this.destroyRef = inject(DestroyRef);
|
|
10267
|
+
/**
|
|
10268
|
+
* Элементы текущей страницы списка.
|
|
10269
|
+
*/
|
|
10270
|
+
this.pageItems = signal(null);
|
|
10271
|
+
/**
|
|
10272
|
+
* Данные пагинации текущей страницы.
|
|
10273
|
+
*/
|
|
10274
|
+
this.pageMeta = signal(null);
|
|
10275
|
+
/**
|
|
10276
|
+
* Признак загрузки страницы списка по действию пользователя.
|
|
10277
|
+
*/
|
|
10278
|
+
this.pageLoading = signal(false);
|
|
10279
|
+
/**
|
|
10280
|
+
* {@link Subject} события загрузки страницы списка по действию пользователя.
|
|
10281
|
+
*/
|
|
10282
|
+
this.pageLoadedSubject = new Subject();
|
|
10283
|
+
/**
|
|
10284
|
+
* Страница с изменённым составом, которая ждёт решения пользователя.
|
|
10285
|
+
*/
|
|
10286
|
+
this.pendingPage = null;
|
|
10287
|
+
/**
|
|
10288
|
+
* Подпись состава страницы, от перезагрузки которой пользователь отказался.
|
|
10289
|
+
*/
|
|
10290
|
+
this.declinedSignature = null;
|
|
10291
|
+
/**
|
|
10292
|
+
* Номера черновиков в обработке, которых не оказалось в ответе опроса: черновики удалены, и опрашивать их не нужно.
|
|
10293
|
+
*/
|
|
10294
|
+
this.missingIds = signal(new Set());
|
|
10295
|
+
/**
|
|
10296
|
+
* Номера раскрытых черновиков, перезапрос которых уже выполняется.
|
|
10297
|
+
*/
|
|
10298
|
+
this.refreshingIds = new Set();
|
|
10299
|
+
/**
|
|
10300
|
+
* Подписка на показанное уведомление об изменении списка.
|
|
10301
|
+
*/
|
|
10302
|
+
this.alertSubscription = null;
|
|
10303
|
+
/**
|
|
10304
|
+
* Номера черновиков Профстрой в обработке на текущей странице.
|
|
10305
|
+
*/
|
|
10306
|
+
this.processingIds = computed(() => (this.pageItems() ?? [])
|
|
10307
|
+
.filter(({ order }) => order.isProfstroy && order.actualization?.isProcessing === true && !this.missingIds().has(order.id))
|
|
10308
|
+
.map(({ order }) => order.id), { equal: (previous, current) => previous.join(',') === current.join(',') });
|
|
10309
|
+
/**
|
|
10310
|
+
* Элементы текущей страницы списка.
|
|
10311
|
+
*/
|
|
10312
|
+
this.items = this.pageItems.asReadonly();
|
|
10313
|
+
/**
|
|
10314
|
+
* Данные пагинации текущей страницы.
|
|
10315
|
+
*/
|
|
10316
|
+
this.meta = this.pageMeta.asReadonly();
|
|
10317
|
+
/**
|
|
10318
|
+
* Текущий момент для определения состояния черновиков Профстрой из общего источника.
|
|
10319
|
+
*/
|
|
10320
|
+
this.now = toSignal(this.expirationClock.now$, { initialValue: new Date() });
|
|
10321
|
+
/**
|
|
10322
|
+
* Признак загрузки страницы списка по действию пользователя (фильтры, сортировка, пагинация). Фоновые проверки его не включают.
|
|
10323
|
+
*/
|
|
10324
|
+
this.loading = this.pageLoading.asReadonly();
|
|
10325
|
+
/**
|
|
10326
|
+
* {@link Observable} события загрузки страницы списка по действию пользователя: страница уже показана.
|
|
10327
|
+
*/
|
|
10328
|
+
this.pageLoaded$ = this.pageLoadedSubject.asObservable();
|
|
10329
|
+
toObservable(this.processingIds)
|
|
10330
|
+
.pipe(switchMap((ids) => (ids.length > 0 ? this.draftsService.watchShortList$(ids).pipe(map((drafts) => ({ ids, drafts }))) : EMPTY)), takeUntilDestroyed(this.destroyRef))
|
|
10331
|
+
.subscribe(({ ids, drafts }) => {
|
|
10332
|
+
this.markMissing(ids, drafts);
|
|
10333
|
+
this.applyDrafts(drafts);
|
|
10334
|
+
});
|
|
10335
|
+
this.destroyRef.onDestroy(() => {
|
|
10336
|
+
this.dismissAlert();
|
|
10337
|
+
});
|
|
10338
|
+
}
|
|
10339
|
+
/**
|
|
10340
|
+
* Подключает к сервису параметры запроса текущей страницы: при каждом изменении параметров загружает страницу с индикатором загрузки
|
|
10341
|
+
* и сообщает о её загрузке, а по текущим параметрам в фоне проверяет изменения списка. Если страница не загрузилась, остаётся текущая,
|
|
10342
|
+
* а смена параметров загружает список заново.
|
|
10343
|
+
*
|
|
10344
|
+
* @param params$ {@link Observable} параметров запроса текущей страницы.
|
|
10345
|
+
*/
|
|
10346
|
+
connect(params$) {
|
|
10347
|
+
params$
|
|
10348
|
+
.pipe(switchMap((params) => this.draftsService.getList$(params).pipe(tap({
|
|
10349
|
+
subscribe: () => {
|
|
10350
|
+
this.pageLoading.set(true);
|
|
10351
|
+
},
|
|
10352
|
+
finalize: () => {
|
|
10353
|
+
this.pageLoading.set(false);
|
|
10354
|
+
},
|
|
10355
|
+
}), catchError(() => EMPTY))), takeUntilDestroyed(this.destroyRef))
|
|
10356
|
+
.subscribe((page) => {
|
|
10357
|
+
this.setPage(page);
|
|
10358
|
+
this.pageLoadedSubject.next();
|
|
10359
|
+
});
|
|
10360
|
+
params$
|
|
10361
|
+
.pipe(distinctUntilChanged((previous, current) => previous.toString() === current.toString()), switchMap((params) => this.draftsService.watchList$(params)), takeUntilDestroyed(this.destroyRef))
|
|
10362
|
+
.subscribe((page) => {
|
|
10363
|
+
this.checkPage(page);
|
|
10364
|
+
});
|
|
10365
|
+
}
|
|
10366
|
+
/**
|
|
10367
|
+
* Подставляет в строку текущей страницы полные данные черновика. Опрос черновиков в обработке сразу учитывает их состояние.
|
|
10368
|
+
*
|
|
10369
|
+
* @param draft Полные данные черновика.
|
|
10370
|
+
*/
|
|
10371
|
+
setFullDraft(draft) {
|
|
10372
|
+
const items = this.pageItems();
|
|
10373
|
+
const item = items?.find(({ order }) => order.id === draft.id);
|
|
10374
|
+
if (!items || !item) {
|
|
10375
|
+
return;
|
|
10376
|
+
}
|
|
10377
|
+
item.order = draft;
|
|
10378
|
+
this.pageItems.set([...items]);
|
|
10379
|
+
}
|
|
10380
|
+
/**
|
|
10381
|
+
* Показывает страницу, загруженную по действию пользователя: строки свёрнуты, отложенные данные и уведомление сбрасываются.
|
|
10382
|
+
*
|
|
10383
|
+
* @param page Страница списка.
|
|
10384
|
+
*/
|
|
10385
|
+
setPage(page) {
|
|
10386
|
+
this.pendingPage = null;
|
|
10387
|
+
this.declinedSignature = null;
|
|
10388
|
+
this.missingIds.set(new Set());
|
|
10389
|
+
this.dismissAlert();
|
|
10390
|
+
this.pageItems.set(page.data.map((order) => ({ order, isExpanded: false })));
|
|
10391
|
+
this.pageMeta.set(page.meta);
|
|
10392
|
+
}
|
|
10393
|
+
/**
|
|
10394
|
+
* Сравнивает полученную в фоне страницу с текущей: при том же составе обновляет черновики на месте, иначе откладывает страницу и предлагает перезагрузить список.
|
|
10395
|
+
*
|
|
10396
|
+
* @param page Страница, полученная в фоне.
|
|
10397
|
+
*/
|
|
10398
|
+
checkPage(page) {
|
|
10399
|
+
const items = this.pageItems();
|
|
10400
|
+
const meta = this.pageMeta();
|
|
10401
|
+
if (!items || !meta) {
|
|
10402
|
+
return;
|
|
10403
|
+
}
|
|
10404
|
+
if (ScDraftListHelper.isSamePage(items, meta.total, page.data, page.meta.total)) {
|
|
10405
|
+
this.pendingPage = null;
|
|
10406
|
+
this.declinedSignature = null;
|
|
10407
|
+
this.dismissAlert();
|
|
10408
|
+
this.applyDrafts(page.data);
|
|
10409
|
+
return;
|
|
10410
|
+
}
|
|
10411
|
+
this.pendingPage = page;
|
|
10412
|
+
if (ScDraftListHelper.getSignature(page) !== this.declinedSignature) {
|
|
10413
|
+
this.showAlert();
|
|
10414
|
+
}
|
|
10415
|
+
}
|
|
10416
|
+
/**
|
|
10417
|
+
* Обновляет на месте краткие данные черновиков и перезапрашивает полные данные раскрытых черновиков, состояние которых изменилось.
|
|
10418
|
+
* Перезапрос раскрытого черновика не повторяется, пока выполняется предыдущий. Ошибка перезапроса не показывается:
|
|
10419
|
+
* это фоновое обновление, его повторит следующий опрос.
|
|
10420
|
+
*
|
|
10421
|
+
* @param drafts Краткие данные черновиков.
|
|
10422
|
+
*/
|
|
10423
|
+
applyDrafts(drafts) {
|
|
10424
|
+
const items = this.pageItems();
|
|
10425
|
+
if (!items) {
|
|
10426
|
+
return;
|
|
10427
|
+
}
|
|
10428
|
+
ScDraftListHelper.getChangedFullDraftIds(items, drafts)
|
|
10429
|
+
.filter((draftId) => !this.refreshingIds.has(draftId))
|
|
10430
|
+
.forEach((draftId) => {
|
|
10431
|
+
this.refreshingIds.add(draftId);
|
|
10432
|
+
this.draftsService
|
|
10433
|
+
.fetch$(draftId)
|
|
10434
|
+
.pipe(catchError(() => EMPTY), finalize(() => {
|
|
10435
|
+
this.refreshingIds.delete(draftId);
|
|
10436
|
+
}), takeUntilDestroyed(this.destroyRef))
|
|
10437
|
+
.subscribe();
|
|
10438
|
+
});
|
|
10439
|
+
this.pageItems.set(ScDraftListHelper.merge(items, drafts));
|
|
10440
|
+
}
|
|
10441
|
+
/**
|
|
10442
|
+
* Запоминает черновики, которых не оказалось в ответе опроса, чтобы больше их не опрашивать.
|
|
10443
|
+
*
|
|
10444
|
+
* @param ids Номера опрошенных черновиков.
|
|
10445
|
+
* @param drafts Полученные краткие данные черновиков.
|
|
10446
|
+
*/
|
|
10447
|
+
markMissing(ids, drafts) {
|
|
10448
|
+
const missing = ids.filter((id) => !drafts.some((draft) => draft.id === id));
|
|
10449
|
+
if (missing.length > 0) {
|
|
10450
|
+
this.missingIds.update((current) => new Set([...current, ...missing]));
|
|
10451
|
+
}
|
|
10452
|
+
}
|
|
10453
|
+
/**
|
|
10454
|
+
* Показывает уведомление об изменении списка, если оно ещё не показано. Ответ «Нет» и закрытие уведомления считаются отказом
|
|
10455
|
+
* от перезагрузки отложенного состава.
|
|
10456
|
+
*/
|
|
10457
|
+
showAlert() {
|
|
10458
|
+
if (this.alertSubscription) {
|
|
10459
|
+
return;
|
|
10460
|
+
}
|
|
10461
|
+
this.alertSubscription = this.alerts
|
|
10462
|
+
.open(new PolymorpheusComponent(ScConfirmAlertComponent), {
|
|
10463
|
+
label: 'Список черновиков изменился',
|
|
10464
|
+
data: { content: 'Перезагрузить список?' },
|
|
10465
|
+
appearance: 'info',
|
|
10466
|
+
autoClose: 0,
|
|
10467
|
+
})
|
|
10468
|
+
.pipe(defaultIfEmpty(false), finalize(() => {
|
|
10469
|
+
this.alertSubscription = null;
|
|
10470
|
+
}))
|
|
10471
|
+
.subscribe((isConfirmed) => {
|
|
10472
|
+
if (isConfirmed) {
|
|
10473
|
+
this.applyPendingPage();
|
|
10474
|
+
}
|
|
10475
|
+
else {
|
|
10476
|
+
this.declinePendingPage();
|
|
10477
|
+
}
|
|
10478
|
+
});
|
|
10479
|
+
}
|
|
10480
|
+
/**
|
|
10481
|
+
* Закрывает показанное уведомление об изменении списка.
|
|
10482
|
+
*/
|
|
10483
|
+
dismissAlert() {
|
|
10484
|
+
this.alertSubscription?.unsubscribe();
|
|
10485
|
+
this.alertSubscription = null;
|
|
10486
|
+
}
|
|
10487
|
+
/**
|
|
10488
|
+
* Показывает отложенную страницу с сохранением раскрытых строк. Уведомление закрывается всякий раз, когда отложенная страница
|
|
10489
|
+
* сбрасывается, поэтому при согласии пользователя она всегда есть.
|
|
10490
|
+
*/
|
|
10491
|
+
applyPendingPage() {
|
|
10492
|
+
const page = this.pendingPage;
|
|
10493
|
+
if (!page) {
|
|
10494
|
+
return;
|
|
10495
|
+
}
|
|
10496
|
+
this.pendingPage = null;
|
|
10497
|
+
this.missingIds.set(new Set());
|
|
10498
|
+
this.pageItems.set(ScDraftListHelper.fromPage(this.pageItems() ?? [], page.data));
|
|
10499
|
+
this.pageMeta.set(page.meta);
|
|
10500
|
+
}
|
|
10501
|
+
/**
|
|
10502
|
+
* Запоминает отказ от перезагрузки отложенного состава: уведомление о нём больше не показывается, пока состав снова не изменится.
|
|
10503
|
+
*/
|
|
10504
|
+
declinePendingPage() {
|
|
10505
|
+
this.declinedSignature = this.pendingPage ? ScDraftListHelper.getSignature(this.pendingPage) : null;
|
|
10506
|
+
}
|
|
10507
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: ScDraftListUpdatesService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
10508
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: ScDraftListUpdatesService }); }
|
|
10509
|
+
}
|
|
10510
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: ScDraftListUpdatesService, decorators: [{
|
|
10511
|
+
type: Injectable
|
|
10512
|
+
}], ctorParameters: () => [] });
|
|
10513
|
+
|
|
10514
|
+
/**
|
|
10515
|
+
* Перечисление состояний черновика Профстрой для отображения пользователю.
|
|
10516
|
+
*/
|
|
10517
|
+
// eslint-disable-next-line no-shadow
|
|
10518
|
+
var ScProfstroyDraftState;
|
|
10519
|
+
(function (ScProfstroyDraftState) {
|
|
10520
|
+
/**
|
|
10521
|
+
* Выполняется обработка.
|
|
10522
|
+
*/
|
|
10523
|
+
ScProfstroyDraftState["processing"] = "processing";
|
|
10524
|
+
/**
|
|
10525
|
+
* Актуализирован, срок действия не истёк.
|
|
10526
|
+
*/
|
|
10527
|
+
ScProfstroyDraftState["actual"] = "actual";
|
|
10528
|
+
/**
|
|
10529
|
+
* Актуализирован, но срок действия истёк.
|
|
10530
|
+
*/
|
|
10531
|
+
ScProfstroyDraftState["outdated"] = "outdated";
|
|
10532
|
+
/**
|
|
10533
|
+
* Обработка завершилась с ошибкой.
|
|
10534
|
+
*/
|
|
10535
|
+
ScProfstroyDraftState["error"] = "error";
|
|
10536
|
+
/**
|
|
10537
|
+
* Требуется актуализация после изменения состава.
|
|
10538
|
+
*/
|
|
10539
|
+
ScProfstroyDraftState["needsActualization"] = "needsActualization";
|
|
10540
|
+
/**
|
|
10541
|
+
* Состояние не известно.
|
|
10542
|
+
*/
|
|
10543
|
+
ScProfstroyDraftState["unknown"] = "unknown";
|
|
10544
|
+
})(ScProfstroyDraftState || (ScProfstroyDraftState = {}));
|
|
10545
|
+
|
|
10546
|
+
/**
|
|
10547
|
+
* Класс-помощник для отображения черновиков Профстрой.
|
|
10548
|
+
*/
|
|
10549
|
+
class ScProfstroyDraftHelper {
|
|
10550
|
+
/**
|
|
10551
|
+
* Данные об отображении состояний черновика.
|
|
10552
|
+
*/
|
|
10553
|
+
static { this.stateViews = {
|
|
10554
|
+
[ScProfstroyDraftState.processing]: {
|
|
10555
|
+
label: 'Выполняется обработка',
|
|
10556
|
+
appearance: 'info',
|
|
10557
|
+
description: 'Файл обрабатывается, позиции и цены уточняются. Изменение состава и оформление заказа станут доступны после завершения.',
|
|
10558
|
+
},
|
|
10559
|
+
[ScProfstroyDraftState.actual]: {
|
|
10560
|
+
label: 'Актуализирован',
|
|
10561
|
+
appearance: 'positive',
|
|
10562
|
+
description: 'Цены и позиции актуальны, заказ можно оформлять.',
|
|
10563
|
+
},
|
|
10564
|
+
[ScProfstroyDraftState.outdated]: {
|
|
10565
|
+
label: 'Данные устарели',
|
|
10566
|
+
appearance: 'warning',
|
|
10567
|
+
description: 'С момента последней актуализации прошло много времени. Запустите её повторно, чтобы оформить заказ по актуальным ценам.',
|
|
10568
|
+
},
|
|
10569
|
+
[ScProfstroyDraftState.error]: {
|
|
10570
|
+
label: 'Ошибка обработки',
|
|
10571
|
+
appearance: 'negative',
|
|
10572
|
+
description: 'Обработка завершилась с ошибкой. Запустите актуализацию повторно или соберите заявку из файла заново.',
|
|
10573
|
+
},
|
|
10574
|
+
[ScProfstroyDraftState.needsActualization]: {
|
|
10575
|
+
label: 'Требуется актуализация',
|
|
10576
|
+
appearance: 'warning',
|
|
10577
|
+
description: 'Состав заявки изменён после последней актуализации. Запустите актуализацию, чтобы обновить цены и оформить заказ. Цены и примечания к позициям обновятся после актуализации.',
|
|
10578
|
+
},
|
|
10579
|
+
[ScProfstroyDraftState.unknown]: {
|
|
10580
|
+
label: 'Состояние неизвестно',
|
|
10581
|
+
appearance: 'neutral',
|
|
10582
|
+
description: '',
|
|
10583
|
+
},
|
|
10584
|
+
}; }
|
|
10585
|
+
/**
|
|
10586
|
+
* Возвращает состояние черновика на указанный момент.
|
|
10587
|
+
*
|
|
10588
|
+
* @param actualization Данные об актуализации.
|
|
10589
|
+
* @param now Момент проверки.
|
|
10590
|
+
*/
|
|
10591
|
+
static getState(actualization, now) {
|
|
10592
|
+
switch (actualization?.status) {
|
|
10593
|
+
case ScOrderDraftActualizationStatus.processing:
|
|
10594
|
+
return ScProfstroyDraftState.processing;
|
|
10595
|
+
case ScOrderDraftActualizationStatus.completed:
|
|
10596
|
+
return actualization.isOutdatedAt(now) ? ScProfstroyDraftState.outdated : ScProfstroyDraftState.actual;
|
|
10597
|
+
case ScOrderDraftActualizationStatus.error:
|
|
10598
|
+
return ScProfstroyDraftState.error;
|
|
10599
|
+
case ScOrderDraftActualizationStatus.needsActualization:
|
|
10600
|
+
return ScProfstroyDraftState.needsActualization;
|
|
10601
|
+
default:
|
|
10602
|
+
return ScProfstroyDraftState.unknown;
|
|
10603
|
+
}
|
|
10604
|
+
}
|
|
10605
|
+
/**
|
|
10606
|
+
* Возвращает данные об отображении состояния черновика на указанный момент.
|
|
10607
|
+
*
|
|
10608
|
+
* @param actualization Данные об актуализации.
|
|
10609
|
+
* @param now Момент проверки.
|
|
10610
|
+
*/
|
|
10611
|
+
static getStateView(actualization, now) {
|
|
10612
|
+
return ScProfstroyDraftHelper.stateViews[ScProfstroyDraftHelper.getState(actualization, now)];
|
|
10613
|
+
}
|
|
10614
|
+
/**
|
|
10615
|
+
* Возвращает данные об отображении панели состояния черновика: для актуальной заявки пояснение зависит от того, сколько позиций попадёт в заказ.
|
|
10616
|
+
*
|
|
10617
|
+
* @param draft Черновик Профстрой.
|
|
10618
|
+
* @param now Момент проверки.
|
|
10619
|
+
*/
|
|
10620
|
+
static getPanelView(draft, now) {
|
|
10621
|
+
const view = ScProfstroyDraftHelper.getStateView(draft.actualization, now);
|
|
10622
|
+
if (ScProfstroyDraftHelper.getState(draft.actualization, now) !== ScProfstroyDraftState.actual) {
|
|
10623
|
+
return view;
|
|
10624
|
+
}
|
|
10625
|
+
const total = draft.products.length;
|
|
10626
|
+
const orderable = draft.products.filter((item) => item.isOrderable).length;
|
|
10627
|
+
if (orderable === 0) {
|
|
10628
|
+
return {
|
|
10629
|
+
...view,
|
|
10630
|
+
appearance: 'warning',
|
|
10631
|
+
description: 'Актуализация завершена, но ни одна позиция не попадёт в заказ. Исправьте или удалите позиции с примечанием и запустите актуализацию повторно или соберите заявку заново из файла.',
|
|
10632
|
+
};
|
|
10633
|
+
}
|
|
10634
|
+
return orderable < total ? { ...view, description: `Цены актуальны. В заказ попадут ${orderable} из ${total} позиций, остальные отмечены в поле «Примечание».` } : view;
|
|
10635
|
+
}
|
|
10636
|
+
}
|
|
10637
|
+
|
|
10638
|
+
/**
|
|
10639
|
+
* Компонент панели состояния актуализации черновика Профстрой с действиями актуализации и пересборки.
|
|
10640
|
+
*/
|
|
10641
|
+
class ScProfstroyActualizationPanelComponent {
|
|
10642
|
+
constructor() {
|
|
10643
|
+
/**
|
|
10644
|
+
* Список ссылок приложения.
|
|
10645
|
+
*/
|
|
10646
|
+
this.urls = inject(SC_URLS);
|
|
10647
|
+
/**
|
|
10648
|
+
* Объект документа.
|
|
10649
|
+
*/
|
|
10650
|
+
this.document = inject(DOCUMENT);
|
|
10651
|
+
/**
|
|
10652
|
+
* Черновик Профстрой.
|
|
10653
|
+
*/
|
|
10654
|
+
this.draft = input.required();
|
|
10655
|
+
/**
|
|
10656
|
+
* Момент, на который определяется состояние. Передаётся снаружи и обновляется по таймеру проверки срока, иначе метка «Данные устарели» не сменится.
|
|
10657
|
+
*/
|
|
10658
|
+
this.now = input.required();
|
|
10659
|
+
/**
|
|
10660
|
+
* Признак, что действия над черновиком недоступны.
|
|
10661
|
+
*/
|
|
10662
|
+
this.busy = input(false);
|
|
10663
|
+
/**
|
|
10664
|
+
* Событие запуска актуализации.
|
|
10665
|
+
*/
|
|
10666
|
+
this.actualize = output();
|
|
10667
|
+
/**
|
|
10668
|
+
* Событие запуска пересборки из файла.
|
|
10669
|
+
*/
|
|
10670
|
+
this.reload = output();
|
|
10671
|
+
/**
|
|
10672
|
+
* Текущее состояние черновика.
|
|
10673
|
+
*/
|
|
10674
|
+
this.state = computed(() => ScProfstroyDraftHelper.getState(this.draft().actualization, this.now()));
|
|
10675
|
+
/**
|
|
10676
|
+
* Данные об отображении состояния.
|
|
10677
|
+
*/
|
|
10678
|
+
this.stateView = computed(() => ScProfstroyDraftHelper.getPanelView(this.draft(), this.now()));
|
|
10679
|
+
/**
|
|
10680
|
+
* Признак, что выполняется обработка.
|
|
10681
|
+
*/
|
|
10682
|
+
this.isProcessing = computed(() => this.state() === ScProfstroyDraftState.processing);
|
|
10683
|
+
/**
|
|
10684
|
+
* Признак, что срок действия актуализации ещё не истёк.
|
|
10685
|
+
*/
|
|
10686
|
+
this.isActual = computed(() => this.state() === ScProfstroyDraftState.actual);
|
|
10687
|
+
/**
|
|
10688
|
+
* Абсолютная ссылка на исходный файл заявки. Файл лежит на хосте API, адрес API может быть указан относительно сайта.
|
|
10689
|
+
*/
|
|
10690
|
+
this.fileUrl = computed(() => {
|
|
10691
|
+
const url = this.draft().file?.url;
|
|
10692
|
+
return url ? new URL(url, new URL(this.urls.apiUrl, this.document.baseURI)).href : null;
|
|
10693
|
+
});
|
|
10694
|
+
}
|
|
10695
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: ScProfstroyActualizationPanelComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
10696
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.14", type: ScProfstroyActualizationPanelComponent, isStandalone: true, selector: "sc-profstroy-actualization-panel", inputs: { draft: { classPropertyName: "draft", publicName: "draft", isSignal: true, isRequired: true, transformFunction: null }, now: { classPropertyName: "now", publicName: "now", isSignal: true, isRequired: true, transformFunction: null }, busy: { classPropertyName: "busy", publicName: "busy", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { actualize: "actualize", reload: "reload" }, ngImport: i0, template: "@let actualization = draft().actualization;\n@let view = stateView();\n\n<tui-notification\n [appearance]=\"view.appearance\"\n size=\"m\"\n>\n <div class=\"flex flex-col items-start gap-2 text-start\">\n <div class=\"flex items-center justify-start gap-2 text-start font-bold\">\n @if (isProcessing()) {\n <tui-loader size=\"s\" />\n }\n {{ view.label }}\n </div>\n\n <div class=\"flex flex-col items-start gap-1\">\n @if (actualization?.actualizedAt; as actualizedAt) {\n <span>\u0410\u043A\u0442\u0443\u0430\u043B\u0438\u0437\u0438\u0440\u043E\u0432\u0430\u043D {{ actualizedAt | scFormatDate }}</span>\n }\n @if (isActual() && actualization?.expiresAt; as expiresAt) {\n <span>\u0410\u043A\u0442\u0443\u0430\u043B\u0435\u043D \u0434\u043E {{ expiresAt | scFormatDate }}</span>\n }\n @if (view.description) {\n <span>{{ view.description }}</span>\n }\n @if (actualization?.error; as error) {\n <span class=\"font-bold\">{{ error }}</span>\n }\n @if (fileUrl(); as url) {\n <span>\n \u0424\u0430\u0439\u043B \u0437\u0430\u044F\u0432\u043A\u0438:\n <a\n tuiLink\n [href]=\"url\"\n target=\"_blank\"\n rel=\"noopener\"\n class=\"break-all\"\n >\n {{ draft().file?.originalName ?? '\u0441\u043A\u0430\u0447\u0430\u0442\u044C' }}\n </a>\n </span>\n }\n </div>\n\n <div class=\"flex flex-wrap gap-2\">\n <button\n tuiButton\n type=\"button\"\n size=\"s\"\n appearance=\"secondary\"\n iconStart=\"@tui.refresh-cw\"\n [disabled]=\"isProcessing() || busy()\"\n (click)=\"actualize.emit()\"\n >\n \u0410\u043A\u0442\u0443\u0430\u043B\u0438\u0437\u0438\u0440\u043E\u0432\u0430\u0442\u044C\n </button>\n <button\n tuiButton\n type=\"button\"\n size=\"s\"\n appearance=\"secondary\"\n iconStart=\"@tui.file-up\"\n [disabled]=\"isProcessing() || busy()\"\n (click)=\"reload.emit()\"\n >\n \u0421\u043E\u0431\u0440\u0430\u0442\u044C \u0437\u0430\u043D\u043E\u0432\u043E \u0438\u0437 \u0444\u0430\u0439\u043B\u0430\n </button>\n </div>\n </div>\n</tui-notification>\n", dependencies: [{ kind: "directive", type: TuiNotification, selector: "tui-notification,a[tuiNotification],button[tuiNotification]", inputs: ["appearance", "icon", "size"] }, { kind: "component", type: TuiLoader, selector: "tui-loader", inputs: ["size", "inheritColor", "overlay", "textContent", "showLoader"] }, { kind: "directive", type: TuiButton, selector: "a[tuiButton],button[tuiButton],a[tuiIconButton],button[tuiIconButton]", inputs: ["size"] }, { kind: "directive", type: TuiLink, selector: "a[tuiLink], button[tuiLink]", inputs: ["pseudo"] }, { kind: "pipe", type: ScFormatDatePipe, name: "scFormatDate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
10697
|
+
}
|
|
10698
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: ScProfstroyActualizationPanelComponent, decorators: [{
|
|
10699
|
+
type: Component,
|
|
10700
|
+
args: [{ standalone: true, selector: 'sc-profstroy-actualization-panel', imports: [TuiNotification, TuiLoader, TuiButton, TuiLink, ScFormatDatePipe], changeDetection: ChangeDetectionStrategy.OnPush, template: "@let actualization = draft().actualization;\n@let view = stateView();\n\n<tui-notification\n [appearance]=\"view.appearance\"\n size=\"m\"\n>\n <div class=\"flex flex-col items-start gap-2 text-start\">\n <div class=\"flex items-center justify-start gap-2 text-start font-bold\">\n @if (isProcessing()) {\n <tui-loader size=\"s\" />\n }\n {{ view.label }}\n </div>\n\n <div class=\"flex flex-col items-start gap-1\">\n @if (actualization?.actualizedAt; as actualizedAt) {\n <span>\u0410\u043A\u0442\u0443\u0430\u043B\u0438\u0437\u0438\u0440\u043E\u0432\u0430\u043D {{ actualizedAt | scFormatDate }}</span>\n }\n @if (isActual() && actualization?.expiresAt; as expiresAt) {\n <span>\u0410\u043A\u0442\u0443\u0430\u043B\u0435\u043D \u0434\u043E {{ expiresAt | scFormatDate }}</span>\n }\n @if (view.description) {\n <span>{{ view.description }}</span>\n }\n @if (actualization?.error; as error) {\n <span class=\"font-bold\">{{ error }}</span>\n }\n @if (fileUrl(); as url) {\n <span>\n \u0424\u0430\u0439\u043B \u0437\u0430\u044F\u0432\u043A\u0438:\n <a\n tuiLink\n [href]=\"url\"\n target=\"_blank\"\n rel=\"noopener\"\n class=\"break-all\"\n >\n {{ draft().file?.originalName ?? '\u0441\u043A\u0430\u0447\u0430\u0442\u044C' }}\n </a>\n </span>\n }\n </div>\n\n <div class=\"flex flex-wrap gap-2\">\n <button\n tuiButton\n type=\"button\"\n size=\"s\"\n appearance=\"secondary\"\n iconStart=\"@tui.refresh-cw\"\n [disabled]=\"isProcessing() || busy()\"\n (click)=\"actualize.emit()\"\n >\n \u0410\u043A\u0442\u0443\u0430\u043B\u0438\u0437\u0438\u0440\u043E\u0432\u0430\u0442\u044C\n </button>\n <button\n tuiButton\n type=\"button\"\n size=\"s\"\n appearance=\"secondary\"\n iconStart=\"@tui.file-up\"\n [disabled]=\"isProcessing() || busy()\"\n (click)=\"reload.emit()\"\n >\n \u0421\u043E\u0431\u0440\u0430\u0442\u044C \u0437\u0430\u043D\u043E\u0432\u043E \u0438\u0437 \u0444\u0430\u0439\u043B\u0430\n </button>\n </div>\n </div>\n</tui-notification>\n" }]
|
|
10701
|
+
}] });
|
|
10702
|
+
|
|
10703
|
+
/**
|
|
10704
|
+
* Компонент метки «Профстрой» у черновика или заказа, созданного из файла программы «Профстрой».
|
|
10705
|
+
*/
|
|
10706
|
+
class ScProfstroyBadgeComponent {
|
|
10707
|
+
constructor() {
|
|
10708
|
+
/**
|
|
10709
|
+
* Размер метки.
|
|
10710
|
+
*/
|
|
10711
|
+
this.size = input('m');
|
|
10712
|
+
}
|
|
10713
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: ScProfstroyBadgeComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
10714
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "18.2.14", type: ScProfstroyBadgeComponent, isStandalone: true, selector: "sc-profstroy-badge", inputs: { size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: "<tui-badge\n class=\"!font-medium\"\n appearance=\"primary\"\n [size]=\"size()\"\n>\n \u041F\u0440\u043E\u0444\u0441\u0442\u0440\u043E\u0439\n</tui-badge>\n", dependencies: [{ kind: "directive", type: TuiBadge, selector: "tui-badge,[tuiBadge]", inputs: ["size"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
10715
|
+
}
|
|
10716
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: ScProfstroyBadgeComponent, decorators: [{
|
|
10717
|
+
type: Component,
|
|
10718
|
+
args: [{ standalone: true, selector: 'sc-profstroy-badge', imports: [TuiBadge], changeDetection: ChangeDetectionStrategy.OnPush, template: "<tui-badge\n class=\"!font-medium\"\n appearance=\"primary\"\n [size]=\"size()\"\n>\n \u041F\u0440\u043E\u0444\u0441\u0442\u0440\u043E\u0439\n</tui-badge>\n" }]
|
|
10719
|
+
}] });
|
|
10720
|
+
|
|
10721
|
+
/**
|
|
10722
|
+
* Компонент меток черновика Профстрой: признак и текущее состояние обработки.
|
|
10723
|
+
*/
|
|
10724
|
+
class ScProfstroyDraftBadgesComponent {
|
|
10725
|
+
constructor() {
|
|
10726
|
+
/**
|
|
10727
|
+
* Данные об актуализации черновика.
|
|
10728
|
+
*/
|
|
10729
|
+
this.actualization = input();
|
|
10730
|
+
/**
|
|
10731
|
+
* Момент, на который определяется состояние. Передаётся снаружи и обновляется по таймеру проверки срока, иначе метка «Данные устарели» не сменится.
|
|
10732
|
+
*/
|
|
10733
|
+
this.now = input.required();
|
|
10734
|
+
/**
|
|
10735
|
+
* Данные об отображении состояния.
|
|
10736
|
+
*/
|
|
10737
|
+
this.stateView = computed(() => ScProfstroyDraftHelper.getStateView(this.actualization(), this.now()));
|
|
10738
|
+
}
|
|
10739
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: ScProfstroyDraftBadgesComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
10740
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "18.2.14", type: ScProfstroyDraftBadgesComponent, isStandalone: true, selector: "sc-profstroy-draft-badges", inputs: { actualization: { classPropertyName: "actualization", publicName: "actualization", isSignal: true, isRequired: false, transformFunction: null }, now: { classPropertyName: "now", publicName: "now", isSignal: true, isRequired: true, transformFunction: null } }, ngImport: i0, template: "<span class=\"inline-flex flex-wrap items-center gap-1\">\n <sc-profstroy-badge />\n <tui-badge\n class=\"!font-medium\"\n [appearance]=\"stateView().appearance\"\n size=\"m\"\n >\n {{ stateView().label }}\n </tui-badge>\n</span>\n", dependencies: [{ kind: "directive", type: TuiBadge, selector: "tui-badge,[tuiBadge]", inputs: ["size"] }, { kind: "component", type: ScProfstroyBadgeComponent, selector: "sc-profstroy-badge", inputs: ["size"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
10741
|
+
}
|
|
10742
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: ScProfstroyDraftBadgesComponent, decorators: [{
|
|
10743
|
+
type: Component,
|
|
10744
|
+
args: [{ standalone: true, selector: 'sc-profstroy-draft-badges', imports: [TuiBadge, ScProfstroyBadgeComponent], changeDetection: ChangeDetectionStrategy.OnPush, template: "<span class=\"inline-flex flex-wrap items-center gap-1\">\n <sc-profstroy-badge />\n <tui-badge\n class=\"!font-medium\"\n [appearance]=\"stateView().appearance\"\n size=\"m\"\n >\n {{ stateView().label }}\n </tui-badge>\n</span>\n" }]
|
|
10745
|
+
}] });
|
|
10746
|
+
|
|
10747
|
+
/**
|
|
10748
|
+
* Класс-помощник для сообщений об ошибках запросов к API.
|
|
10749
|
+
*/
|
|
10750
|
+
class ScApiErrorHelper {
|
|
10751
|
+
/**
|
|
10752
|
+
* Возвращает текст ошибки из ответа API или указанный текст, если ответ его не содержит.
|
|
10753
|
+
*
|
|
10754
|
+
* @param error Ошибка запроса.
|
|
10755
|
+
* @param fallback Текст по умолчанию.
|
|
10756
|
+
*/
|
|
10757
|
+
static getMessage(error, fallback) {
|
|
10758
|
+
const message = error instanceof HttpErrorResponse ? error.error?.message : undefined;
|
|
10759
|
+
return message?.trim() ? message : fallback;
|
|
10760
|
+
}
|
|
10761
|
+
/**
|
|
10762
|
+
* Возвращает признак ответа 404: запись не найдена, например удалена в другой вкладке.
|
|
10763
|
+
*
|
|
10764
|
+
* @param error Ошибка запроса.
|
|
10765
|
+
*/
|
|
10766
|
+
static isNotFound(error) {
|
|
10767
|
+
return error instanceof HttpErrorResponse && error.status === HttpStatusCode.NotFound;
|
|
10768
|
+
}
|
|
10769
|
+
}
|
|
10770
|
+
|
|
10771
|
+
/* eslint-disable @typescript-eslint/unbound-method */
|
|
10772
|
+
/**
|
|
10773
|
+
* Компонент диалога создания черновика заказа из файла программы «Профстрой».
|
|
10774
|
+
*/
|
|
10775
|
+
class ScProfstroyDraftCreateDialogComponent {
|
|
10776
|
+
constructor() {
|
|
10777
|
+
/**
|
|
10778
|
+
* Контекст диалогового окна, в котором открыт компонент.
|
|
10779
|
+
*/
|
|
10780
|
+
this.context = inject(POLYMORPHEUS_CONTEXT);
|
|
10781
|
+
/**
|
|
10782
|
+
* Сервис для работы с черновиками заказов.
|
|
10783
|
+
*/
|
|
10784
|
+
this.draftsService = inject(ScOrderDraftsService);
|
|
10785
|
+
/**
|
|
10786
|
+
* Ссылка для автоматического управления уничтожением зависимостей.
|
|
10787
|
+
*/
|
|
10788
|
+
this.destroyRef = inject(DestroyRef);
|
|
10789
|
+
/**
|
|
10790
|
+
* Настройки поля выбора файлов, в том числе наибольший размер файла из настроек приложения.
|
|
10791
|
+
*/
|
|
10792
|
+
this.inputFilesOptions = inject(TUI_INPUT_FILES_OPTIONS);
|
|
10793
|
+
/**
|
|
10794
|
+
* Допустимое расширение файла.
|
|
10795
|
+
*/
|
|
10796
|
+
this.accept = '.xlsx';
|
|
10797
|
+
/**
|
|
10798
|
+
* Наибольшая длина названия заявки.
|
|
10799
|
+
*/
|
|
10800
|
+
this.nameMaxLength = 255;
|
|
10801
|
+
/**
|
|
10802
|
+
* Форма создания черновика. Формат и размер файла проверяются на самом поле: после выбора файла поле выбора убирается вместе со своими проверками.
|
|
10803
|
+
*/
|
|
10804
|
+
this.form = new FormGroup({
|
|
10805
|
+
file: new FormControl(null, [Validators.required, tuiCreateFileFormatValidator(this.accept), tuiCreateFileSizeValidator(this.inputFilesOptions.maxFileSize)]),
|
|
10806
|
+
name: new FormControl('', { nonNullable: true, validators: Validators.maxLength(this.nameMaxLength) }),
|
|
10807
|
+
});
|
|
10808
|
+
/**
|
|
10809
|
+
* Признак выполнения запроса создания.
|
|
10810
|
+
*/
|
|
10811
|
+
this.submitting = signal(false);
|
|
10812
|
+
/**
|
|
10813
|
+
* Признак попытки создать заявку. До неё незаполненное поле файла ошибкой не подсвечивается: поле теряет фокус уже при открытии окна выбора файла.
|
|
10814
|
+
*/
|
|
10815
|
+
this.submitAttempted = signal(false);
|
|
10816
|
+
}
|
|
10817
|
+
/**
|
|
10818
|
+
* Удаляет выбранный файл.
|
|
10819
|
+
*/
|
|
10820
|
+
removeFile() {
|
|
10821
|
+
this.form.controls.file.setValue(null);
|
|
10822
|
+
}
|
|
10823
|
+
/**
|
|
10824
|
+
* Отправляет файл и создаёт черновик. Ошибки сервера с прошлой отправки не мешают отправить форму снова.
|
|
10825
|
+
*/
|
|
10826
|
+
submit() {
|
|
10827
|
+
this.submitAttempted.set(true);
|
|
10828
|
+
Object.values(this.form.controls).forEach((control) => {
|
|
10829
|
+
control.updateValueAndValidity();
|
|
10830
|
+
});
|
|
10831
|
+
this.form.markAllAsTouched();
|
|
10832
|
+
const file = this.form.controls.file.value;
|
|
10833
|
+
if (this.form.invalid || !file || this.submitting()) {
|
|
10834
|
+
return;
|
|
10835
|
+
}
|
|
10836
|
+
this.submitting.set(true);
|
|
10837
|
+
ScFileHelper.readAsUploadedFile$(file)
|
|
10838
|
+
.pipe(switchMap((uploadedFile) => this.draftsService.createProfstroy$({ file: uploadedFile, name: this.form.controls.name.value.trim() || undefined })), finalize(() => {
|
|
10839
|
+
this.submitting.set(false);
|
|
10840
|
+
}), takeUntilDestroyed(this.destroyRef))
|
|
10841
|
+
.subscribe({
|
|
10842
|
+
next: (draft) => {
|
|
10843
|
+
this.context.completeWith(draft);
|
|
10844
|
+
},
|
|
10845
|
+
error: (error) => {
|
|
10846
|
+
this.applyError(error);
|
|
10847
|
+
},
|
|
10848
|
+
});
|
|
10849
|
+
}
|
|
10850
|
+
/**
|
|
10851
|
+
* Показывает ошибку создания в форме: ошибки проверки данных у полей, ошибку чтения файла у поля файла, остальные ошибкой формы (401 обрабатывает авторизация).
|
|
10852
|
+
* Диалог остаётся открытым.
|
|
10853
|
+
*
|
|
10854
|
+
* @param error Ошибка чтения файла или запроса.
|
|
10855
|
+
*/
|
|
10856
|
+
applyError(error) {
|
|
10857
|
+
if (!(error instanceof HttpErrorResponse)) {
|
|
10858
|
+
this.form.controls.file.setErrors({ serverResponse: new TuiValidationError('Не удалось прочитать файл.') });
|
|
10859
|
+
return;
|
|
10860
|
+
}
|
|
10861
|
+
if (error.status === HttpStatusCode.Unauthorized) {
|
|
10862
|
+
return;
|
|
10863
|
+
}
|
|
10864
|
+
const { errors } = (error.error ?? {});
|
|
10865
|
+
if (error.status !== HttpStatusCode.UnprocessableEntity || !errors) {
|
|
10866
|
+
this.form.setErrors({ serverResponse: new TuiValidationError(ScApiErrorHelper.getMessage(error, 'Не удалось создать заявку.')) });
|
|
10867
|
+
return;
|
|
10868
|
+
}
|
|
10869
|
+
Object.entries(errors).forEach(([key, messages]) => {
|
|
10870
|
+
const control = key === 'name' ? this.form.controls.name : this.form.controls.file;
|
|
10871
|
+
control.setErrors({ serverResponse: new TuiValidationError(messages.join(' ')) });
|
|
10872
|
+
control.markAsTouched();
|
|
10873
|
+
});
|
|
10874
|
+
}
|
|
10875
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: ScProfstroyDraftCreateDialogComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
10876
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.14", type: ScProfstroyDraftCreateDialogComponent, isStandalone: true, selector: "sc-profstroy-draft-create-dialog", providers: [
|
|
10877
|
+
{
|
|
10878
|
+
provide: TUI_INPUT_FILE_TEXTS,
|
|
10879
|
+
useFactory: () => inject(TUI_INPUT_FILE_TEXTS, { skipSelf: true }).pipe(map((texts) => ({ ...texts, formatRejectionReason: 'Файл должен быть в формате .xlsx' }))),
|
|
10880
|
+
},
|
|
10881
|
+
], ngImport: i0, template: "<form\n [formGroup]=\"form\"\n class=\"flex flex-col gap-5\"\n (ngSubmit)=\"submit()\"\n>\n <!-- \u0424\u0430\u0439\u043B. \u041F\u0440\u0438\u0447\u0438\u043D\u0443 \u043E\u0442\u043A\u043B\u043E\u043D\u0435\u043D\u0438\u044F \u0444\u0430\u0439\u043B\u0430 \u043F\u043E\u043A\u0430\u0437\u044B\u0432\u0430\u0435\u0442 \u0441\u0442\u0440\u043E\u043A\u0430 \u0444\u0430\u0439\u043B\u0430, \u043F\u043E\u0434 \u043F\u043E\u043B\u0435\u043C \u043E\u043D\u0430 \u043D\u0435 \u043F\u043E\u0432\u0442\u043E\u0440\u044F\u0435\u0442\u0441\u044F. \u041D\u0435\u0437\u0430\u043F\u043E\u043B\u043D\u0435\u043D\u043D\u043E\u0435 \u043F\u043E\u043B\u0435 \u043F\u043E\u0434\u0441\u0432\u0435\u0447\u0438\u0432\u0430\u0435\u0442\u0441\u044F \u0442\u043E\u043B\u044C\u043A\u043E \u043F\u043E\u0441\u043B\u0435 \u043F\u043E\u043F\u044B\u0442\u043A\u0438 \u0441\u043E\u0437\u0434\u0430\u0442\u044C \u0437\u0430\u044F\u0432\u043A\u0443. -->\n <div class=\"flex flex-col gap-1\">\n @let file = form.controls.file.value;\n @let rejected = file | tuiFileRejected: { accept } | async;\n @if (!file) {\n <label tuiInputFiles>\n <input\n tuiInputFiles\n formControlName=\"file\"\n [accept]=\"accept\"\n [invalid]=\"submitAttempted() ? null : false\"\n />\n </label>\n }\n <tui-files>\n @if (file) {\n <tui-file\n [state]=\"rejected ? 'error' : 'normal'\"\n [file]=\"rejected ?? file\"\n (remove)=\"removeFile()\"\n />\n }\n </tui-files>\n <span class=\"text-body-s text-tui-text-02\">\u0424\u0430\u0439\u043B Excel, \u0432\u044B\u0433\u0440\u0443\u0436\u0435\u043D\u043D\u044B\u0439 \u0438\u0437 \u043F\u0440\u043E\u0433\u0440\u0430\u043C\u043C\u044B \u00AB\u041F\u0440\u043E\u0444\u0441\u0442\u0440\u043E\u0439\u00BB: {{ accept }}</span>\n @if (!rejected) {\n @let fileErrorVisible = submitAttempted() || !form.controls.file.hasError('required');\n <tui-error\n formControlName=\"file\"\n [error]=\"fileErrorVisible ? (['required', 'serverResponse'] | tuiFieldError | async) : null\"\n />\n }\n </div>\n\n <!-- \u041D\u0430\u0437\u0432\u0430\u043D\u0438\u0435 \u0437\u0430\u044F\u0432\u043A\u0438. -->\n <label tuiLabel>\n \u041D\u0430\u0437\u0432\u0430\u043D\u0438\u0435 \u0437\u0430\u044F\u0432\u043A\u0438\n <tui-textfield>\n <input\n tuiTextfield\n formControlName=\"name\"\n placeholder=\"\u041D\u0430\u043F\u0440\u0438\u043C\u0435\u0440: \u0411\u0430\u043B\u043A\u043E\u043D\u044B, \u043E\u0431\u044A\u0435\u043A\u0442 \u043D\u0430 \u041B\u0435\u043D\u0438\u043D\u0430\"\n autocomplete=\"off\"\n [maxLength]=\"nameMaxLength\"\n />\n </tui-textfield>\n <tui-error\n formControlName=\"name\"\n [error]=\"[] | tuiFieldError | async\"\n />\n </label>\n\n @if (form.errors?.['serverResponse']) {\n <tui-error [error]=\"[] | tuiFieldError | async\" />\n }\n\n <tui-notification\n appearance=\"info\"\n size=\"m\"\n >\n \u041E\u0431\u0440\u0430\u0431\u043E\u0442\u043A\u0430 \u0444\u0430\u0439\u043B\u0430 \u0432\u044B\u043F\u043E\u043B\u043D\u044F\u0435\u0442\u0441\u044F \u0432 \u0444\u043E\u043D\u0435: \u043F\u043E\u0437\u0438\u0446\u0438\u0438 \u0438 \u0446\u0435\u043D\u044B \u043F\u043E\u044F\u0432\u044F\u0442\u0441\u044F \u0432 \u0447\u0435\u0440\u043D\u043E\u0432\u0438\u043A\u0435 \u0447\u0435\u0440\u0435\u0437 \u043D\u0435\u043A\u043E\u0442\u043E\u0440\u043E\u0435 \u0432\u0440\u0435\u043C\u044F.\n </tui-notification>\n\n <div class=\"flex flex-wrap gap-2\">\n <button\n tuiButton\n type=\"submit\"\n iconStart=\"@tui.sc.send\"\n [loading]=\"submitting()\"\n >\n \u0421\u043E\u0437\u0434\u0430\u0442\u044C \u0437\u0430\u044F\u0432\u043A\u0443\n </button>\n <button\n tuiButton\n type=\"button\"\n appearance=\"secondary\"\n [disabled]=\"submitting()\"\n (click)=\"context.$implicit.complete()\"\n >\n \u041E\u0442\u043C\u0435\u043D\u0430\n </button>\n </div>\n</form>\n", dependencies: [{ kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1$1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1$1.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: i1$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1$1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1$1.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "component", type: i2$1.TuiFile, selector: "tui-file,a[tuiFile],button[tuiFile]", inputs: ["file", "state", "size", "showDelete", "showSize", "leftContent"], outputs: ["remove"] }, { kind: "component", type: i2$1.TuiInputFiles, selector: "label[tuiInputFiles]" }, { kind: "component", type: i2$1.TuiFilesComponent, selector: "tui-files", inputs: ["max", "expanded"], outputs: ["expandedChange"] }, { kind: "pipe", type: i2$1.TuiFileRejectedPipe, name: "tuiFileRejected" }, { kind: "directive", type: i2$1.TuiInputFilesDirective, selector: "input[tuiInputFiles]", outputs: ["reject"] }, { kind: "directive", type: TuiLabel, selector: "label[tuiLabel]" }, { kind: "component", type: TuiError, selector: "tui-error", inputs: ["error"] }, { kind: "pipe", type: TuiFieldErrorPipe, name: "tuiFieldError" }, { kind: "component", type: i1$2.TuiTextfieldComponent, selector: "tui-textfield:not([multi])" }, { kind: "directive", type: i1$2.TuiTextfieldDirective, selector: "input[tuiTextfield]:not([tuiInputCard]):not([tuiInputExpire]):not([tuiInputCVC])" }, { kind: "directive", type: TuiNotification, selector: "tui-notification,a[tuiNotification],button[tuiNotification]", inputs: ["appearance", "icon", "size"] }, { 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" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
10882
|
+
}
|
|
10883
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: ScProfstroyDraftCreateDialogComponent, decorators: [{
|
|
10884
|
+
type: Component,
|
|
10885
|
+
args: [{ standalone: true, selector: 'sc-profstroy-draft-create-dialog', imports: [ReactiveFormsModule, TuiFiles, TuiFileRejectedPipe, TuiLabel, TuiError, TuiFieldErrorPipe, TuiTextfield, TuiNotification, TuiButton, TuiButtonLoading, AsyncPipe], providers: [
|
|
10886
|
+
{
|
|
10887
|
+
provide: TUI_INPUT_FILE_TEXTS,
|
|
10888
|
+
useFactory: () => inject(TUI_INPUT_FILE_TEXTS, { skipSelf: true }).pipe(map((texts) => ({ ...texts, formatRejectionReason: 'Файл должен быть в формате .xlsx' }))),
|
|
10889
|
+
},
|
|
10890
|
+
], changeDetection: ChangeDetectionStrategy.OnPush, template: "<form\n [formGroup]=\"form\"\n class=\"flex flex-col gap-5\"\n (ngSubmit)=\"submit()\"\n>\n <!-- \u0424\u0430\u0439\u043B. \u041F\u0440\u0438\u0447\u0438\u043D\u0443 \u043E\u0442\u043A\u043B\u043E\u043D\u0435\u043D\u0438\u044F \u0444\u0430\u0439\u043B\u0430 \u043F\u043E\u043A\u0430\u0437\u044B\u0432\u0430\u0435\u0442 \u0441\u0442\u0440\u043E\u043A\u0430 \u0444\u0430\u0439\u043B\u0430, \u043F\u043E\u0434 \u043F\u043E\u043B\u0435\u043C \u043E\u043D\u0430 \u043D\u0435 \u043F\u043E\u0432\u0442\u043E\u0440\u044F\u0435\u0442\u0441\u044F. \u041D\u0435\u0437\u0430\u043F\u043E\u043B\u043D\u0435\u043D\u043D\u043E\u0435 \u043F\u043E\u043B\u0435 \u043F\u043E\u0434\u0441\u0432\u0435\u0447\u0438\u0432\u0430\u0435\u0442\u0441\u044F \u0442\u043E\u043B\u044C\u043A\u043E \u043F\u043E\u0441\u043B\u0435 \u043F\u043E\u043F\u044B\u0442\u043A\u0438 \u0441\u043E\u0437\u0434\u0430\u0442\u044C \u0437\u0430\u044F\u0432\u043A\u0443. -->\n <div class=\"flex flex-col gap-1\">\n @let file = form.controls.file.value;\n @let rejected = file | tuiFileRejected: { accept } | async;\n @if (!file) {\n <label tuiInputFiles>\n <input\n tuiInputFiles\n formControlName=\"file\"\n [accept]=\"accept\"\n [invalid]=\"submitAttempted() ? null : false\"\n />\n </label>\n }\n <tui-files>\n @if (file) {\n <tui-file\n [state]=\"rejected ? 'error' : 'normal'\"\n [file]=\"rejected ?? file\"\n (remove)=\"removeFile()\"\n />\n }\n </tui-files>\n <span class=\"text-body-s text-tui-text-02\">\u0424\u0430\u0439\u043B Excel, \u0432\u044B\u0433\u0440\u0443\u0436\u0435\u043D\u043D\u044B\u0439 \u0438\u0437 \u043F\u0440\u043E\u0433\u0440\u0430\u043C\u043C\u044B \u00AB\u041F\u0440\u043E\u0444\u0441\u0442\u0440\u043E\u0439\u00BB: {{ accept }}</span>\n @if (!rejected) {\n @let fileErrorVisible = submitAttempted() || !form.controls.file.hasError('required');\n <tui-error\n formControlName=\"file\"\n [error]=\"fileErrorVisible ? (['required', 'serverResponse'] | tuiFieldError | async) : null\"\n />\n }\n </div>\n\n <!-- \u041D\u0430\u0437\u0432\u0430\u043D\u0438\u0435 \u0437\u0430\u044F\u0432\u043A\u0438. -->\n <label tuiLabel>\n \u041D\u0430\u0437\u0432\u0430\u043D\u0438\u0435 \u0437\u0430\u044F\u0432\u043A\u0438\n <tui-textfield>\n <input\n tuiTextfield\n formControlName=\"name\"\n placeholder=\"\u041D\u0430\u043F\u0440\u0438\u043C\u0435\u0440: \u0411\u0430\u043B\u043A\u043E\u043D\u044B, \u043E\u0431\u044A\u0435\u043A\u0442 \u043D\u0430 \u041B\u0435\u043D\u0438\u043D\u0430\"\n autocomplete=\"off\"\n [maxLength]=\"nameMaxLength\"\n />\n </tui-textfield>\n <tui-error\n formControlName=\"name\"\n [error]=\"[] | tuiFieldError | async\"\n />\n </label>\n\n @if (form.errors?.['serverResponse']) {\n <tui-error [error]=\"[] | tuiFieldError | async\" />\n }\n\n <tui-notification\n appearance=\"info\"\n size=\"m\"\n >\n \u041E\u0431\u0440\u0430\u0431\u043E\u0442\u043A\u0430 \u0444\u0430\u0439\u043B\u0430 \u0432\u044B\u043F\u043E\u043B\u043D\u044F\u0435\u0442\u0441\u044F \u0432 \u0444\u043E\u043D\u0435: \u043F\u043E\u0437\u0438\u0446\u0438\u0438 \u0438 \u0446\u0435\u043D\u044B \u043F\u043E\u044F\u0432\u044F\u0442\u0441\u044F \u0432 \u0447\u0435\u0440\u043D\u043E\u0432\u0438\u043A\u0435 \u0447\u0435\u0440\u0435\u0437 \u043D\u0435\u043A\u043E\u0442\u043E\u0440\u043E\u0435 \u0432\u0440\u0435\u043C\u044F.\n </tui-notification>\n\n <div class=\"flex flex-wrap gap-2\">\n <button\n tuiButton\n type=\"submit\"\n iconStart=\"@tui.sc.send\"\n [loading]=\"submitting()\"\n >\n \u0421\u043E\u0437\u0434\u0430\u0442\u044C \u0437\u0430\u044F\u0432\u043A\u0443\n </button>\n <button\n tuiButton\n type=\"button\"\n appearance=\"secondary\"\n [disabled]=\"submitting()\"\n (click)=\"context.$implicit.complete()\"\n >\n \u041E\u0442\u043C\u0435\u043D\u0430\n </button>\n </div>\n</form>\n" }]
|
|
10891
|
+
}] });
|
|
10892
|
+
|
|
10893
|
+
/**
|
|
10894
|
+
* Компонент страницы черновика заказа (аналог корзины для одного черновика).
|
|
10895
|
+
*/
|
|
10896
|
+
class ScDraftComponent {
|
|
10897
|
+
/**
|
|
10898
|
+
* Инициализирует экземпляр класса {@link ScDraftComponent}.
|
|
10899
|
+
*/
|
|
10900
|
+
constructor() {
|
|
10901
|
+
/**
|
|
10902
|
+
* Сервис маршрутизации.
|
|
10903
|
+
*/
|
|
10904
|
+
this.router = inject(Router);
|
|
10905
|
+
/**
|
|
10906
|
+
* Сервис предоставляющий доступ к данным о маршруте.
|
|
10907
|
+
*/
|
|
10908
|
+
this.route = inject(ActivatedRoute);
|
|
10909
|
+
/**
|
|
10910
|
+
* Сервис для работы с черновиками заказов.
|
|
10911
|
+
*/
|
|
10912
|
+
this.draftsService = inject(ScOrderDraftsService);
|
|
10913
|
+
/**
|
|
10914
|
+
* Сервис уведомлений.
|
|
10915
|
+
*/
|
|
10916
|
+
this.alerts = inject(TuiAlertService);
|
|
10917
|
+
/**
|
|
10918
|
+
* Сигнал с данными текущего черновика заказа (из параметра orderId в URL).
|
|
10919
|
+
*/
|
|
10920
|
+
this.draft = model(null);
|
|
10921
|
+
/**
|
|
10922
|
+
* Идентификаторы позиций черновика, выбранных для оформления заказа.
|
|
10923
|
+
*/
|
|
10924
|
+
this.selectedItemsIds = signal([]);
|
|
10925
|
+
/**
|
|
10926
|
+
* Источник события удаления или отмены заказа.
|
|
9824
10927
|
*/
|
|
9825
10928
|
this.deleteEvent = output();
|
|
9826
10929
|
/**
|
|
@@ -9831,6 +10934,45 @@ class ScDraftComponent {
|
|
|
9831
10934
|
* Текущий ключ маршрута.
|
|
9832
10935
|
*/
|
|
9833
10936
|
this.isDraftPage = toSignal(this.route.data.pipe(map((data) => data['routes_key'] === ScRouteKeys.draft), distinctUntilChanged()), { initialValue: false });
|
|
10937
|
+
/**
|
|
10938
|
+
* Признак черновика Профстрой.
|
|
10939
|
+
*/
|
|
10940
|
+
this.isProfstroy = computed(() => this.draft()?.isProfstroy === true);
|
|
10941
|
+
/**
|
|
10942
|
+
* Признак, что идёт обработка черновика Профстрой.
|
|
10943
|
+
*/
|
|
10944
|
+
this.isProcessing = computed(() => this.draft()?.actualization?.isProcessing === true);
|
|
10945
|
+
/**
|
|
10946
|
+
* Сервис текущего момента для проверки срока актуализации.
|
|
10947
|
+
*/
|
|
10948
|
+
this.expirationClock = inject(ScDraftExpirationClockService);
|
|
10949
|
+
/**
|
|
10950
|
+
* Текущий момент для проверки срока актуализации из общего источника. Обновляется, пока открыт черновик Профстрой.
|
|
10951
|
+
*/
|
|
10952
|
+
this.now = this.expirationClock.nowWhile(this.isProfstroy);
|
|
10953
|
+
/**
|
|
10954
|
+
* Признак выполнения актуализации или пересборки черновика Профстрой.
|
|
10955
|
+
*/
|
|
10956
|
+
this.actionRunning = signal(false);
|
|
10957
|
+
/**
|
|
10958
|
+
* Признак, что для черновика выполняются или ждут очереди запросы изменения или перечитывания.
|
|
10959
|
+
*/
|
|
10960
|
+
this.queueBusy = toSignal(toObservable(this.draft).pipe(map((draft) => draft?.id), distinctUntilChanged(), switchMap((draftId) => (draftId === undefined ? of(false) : this.draftsService.isBusy$(draftId)))), { initialValue: false });
|
|
10961
|
+
/**
|
|
10962
|
+
* Признак, что действия над черновиком Профстрой недоступны, пока выполняется действие или запрос черновика.
|
|
10963
|
+
*/
|
|
10964
|
+
this.busy = computed(() => this.actionRunning() || this.queueBusy());
|
|
10965
|
+
/**
|
|
10966
|
+
* Признак, что можно менять состав черновика Профстрой.
|
|
10967
|
+
*/
|
|
10968
|
+
this.canEditProfstroy = computed(() => this.isDraftPage() && !this.actionRunning() && !this.isProcessing());
|
|
10969
|
+
/**
|
|
10970
|
+
* Причина, по которой нельзя оформить заказ из черновика Профстрой, или пустая строка.
|
|
10971
|
+
*/
|
|
10972
|
+
this.profstroyOrderBlockReason = computed(() => {
|
|
10973
|
+
const reason = this.draft()?.getOrderBlockReason(this.now()) ?? '';
|
|
10974
|
+
return reason || (this.selectedItemsIds().length === 0 ? 'Выберите позиции для заказа.' : '');
|
|
10975
|
+
});
|
|
9834
10976
|
/**
|
|
9835
10977
|
* Ссылка для автоматического управления уничтожением зависимостей.
|
|
9836
10978
|
*/
|
|
@@ -9839,6 +10981,7 @@ class ScDraftComponent {
|
|
|
9839
10981
|
* Сервис установки SEO-данных приложения.
|
|
9840
10982
|
*/
|
|
9841
10983
|
this.seoService = inject(ScSeoService);
|
|
10984
|
+
this.watchProfstroyProcessing();
|
|
9842
10985
|
}
|
|
9843
10986
|
/** @inheritDoc */
|
|
9844
10987
|
ngOnInit() {
|
|
@@ -9859,7 +11002,32 @@ class ScDraftComponent {
|
|
|
9859
11002
|
this.router.navigate(['orders', 'new', this.draft()?.id, 'draft'], { queryParams: { itemsIds: this.selectedItemsIds() } }).catch(noop);
|
|
9860
11003
|
}
|
|
9861
11004
|
/**
|
|
9862
|
-
*
|
|
11005
|
+
* Запускает актуализацию черновика Профстрой.
|
|
11006
|
+
*/
|
|
11007
|
+
actualizeProfstroy() {
|
|
11008
|
+
this.runProfstroyAction((draftId) => this.draftsService.actualizeProfstroy$(draftId));
|
|
11009
|
+
}
|
|
11010
|
+
/**
|
|
11011
|
+
* Запускает пересборку черновика Профстрой из файла после подтверждения.
|
|
11012
|
+
*/
|
|
11013
|
+
reloadProfstroy() {
|
|
11014
|
+
this.dialogService
|
|
11015
|
+
.open(TUI_CONFIRM, {
|
|
11016
|
+
label: 'Собрать заявку заново?',
|
|
11017
|
+
data: {
|
|
11018
|
+
content: 'Заявка будет собрана заново из загруженного файла. Изменённые количество, метраж и маркировка позиций, а также удалённые позиции будут потеряны. Продолжить?',
|
|
11019
|
+
yes: 'Да, собрать заново',
|
|
11020
|
+
no: 'Отмена',
|
|
11021
|
+
},
|
|
11022
|
+
})
|
|
11023
|
+
.pipe(filter(Boolean), takeUntilDestroyed(this.destroyRef))
|
|
11024
|
+
.subscribe(() => {
|
|
11025
|
+
this.runProfstroyAction((draftId) => this.draftsService.reloadProfstroy$(draftId));
|
|
11026
|
+
});
|
|
11027
|
+
}
|
|
11028
|
+
/**
|
|
11029
|
+
* Удаляет черновик и перенаправляет к списку черновиков. Пока черновик удаляется, действия над ним заблокированы.
|
|
11030
|
+
* Если удалить не удалось, пользователь остаётся на странице, сообщение об ошибке показывает перехватчик приложения.
|
|
9863
11031
|
*/
|
|
9864
11032
|
clearDraft() {
|
|
9865
11033
|
const { id } = this.draft() ?? {};
|
|
@@ -9875,29 +11043,86 @@ class ScDraftComponent {
|
|
|
9875
11043
|
no: 'Нет, оставить',
|
|
9876
11044
|
},
|
|
9877
11045
|
})
|
|
9878
|
-
.pipe(filter(Boolean), switchMap(() =>
|
|
9879
|
-
.
|
|
9880
|
-
this.
|
|
9881
|
-
|
|
11046
|
+
.pipe(filter(Boolean), switchMap(() => {
|
|
11047
|
+
this.actionRunning.set(true);
|
|
11048
|
+
return this.draftsService.delete$(id).pipe(finalize(() => {
|
|
11049
|
+
this.actionRunning.set(false);
|
|
11050
|
+
}));
|
|
11051
|
+
}), takeUntilDestroyed(this.destroyRef))
|
|
11052
|
+
.subscribe({
|
|
11053
|
+
next: () => {
|
|
11054
|
+
this.deleteEvent.emit();
|
|
11055
|
+
this.router.navigate(['/drafts']).catch(noop);
|
|
11056
|
+
},
|
|
11057
|
+
error: noop,
|
|
9882
11058
|
});
|
|
9883
11059
|
}
|
|
11060
|
+
/**
|
|
11061
|
+
* Выполняет действие над черновиком Профстрой. Пока действие выполняется, остальные заблокированы.
|
|
11062
|
+
* Ответ попадает в кэш черновика, после отказа 403 или 409 сервис сам перезапрашивает черновик. Сообщение об ошибке показывает перехватчик приложения.
|
|
11063
|
+
*
|
|
11064
|
+
* @param action Функция, возвращающая запрос действия по идентификатору черновика.
|
|
11065
|
+
*/
|
|
11066
|
+
runProfstroyAction(action) {
|
|
11067
|
+
const draftId = this.draft()?.id;
|
|
11068
|
+
if (draftId === undefined || this.busy()) {
|
|
11069
|
+
return;
|
|
11070
|
+
}
|
|
11071
|
+
this.actionRunning.set(true);
|
|
11072
|
+
action(draftId)
|
|
11073
|
+
.pipe(finalize(() => {
|
|
11074
|
+
this.actionRunning.set(false);
|
|
11075
|
+
}), takeUntilDestroyed(this.destroyRef))
|
|
11076
|
+
.subscribe({ error: noop });
|
|
11077
|
+
}
|
|
11078
|
+
/**
|
|
11079
|
+
* Пока черновик Профстрой на его странице в обработке, опрашивает состояние лёгким запросом списка и после окончания обработки перезапрашивает черновик.
|
|
11080
|
+
* Опрос идёт, пока перезапрошенный черновик остаётся в обработке: неудачный перезапрос повторяется на следующем шаге.
|
|
11081
|
+
* Если черновика больше нет (удалён в другой вкладке), опрос завершается, а пользователь возвращается к списку. В списке черновиков состояние опрашивает сам список.
|
|
11082
|
+
*/
|
|
11083
|
+
watchProfstroyProcessing() {
|
|
11084
|
+
toObservable(computed(() => (this.isDraftPage() && this.isProcessing() ? this.draft()?.id : undefined)))
|
|
11085
|
+
.pipe(distinctUntilChanged(), switchMap((draftId) => draftId === undefined
|
|
11086
|
+
? EMPTY
|
|
11087
|
+
: this.draftsService.watchShortList$([draftId]).pipe(filter((drafts) => !drafts.some((short) => short.id === draftId && short.actualization?.isProcessing)), exhaustMap(() => this.draftsService.fetch$(draftId).pipe(catchError((error) => (ScApiErrorHelper.isNotFound(error) ? of(null) : EMPTY)))), takeWhile((draft) => draft?.actualization?.isProcessing === true, true), filter((draft) => draft === null), map(() => draftId))), takeUntilDestroyed(this.destroyRef))
|
|
11088
|
+
.subscribe((draftId) => {
|
|
11089
|
+
this.leaveMissingDraft(draftId);
|
|
11090
|
+
});
|
|
11091
|
+
}
|
|
11092
|
+
/**
|
|
11093
|
+
* Сообщает, что черновик не найден, и возвращает к списку черновиков.
|
|
11094
|
+
*
|
|
11095
|
+
* @param draftId Идентификатор черновика.
|
|
11096
|
+
*/
|
|
11097
|
+
leaveMissingDraft(draftId) {
|
|
11098
|
+
this.alerts.open(`Черновик №${draftId} не найден. Возможно, он удалён.`, { appearance: 'warning' }).subscribe();
|
|
11099
|
+
this.router.navigate(['/drafts']).catch(noop);
|
|
11100
|
+
}
|
|
9884
11101
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: ScDraftComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
9885
11102
|
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.14", type: ScDraftComponent, isStandalone: true, selector: "sc-draft", inputs: { draft: { classPropertyName: "draft", publicName: "draft", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { draft: "draftChange", deleteEvent: "deleteEvent" }, providers: [
|
|
9886
11103
|
{
|
|
9887
11104
|
provide: SC_ORDER_LOADER,
|
|
9888
11105
|
useExisting: ScOrderDraftsService,
|
|
9889
11106
|
},
|
|
9890
|
-
], ngImport: i0, template: "@let items = draft()?.products;\n\n@if (items) {\n
|
|
11107
|
+
], ngImport: i0, template: "@let currentDraft = draft();\n\n@if (currentDraft && currentDraft.isProfstroy) {\n <div class=\"flex flex-col gap-5\">\n <sc-profstroy-draft-badges\n [actualization]=\"currentDraft.actualization\"\n [now]=\"now()\"\n />\n\n <sc-profstroy-actualization-panel\n [draft]=\"currentDraft\"\n [now]=\"now()\"\n [busy]=\"busy() || !isDraftPage()\"\n (actualize)=\"actualizeProfstroy()\"\n (reload)=\"reloadProfstroy()\"\n />\n\n @if (currentDraft.products.length) {\n <sc-order-items-list\n scOrder\n [orderId]=\"currentDraft.id\"\n [items]=\"currentDraft.products\"\n [grouped]=\"true\"\n [markUnavailable]=\"true\"\n [useLinePrice]=\"true\"\n [measuredInDialog]=\"true\"\n [editable]=\"canEditProfstroy()\"\n [selectable]=\"true\"\n deleteConfirmationTarget=\"\u0438\u0437 \u0437\u0430\u044F\u0432\u043A\u0438\"\n totalLabel=\"\u0418\u0422\u041E\u0413\u041E \u041A \u0417\u0410\u041A\u0410\u0417\u0423\"\n (selectedItemsIdsChange)=\"selectedItemsIds.set($event)\"\n />\n } @else {\n <p class=\"text-base font-bold\">{{ isProcessing() ? '\u041F\u043E\u0437\u0438\u0446\u0438\u0438 \u043F\u043E\u044F\u0432\u044F\u0442\u0441\u044F \u043F\u043E\u0441\u043B\u0435 \u043E\u0431\u0440\u0430\u0431\u043E\u0442\u043A\u0438 \u0444\u0430\u0439\u043B\u0430' : '\u0412 \u0437\u0430\u044F\u0432\u043A\u0435 \u043D\u0435\u0442 \u043F\u043E\u0437\u0438\u0446\u0438\u0439' }}</p>\n }\n\n <div class=\"flex flex-col items-center gap-x-2.5 gap-y-4 sm:flex-row sm:flex-wrap-reverse sm:items-start\">\n <button\n tuiButton\n appearance=\"secondary\"\n iconStart=\"@tui.trash-2\"\n [disabled]=\"busy()\"\n (click)=\"clearDraft()\"\n >\n \u0423\u0434\u0430\u043B\u0438\u0442\u044C \u0447\u0435\u0440\u043D\u043E\u0432\u0438\u043A\n </button>\n\n <div class=\"flex flex-col items-center gap-1 sm:ml-auto sm:items-end\">\n <button\n tuiButton\n type=\"button\"\n iconStart=\"@tui.shopping-cart\"\n [disabled]=\"busy() || !!profstroyOrderBlockReason()\"\n (click)=\"goToNewOrder()\"\n >\n \u041E\u0444\u043E\u0440\u043C\u0438\u0442\u044C \u0437\u0430\u043A\u0430\u0437\n </button>\n @if (profstroyOrderBlockReason(); as reason) {\n <span class=\"text-body-s text-tui-text-02\">{{ reason }}</span>\n }\n </div>\n </div>\n </div>\n} @else {\n @let items = draft()?.products;\n\n @if (items) {\n <div class=\"flex flex-col gap-12\">\n <div class=\"flex flex-col gap-5\">\n @if (items.length) {\n <sc-order-items-list-with-actions\n scOrder\n [orderId]=\"draft()?.id\"\n [items]=\"items\"\n [editable]=\"isDraftPage()\"\n deleteConfirmationTarget=\"\u0438\u0437 \u0447\u0435\u0440\u043D\u043E\u0432\u0438\u043A\u0430\"\n (selectedItemsIdsChange)=\"selectedItemsIds.set($event)\"\n >\n <div\n footerActions\n class=\"flex w-full flex-col items-center gap-x-2.5 gap-y-4 sm:flex-row sm:flex-wrap sm:items-start\"\n >\n <button\n tuiButton\n appearance=\"secondary\"\n (click)=\"clearDraft()\"\n iconStart=\"@tui.trash-2\"\n >\n \u0423\u0434\u0430\u043B\u0438\u0442\u044C \u0447\u0435\u0440\u043D\u043E\u0432\u0438\u043A\n </button>\n\n <button\n tuiButton\n type=\"button\"\n class=\"sm:ml-auto\"\n iconStart=\"@tui.shopping-cart\"\n [disabled]=\"selectedItemsIds().length === 0\"\n (click)=\"goToNewOrder()\"\n >\n \u0417\u0430\u043A\u0430\u0437\u0430\u0442\u044C\n </button>\n </div>\n </sc-order-items-list-with-actions>\n } @else {\n <p class=\"text-base font-bold\">\u0427\u0435\u0440\u043D\u043E\u0432\u0438\u043A \u043F\u0443\u0441\u0442.</p>\n <div\n footerActions\n class=\"flex gap-2\"\n >\n <button\n tuiButton\n appearance=\"secondary\"\n (click)=\"clearDraft()\"\n iconStart=\"@tui.trash-2\"\n >\n \u0423\u0434\u0430\u043B\u0438\u0442\u044C \u0447\u0435\u0440\u043D\u043E\u0432\u0438\u043A\n </button>\n </div>\n }\n </div>\n </div>\n } @else {\n <div class=\"flex flex-col gap-12\">\n <div class=\"flex flex-col gap-5\">\n @for (item of [1, 2, 3, 4]; track item) {\n <div class=\"tui-skeleton flex h-[8.25rem] w-full overflow-hidden rounded-xl\"></div>\n }\n <div class=\"flex gap-2\">\n <div class=\"tui-skeleton h-10 w-40 rounded-md\"></div>\n <div class=\"tui-skeleton h-10 w-36 rounded-md\"></div>\n </div>\n </div>\n </div>\n }\n}\n", dependencies: [{ kind: "component", type: ScOrderItemsListWithActionsComponent, selector: "sc-order-items-list-with-actions", inputs: ["items", "selectable", "editable", "useLinePrice", "deleteConfirmationTarget", "totalLabel", "selectedItemsIds"], outputs: ["selectedItemsIdsChange"] }, { kind: "directive", type: ScOrderAccessorDirective, selector: "[scOrder]", inputs: ["orderId"] }, { kind: "component", type: ScProfstroyActualizationPanelComponent, selector: "sc-profstroy-actualization-panel", inputs: ["draft", "now", "busy"], outputs: ["actualize", "reload"] }, { kind: "component", type: ScProfstroyDraftBadgesComponent, selector: "sc-profstroy-draft-badges", inputs: ["actualization", "now"] }, { kind: "component", type: ScOrderItemsListComponent, selector: "sc-order-items-list", inputs: ["selectable", "editable", "grouped", "markUnavailable", "useLinePrice", "measuredInDialog", "deleteConfirmationTarget", "totalLabel", "items"], outputs: ["selectedItemsIdsChange"] }, { kind: "directive", type: TuiButton, selector: "a[tuiButton],button[tuiButton],a[tuiIconButton],button[tuiIconButton]", inputs: ["size"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
9891
11108
|
}
|
|
9892
11109
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: ScDraftComponent, decorators: [{
|
|
9893
11110
|
type: Component,
|
|
9894
|
-
args: [{ standalone: true, selector: 'sc-draft', imports: [
|
|
11111
|
+
args: [{ standalone: true, selector: 'sc-draft', imports: [
|
|
11112
|
+
ScOrderItemsListWithActionsComponent,
|
|
11113
|
+
ScOrderAccessorDirective,
|
|
11114
|
+
ScProfstroyActualizationPanelComponent,
|
|
11115
|
+
ScProfstroyDraftBadgesComponent,
|
|
11116
|
+
ScOrderItemsListComponent,
|
|
11117
|
+
TuiButton,
|
|
11118
|
+
TuiIcons,
|
|
11119
|
+
], providers: [
|
|
9895
11120
|
{
|
|
9896
11121
|
provide: SC_ORDER_LOADER,
|
|
9897
11122
|
useExisting: ScOrderDraftsService,
|
|
9898
11123
|
},
|
|
9899
|
-
], changeDetection: ChangeDetectionStrategy.OnPush, template: "@let items = draft()?.products;\n\n@if (items) {\n
|
|
9900
|
-
}] });
|
|
11124
|
+
], changeDetection: ChangeDetectionStrategy.OnPush, template: "@let currentDraft = draft();\n\n@if (currentDraft && currentDraft.isProfstroy) {\n <div class=\"flex flex-col gap-5\">\n <sc-profstroy-draft-badges\n [actualization]=\"currentDraft.actualization\"\n [now]=\"now()\"\n />\n\n <sc-profstroy-actualization-panel\n [draft]=\"currentDraft\"\n [now]=\"now()\"\n [busy]=\"busy() || !isDraftPage()\"\n (actualize)=\"actualizeProfstroy()\"\n (reload)=\"reloadProfstroy()\"\n />\n\n @if (currentDraft.products.length) {\n <sc-order-items-list\n scOrder\n [orderId]=\"currentDraft.id\"\n [items]=\"currentDraft.products\"\n [grouped]=\"true\"\n [markUnavailable]=\"true\"\n [useLinePrice]=\"true\"\n [measuredInDialog]=\"true\"\n [editable]=\"canEditProfstroy()\"\n [selectable]=\"true\"\n deleteConfirmationTarget=\"\u0438\u0437 \u0437\u0430\u044F\u0432\u043A\u0438\"\n totalLabel=\"\u0418\u0422\u041E\u0413\u041E \u041A \u0417\u0410\u041A\u0410\u0417\u0423\"\n (selectedItemsIdsChange)=\"selectedItemsIds.set($event)\"\n />\n } @else {\n <p class=\"text-base font-bold\">{{ isProcessing() ? '\u041F\u043E\u0437\u0438\u0446\u0438\u0438 \u043F\u043E\u044F\u0432\u044F\u0442\u0441\u044F \u043F\u043E\u0441\u043B\u0435 \u043E\u0431\u0440\u0430\u0431\u043E\u0442\u043A\u0438 \u0444\u0430\u0439\u043B\u0430' : '\u0412 \u0437\u0430\u044F\u0432\u043A\u0435 \u043D\u0435\u0442 \u043F\u043E\u0437\u0438\u0446\u0438\u0439' }}</p>\n }\n\n <div class=\"flex flex-col items-center gap-x-2.5 gap-y-4 sm:flex-row sm:flex-wrap-reverse sm:items-start\">\n <button\n tuiButton\n appearance=\"secondary\"\n iconStart=\"@tui.trash-2\"\n [disabled]=\"busy()\"\n (click)=\"clearDraft()\"\n >\n \u0423\u0434\u0430\u043B\u0438\u0442\u044C \u0447\u0435\u0440\u043D\u043E\u0432\u0438\u043A\n </button>\n\n <div class=\"flex flex-col items-center gap-1 sm:ml-auto sm:items-end\">\n <button\n tuiButton\n type=\"button\"\n iconStart=\"@tui.shopping-cart\"\n [disabled]=\"busy() || !!profstroyOrderBlockReason()\"\n (click)=\"goToNewOrder()\"\n >\n \u041E\u0444\u043E\u0440\u043C\u0438\u0442\u044C \u0437\u0430\u043A\u0430\u0437\n </button>\n @if (profstroyOrderBlockReason(); as reason) {\n <span class=\"text-body-s text-tui-text-02\">{{ reason }}</span>\n }\n </div>\n </div>\n </div>\n} @else {\n @let items = draft()?.products;\n\n @if (items) {\n <div class=\"flex flex-col gap-12\">\n <div class=\"flex flex-col gap-5\">\n @if (items.length) {\n <sc-order-items-list-with-actions\n scOrder\n [orderId]=\"draft()?.id\"\n [items]=\"items\"\n [editable]=\"isDraftPage()\"\n deleteConfirmationTarget=\"\u0438\u0437 \u0447\u0435\u0440\u043D\u043E\u0432\u0438\u043A\u0430\"\n (selectedItemsIdsChange)=\"selectedItemsIds.set($event)\"\n >\n <div\n footerActions\n class=\"flex w-full flex-col items-center gap-x-2.5 gap-y-4 sm:flex-row sm:flex-wrap sm:items-start\"\n >\n <button\n tuiButton\n appearance=\"secondary\"\n (click)=\"clearDraft()\"\n iconStart=\"@tui.trash-2\"\n >\n \u0423\u0434\u0430\u043B\u0438\u0442\u044C \u0447\u0435\u0440\u043D\u043E\u0432\u0438\u043A\n </button>\n\n <button\n tuiButton\n type=\"button\"\n class=\"sm:ml-auto\"\n iconStart=\"@tui.shopping-cart\"\n [disabled]=\"selectedItemsIds().length === 0\"\n (click)=\"goToNewOrder()\"\n >\n \u0417\u0430\u043A\u0430\u0437\u0430\u0442\u044C\n </button>\n </div>\n </sc-order-items-list-with-actions>\n } @else {\n <p class=\"text-base font-bold\">\u0427\u0435\u0440\u043D\u043E\u0432\u0438\u043A \u043F\u0443\u0441\u0442.</p>\n <div\n footerActions\n class=\"flex gap-2\"\n >\n <button\n tuiButton\n appearance=\"secondary\"\n (click)=\"clearDraft()\"\n iconStart=\"@tui.trash-2\"\n >\n \u0423\u0434\u0430\u043B\u0438\u0442\u044C \u0447\u0435\u0440\u043D\u043E\u0432\u0438\u043A\n </button>\n </div>\n }\n </div>\n </div>\n } @else {\n <div class=\"flex flex-col gap-12\">\n <div class=\"flex flex-col gap-5\">\n @for (item of [1, 2, 3, 4]; track item) {\n <div class=\"tui-skeleton flex h-[8.25rem] w-full overflow-hidden rounded-xl\"></div>\n }\n <div class=\"flex gap-2\">\n <div class=\"tui-skeleton h-10 w-40 rounded-md\"></div>\n <div class=\"tui-skeleton h-10 w-36 rounded-md\"></div>\n </div>\n </div>\n </div>\n }\n}\n" }]
|
|
11125
|
+
}], ctorParameters: () => [] });
|
|
9901
11126
|
|
|
9902
11127
|
/**
|
|
9903
11128
|
* Компонент статуса оплаты заказа.
|
|
@@ -12006,5 +13231,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
|
|
|
12006
13231
|
* Generated bundle index. Do not edit.
|
|
12007
13232
|
*/
|
|
12008
13233
|
|
|
12009
|
-
export { AbstractScPriceCard, AuthMethod, CURRENT_COUNTRY_ID, CostWithDiscountComponent, FilesAndDocumentsComponent, FilesAndDocumentsModule, FinishDateTimeTransformerDirective, IDLE_ITEM_STATE, IS_DEFAULT_COUNTRY, MAX_FILES_IN_FORM_INPUT, SC_ALLOW_SELECT_TERMINATED, SC_BANNER_DURATION, SC_BLOCK_INCOMPLETE_ADDRESS, SC_CATALOG_FILTER_PREVIEWS, SC_CATALOG_PRODUCTS_FILTERS, SC_CATALOG_SHOW_PRODUCTS_RECURSIVELY, SC_DATE_FORMATTER, SC_DEBOUNCE_TIME_DEFAULT, SC_DIALOG_SERVICE_TOKEN, SC_ERROR_CHANGE_HANDLER, SC_FAQ_GROUP_QUERY_PARAM, SC_FAQ_QUESTION_ANCHOR_PREFIX, SC_HELP_NOTIFICATION_CLOSE, SC_HELP_NOTIFICATION_LIMIT, SC_HIDDEN_PRINT_ELEMENTS, SC_MANAGER_QR_HANDLER, SC_NOTIFY_WHEN_IN_STOCK_REQUIRED_FIELDS, SC_ORDER_FORM_CONFIG, SC_ORDER_OPTIONS, 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, ScAddressQualityDirective, ScAddressQualityService, ScAddressesSelectionFieldComponent, ScAuthModule, ScBannerComponent, ScBannerModule, ScBrandsListComponent, ScBrandsListModule, ScCartAddProductsFromCsvDialogComponent, ScCatalogFilterCollapsibleComponent, ScCatalogFiltersComponent, ScCatalogModule, ScCategoryCardComponent, ScContactsAccordionComponent, ScContactsModule, ScContragentsAccordionComponent, ScContragentsAccordionItemComponent, ScContragentsModule, ScDeliveryAddressAccordionComponent, ScDeliveryAddressAccordionItemComponent, ScDeliveryAddressModule, ScDownloadPriceListComponent, ScDraftComponent, ScEmailLinkDirective, ScErrorBlockStatusComponent, ScErrorHandlerComponent, ScFavoriteButtonComponent, ScFeedbackFormComponent, ScFocusFirstInvalidFieldDirective, ScFormFieldsModule, ScFormatDatePipe, ScFrequentlyAskedQuestionsComponent, ScFrequentlyAskedQuestionsGroupSelectorComponent, ScFrequentlyAskedQuestionsWithGroupsComponent, ScGratitudeComponent, ScHelpNotificationService, ScHoverImageCarouselComponent, ScInputQuantityComponent, ScLinks, ScManagerCardComponent, ScManagerCardPushComponent, ScNewContactFormComponent, ScNewContragentBankAccountsFormComponent, ScNewContragentFormComponent, ScNewOrderItemsComponent, ScNewsCardComponent, ScNewsCardSkeletonComponent, ScNewsListComponent, ScNewsModule, ScNextInputFocusDirective, ScNextInputFocusModule, ScNoindexDirective, ScNoindexWrapperComponent, ScNotifyWhenInStockDialogComponent, ScOrderAccessorDirective, ScOrderFormComponent, ScOrderItemComponent, ScOrderItemsListByStockComponent, ScOrderItemsListComponent, ScOrderItemsListWithActionsComponent, ScOrderModule, ScPaginationLinksComponent, ScPaymentStatusComponent, ScPersonalDataProcessingPolicyComponent, ScPhoneFormatPipe, ScPreviewSampleComponent, ScPreviewSampleModule, ScPreviewSamplesMosquitoComponent, ScPriceCardComponent, ScPriceCardInlineComponent, ScPriceHistoryComponent, ScPriceListPaginationComponent, ScPriceWarehouseStockComponent, ScPrintDirective, ScPrintService, ScPrivacyPolicyComponent, ScProductInAllWarehousesPipe, ScProfileAccordionsContentComponent, ScProfileModule, ScPublicOfferComponent, ScQRCodeDialogComponent, ScQRCodeModule, ScResetUserPasswordComponent, ScResourcePreviewComponent, ScSandwichComponent, ScSandwichM2Component, ScSandwichM2SkeletonComponent, ScSandwichSkeletonComponent, ScSelectOnFocusinDirective, ScSeoTagsComponent, ScShareButtonComponent, ScShareDialogComponent, ScShareMessengersLinksComponent, ScShareService, ScSignInFormByEmailComponent, ScSignInFormByPhoneComponent, ScSignInFormComponent, ScSignUpFormComponent, ScSimpleSignUpFormComponent, ScSuggestionFieldComponent, ScTelLinkDirective, ScTerminalLinkDirective, ScUpdateUserInfoDialogComponent, ScUserManagersComponent, ScUserModule, ScUserPhoneApproveDialogComponent, ScVerificationModule, ScVerificationPhoneCheckFormComponent, TreeDirective, TreeIconService, TreeLoaderService, TreeTopDirective, cleanupRecaptchaArtifacts, phoneValidator, scAtLeastOneRequiredValidator, scBicValidator, scClientUiIconsName, scCorrespondentAccountValidator, scGetCurrentRoute, scGetFaqGroupQueryParams, scGetFaqQuestionAnchor, scGetFaqQuestionIdFromAnchor, scPasswordConfirmMatchingValidator, stepValidator, tuiDateValueTransformerDefaultProvider };
|
|
13234
|
+
export { AbstractScPriceCard, AuthMethod, CURRENT_COUNTRY_ID, CostWithDiscountComponent, FilesAndDocumentsComponent, FilesAndDocumentsModule, FinishDateTimeTransformerDirective, IDLE_ITEM_STATE, IS_DEFAULT_COUNTRY, MAX_FILES_IN_FORM_INPUT, SC_ALLOW_SELECT_TERMINATED, SC_BANNER_DURATION, SC_BLOCK_INCOMPLETE_ADDRESS, SC_CATALOG_FILTER_PREVIEWS, SC_CATALOG_PRODUCTS_FILTERS, SC_CATALOG_SHOW_PRODUCTS_RECURSIVELY, SC_DATE_FORMATTER, SC_DEBOUNCE_TIME_DEFAULT, SC_DIALOG_SERVICE_TOKEN, SC_ERROR_CHANGE_HANDLER, SC_FAQ_GROUP_QUERY_PARAM, SC_FAQ_QUESTION_ANCHOR_PREFIX, SC_HELP_NOTIFICATION_CLOSE, SC_HELP_NOTIFICATION_LIMIT, SC_HIDDEN_PRINT_ELEMENTS, SC_MANAGER_QR_HANDLER, SC_NOTIFY_WHEN_IN_STOCK_REQUIRED_FIELDS, SC_ORDER_FORM_CONFIG, SC_ORDER_OPTIONS, 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, ScAddressQualityDirective, ScAddressQualityService, ScAddressesSelectionFieldComponent, ScAuthModule, ScBannerComponent, ScBannerModule, ScBrandsListComponent, ScBrandsListModule, ScCartAddProductsFromCsvDialogComponent, ScCatalogFilterCollapsibleComponent, ScCatalogFiltersComponent, ScCatalogModule, ScCategoryCardComponent, ScConfirmAlertComponent, ScContactsAccordionComponent, ScContactsModule, ScContragentsAccordionComponent, ScContragentsAccordionItemComponent, ScContragentsModule, ScDeliveryAddressAccordionComponent, ScDeliveryAddressAccordionItemComponent, ScDeliveryAddressModule, ScDownloadPriceListComponent, ScDraftComponent, ScDraftExpirationClockService, ScDraftListHelper, ScDraftListUpdatesService, ScEmailLinkDirective, ScErrorBlockStatusComponent, ScErrorHandlerComponent, ScFavoriteButtonComponent, ScFeedbackFormComponent, ScFocusFirstInvalidFieldDirective, ScFormFieldsModule, ScFormatDatePipe, ScFrequentlyAskedQuestionsComponent, ScFrequentlyAskedQuestionsGroupSelectorComponent, ScFrequentlyAskedQuestionsWithGroupsComponent, ScGratitudeComponent, ScHelpNotificationService, ScHoverImageCarouselComponent, ScInputQuantityComponent, ScLinks, ScManagerCardComponent, ScManagerCardPushComponent, ScNewContactFormComponent, ScNewContragentBankAccountsFormComponent, ScNewContragentFormComponent, ScNewOrderItemsComponent, ScNewsCardComponent, ScNewsCardSkeletonComponent, ScNewsListComponent, ScNewsModule, ScNextInputFocusDirective, ScNextInputFocusModule, ScNoindexDirective, ScNoindexWrapperComponent, ScNotifyWhenInStockDialogComponent, ScOrderAccessorDirective, ScOrderFormComponent, ScOrderItemComponent, ScOrderItemsListByStockComponent, ScOrderItemsListComponent, ScOrderItemsListWithActionsComponent, ScOrderModule, ScPaginationLinksComponent, ScPaymentStatusComponent, ScPersonalDataProcessingPolicyComponent, ScPhoneFormatPipe, ScPreviewSampleComponent, ScPreviewSampleModule, ScPreviewSamplesMosquitoComponent, ScPriceCardComponent, ScPriceCardInlineComponent, ScPriceHistoryComponent, ScPriceListPaginationComponent, ScPriceWarehouseStockComponent, ScPrintDirective, ScPrintService, ScPrivacyPolicyComponent, ScProductInAllWarehousesPipe, ScProfileAccordionsContentComponent, ScProfileModule, ScProfstroyActualizationPanelComponent, ScProfstroyBadgeComponent, ScProfstroyDraftBadgesComponent, ScProfstroyDraftCreateDialogComponent, ScProfstroyDraftHelper, ScProfstroyDraftState, ScPublicOfferComponent, ScQRCodeDialogComponent, ScQRCodeModule, ScResetUserPasswordComponent, ScResourcePreviewComponent, ScSandwichComponent, ScSandwichM2Component, ScSandwichM2SkeletonComponent, ScSandwichSkeletonComponent, ScSelectOnFocusinDirective, ScSeoTagsComponent, ScShareButtonComponent, ScShareDialogComponent, ScShareMessengersLinksComponent, ScShareService, ScSignInFormByEmailComponent, ScSignInFormByPhoneComponent, ScSignInFormComponent, ScSignUpFormComponent, ScSimpleSignUpFormComponent, ScSuggestionFieldComponent, ScTelLinkDirective, ScTerminalLinkDirective, ScUpdateUserInfoDialogComponent, ScUserManagersComponent, ScUserModule, ScUserPhoneApproveDialogComponent, ScVerificationModule, ScVerificationPhoneCheckFormComponent, TreeDirective, TreeIconService, TreeLoaderService, TreeTopDirective, cleanupRecaptchaArtifacts, phoneValidator, scAtLeastOneRequiredValidator, scBicValidator, scClientUiIconsName, scCorrespondentAccountValidator, scGetCurrentRoute, scGetFaqGroupQueryParams, scGetFaqQuestionAnchor, scGetFaqQuestionIdFromAnchor, scPasswordConfirmMatchingValidator, stepValidator, tuiDateValueTransformerDefaultProvider };
|
|
12010
13235
|
//# sourceMappingURL=snabcentr-client-ui.mjs.map
|