@snabcentr/client-ui 4.21.0 → 4.23.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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/feedback/feedback-form/sc-feedback-form.component.mjs +21 -11
- 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/cleanup-recaptcha-artifacts.mjs +30 -0
- package/esm2022/providers/index.mjs +2 -1
- package/esm2022/providers/sc-date-formatter.mjs +1 -1
- package/esm2022/public-api.mjs +2 -2
- package/esm2022/samples/sc-sample.module.mjs +10 -3
- package/esm2022/services/index.mjs +1 -2
- package/feedback/feedback-form/sc-feedback-form.component.d.ts +1 -1
- package/fesm2022/snabcentr-client-ui.mjs +1399 -477
- 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 +3 -2
- package/pipes/sc-format-date.d.ts +1 -1
- package/providers/cleanup-recaptcha-artifacts.d.ts +7 -0
- package/providers/index.d.ts +1 -0
- package/providers/sc-date-formatter.d.ts +1 -1
- package/public-api.d.ts +1 -1
- package/release_notes.tmp +6 -7
- package/samples/sc-sample.module.d.ts +2 -1
- package/services/index.d.ts +0 -1
- package/styles/tailwind/tailwind.scss +43 -40
- package/esm2022/services/sc-scripts.service.mjs +0 -106
- package/esm2022/smart-captcha/index.mjs +0 -5
- package/esm2022/smart-captcha/interfaces/index.mjs +0 -6
- package/esm2022/smart-captcha/interfaces/sc-smart-captcha-api.mjs +0 -2
- package/esm2022/smart-captcha/interfaces/sc-smart-captcha-event.mjs +0 -2
- package/esm2022/smart-captcha/interfaces/sc-smart-captcha-language.mjs +0 -2
- package/esm2022/smart-captcha/interfaces/sc-smart-captcha-render-params.mjs +0 -2
- package/esm2022/smart-captcha/interfaces/sc-smart-captcha-settings.mjs +0 -2
- package/esm2022/smart-captcha/interfaces/sc-smart-captcha-window.mjs +0 -2
- package/esm2022/smart-captcha/sc-smart-captcha.component.mjs +0 -167
- package/esm2022/smart-captcha/services/index.mjs +0 -2
- package/esm2022/smart-captcha/services/sc-smart-captcha-loader.service.mjs +0 -87
- package/esm2022/smart-captcha/tokens/index.mjs +0 -3
- package/esm2022/smart-captcha/tokens/sc-smart-captcha-load-timeout.token.mjs +0 -8
- package/esm2022/smart-captcha/tokens/sc-smart-captcha-settings.token.mjs +0 -6
- package/services/sc-scripts.service.d.ts +0 -51
- package/smart-captcha/index.d.ts +0 -4
- package/smart-captcha/interfaces/index.d.ts +0 -5
- package/smart-captcha/interfaces/sc-smart-captcha-api.d.ts +0 -36
- package/smart-captcha/interfaces/sc-smart-captcha-event.d.ts +0 -4
- package/smart-captcha/interfaces/sc-smart-captcha-language.d.ts +0 -4
- package/smart-captcha/interfaces/sc-smart-captcha-render-params.d.ts +0 -16
- package/smart-captcha/interfaces/sc-smart-captcha-settings.d.ts +0 -16
- package/smart-captcha/interfaces/sc-smart-captcha-window.d.ts +0 -14
- package/smart-captcha/sc-smart-captcha.component.d.ts +0 -92
- package/smart-captcha/services/index.d.ts +0 -1
- package/smart-captcha/services/sc-smart-captcha-loader.service.d.ts +0 -50
- package/smart-captcha/tokens/index.d.ts +0 -2
- package/smart-captcha/tokens/sc-smart-captcha-load-timeout.token.d.ts +0 -5
- package/smart-captcha/tokens/sc-smart-captcha-settings.token.d.ts +0 -6
|
@@ -1,22 +1,20 @@
|
|
|
1
1
|
import * as i0 from '@angular/core';
|
|
2
|
-
import { InjectionToken, inject, Injectable, NgZone,
|
|
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,
|
|
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,
|
|
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
|
-
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,
|
|
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
|
|
14
|
-
import { DOCUMENT, CommonModule, NgIf, AsyncPipe, NgTemplateOutlet, NgFor, NgClass } from '@angular/common';
|
|
15
|
-
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';
|
|
16
14
|
import * as i1$1 from '@angular/forms';
|
|
17
|
-
import { FormControl, NgControl, FormGroupDirective, FormGroup, Validators, FormArray, FormsModule, ReactiveFormsModule
|
|
15
|
+
import { FormControl, NgControl, FormGroupDirective, FormGroup, Validators, FormArray, FormsModule, ReactiveFormsModule } from '@angular/forms';
|
|
18
16
|
import * as i1$2 from '@taiga-ui/core';
|
|
19
|
-
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';
|
|
20
18
|
import { isValidPhoneNumber } from 'libphonenumber-js/max';
|
|
21
19
|
import * as i4 from '@taiga-ui/legacy/components/primitive-textfield';
|
|
22
20
|
import * as i3 from '@taiga-ui/legacy';
|
|
@@ -26,6 +24,8 @@ import { isNil, isUndefined, isObject, isArray, isString } from 'lodash-es';
|
|
|
26
24
|
import * as i5 from '@taiga-ui/core/components/label';
|
|
27
25
|
import * as i8 from '@maskito/angular';
|
|
28
26
|
import { MaskitoDirective } from '@maskito/angular';
|
|
27
|
+
import * as i2$2 from '@angular/common';
|
|
28
|
+
import { DOCUMENT, CommonModule, NgIf, AsyncPipe, NgTemplateOutlet, NgFor, NgClass } from '@angular/common';
|
|
29
29
|
import * as i2$3 from '@taiga-ui/polymorpheus';
|
|
30
30
|
import { POLYMORPHEUS_CONTEXT, PolymorpheusComponent, PolymorpheusTemplate, PolymorpheusOutlet, injectContext } from '@taiga-ui/polymorpheus';
|
|
31
31
|
import * as i2 from 'angularx-qrcode';
|
|
@@ -55,6 +55,8 @@ import { Clipboard } from '@angular/cdk/clipboard';
|
|
|
55
55
|
import * as i2$5 from '@taiga-ui/experimental/components/expand';
|
|
56
56
|
import * as i3$3 from '@taiga-ui/kit/components/data-list-wrapper';
|
|
57
57
|
import { TuiGroup } from '@taiga-ui/core/directives/group';
|
|
58
|
+
import * as i4$3 from 'ng-recaptcha-2';
|
|
59
|
+
import { RecaptchaFormsModule, RecaptchaModule, RecaptchaComponent } from 'ng-recaptcha-2';
|
|
58
60
|
|
|
59
61
|
/**
|
|
60
62
|
* Поток для закрытия уведомлений помощи.
|
|
@@ -354,6 +356,36 @@ const SC_HIDDEN_PRINT_ELEMENTS = new InjectionToken('SC_HIDDEN_PRINT_ELEMENTS',
|
|
|
354
356
|
providedIn: 'root',
|
|
355
357
|
});
|
|
356
358
|
|
|
359
|
+
/**
|
|
360
|
+
* Селектор iframe reCAPTCHA.
|
|
361
|
+
*/
|
|
362
|
+
const RECAPTCHA_IFRAME_SELECTOR = 'iframe[src*="recaptcha"]';
|
|
363
|
+
/**
|
|
364
|
+
* Селектор хоста reCAPTCHA.
|
|
365
|
+
*/
|
|
366
|
+
const RECAPTCHA_HOST_SELECTOR = 're-captcha';
|
|
367
|
+
/**
|
|
368
|
+
* Освобождает ресурсы reCAPTCHA и удаляет осиротевшие iframe после смены маршрута.
|
|
369
|
+
*
|
|
370
|
+
* @param documentReference Объект документа.
|
|
371
|
+
* @param windowReference Объект окна браузера.
|
|
372
|
+
*/
|
|
373
|
+
function cleanupRecaptchaArtifacts(documentReference, windowReference) {
|
|
374
|
+
const windowWithRecaptcha = windowReference;
|
|
375
|
+
try {
|
|
376
|
+
windowWithRecaptcha.grecaptcha?.reset();
|
|
377
|
+
}
|
|
378
|
+
catch {
|
|
379
|
+
// Ошибки reCAPTCHA не должны блокировать навигацию.
|
|
380
|
+
}
|
|
381
|
+
documentReference.querySelectorAll(RECAPTCHA_IFRAME_SELECTOR).forEach((iframe) => {
|
|
382
|
+
if (iframe.closest(RECAPTCHA_HOST_SELECTOR)) {
|
|
383
|
+
return;
|
|
384
|
+
}
|
|
385
|
+
iframe.remove();
|
|
386
|
+
});
|
|
387
|
+
}
|
|
388
|
+
|
|
357
389
|
/**
|
|
358
390
|
* Сервис по работе с печатью страницы.
|
|
359
391
|
*/
|
|
@@ -399,107 +431,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
|
|
|
399
431
|
}]
|
|
400
432
|
}] });
|
|
401
433
|
|
|
402
|
-
/**
|
|
403
|
-
* Сервис для вставки в {@link Document} внешних и дополнительных скриптов.
|
|
404
|
-
*/
|
|
405
|
-
class ScScriptsService {
|
|
406
|
-
constructor() {
|
|
407
|
-
/**
|
|
408
|
-
* Объект {@link Document}, предоставляющий доступ к DOM страницы.
|
|
409
|
-
*/
|
|
410
|
-
this.document = inject(DOCUMENT);
|
|
411
|
-
/**
|
|
412
|
-
* Объект {@link Renderer2}, предоставляющий доступ к DOM страницы.
|
|
413
|
-
*/
|
|
414
|
-
this.renderer = inject(RendererFactory2).createRenderer(null, null);
|
|
415
|
-
/**
|
|
416
|
-
* Признак того, что скрипт выполняется в браузере.
|
|
417
|
-
*/
|
|
418
|
-
this.isBrowser = inject(IS_BROWSER);
|
|
419
|
-
/**
|
|
420
|
-
* {@link NgZone} для вставки скриптов вне зоны Angular.
|
|
421
|
-
*/
|
|
422
|
-
this.ngZone = inject(NgZone);
|
|
423
|
-
}
|
|
424
|
-
/**
|
|
425
|
-
* Вставляет в конец {@link Document.body} скрипты, заданные строками HTML, вне зоны Angular. Из каждой строки берётся первый `<script>`. На сервере ничего не делает.
|
|
426
|
-
* Принимает только доверенные строки из конфигурации приложения: строка разбирается как HTML и может выполнить код.
|
|
427
|
-
*
|
|
428
|
-
* @param scriptStrings Строки HTML скриптов.
|
|
429
|
-
*/
|
|
430
|
-
appendScripts(scriptStrings) {
|
|
431
|
-
if (!this.isBrowser) {
|
|
432
|
-
return;
|
|
433
|
-
}
|
|
434
|
-
this.ngZone.runOutsideAngular(() => {
|
|
435
|
-
scriptStrings.forEach((scriptString) => {
|
|
436
|
-
const script = this.createScript(scriptString);
|
|
437
|
-
if (script) {
|
|
438
|
-
this.renderer.appendChild(this.document.body, script);
|
|
439
|
-
}
|
|
440
|
-
});
|
|
441
|
-
});
|
|
442
|
-
}
|
|
443
|
-
/**
|
|
444
|
-
* Загружает внешний скрипт: при подписке добавляет его в {@link Document.head}, после загрузки выдаёт элемент скрипта и завершается.
|
|
445
|
-
* Если скрипт не загрузился или подписка отменена раньше, удаляет его со страницы; удаление не отменяет загрузку и выполнение уже вставленного скрипта. На сервере завершается без загрузки.
|
|
446
|
-
*
|
|
447
|
-
* @param url Адрес скрипта.
|
|
448
|
-
*/
|
|
449
|
-
loadScript$(url) {
|
|
450
|
-
if (!this.isBrowser) {
|
|
451
|
-
return EMPTY;
|
|
452
|
-
}
|
|
453
|
-
return new Observable((subscriber) => {
|
|
454
|
-
const script = this.renderer.createElement('script');
|
|
455
|
-
let isLoaded = false;
|
|
456
|
-
const unlistenLoad = this.renderer.listen(script, 'load', () => {
|
|
457
|
-
isLoaded = true;
|
|
458
|
-
subscriber.next(script);
|
|
459
|
-
subscriber.complete();
|
|
460
|
-
});
|
|
461
|
-
const unlistenError = this.renderer.listen(script, 'error', () => {
|
|
462
|
-
subscriber.error(new Error(`Failed to load script ${url}`));
|
|
463
|
-
});
|
|
464
|
-
this.renderer.setProperty(script, 'src', url);
|
|
465
|
-
this.renderer.appendChild(this.document.head, script);
|
|
466
|
-
return () => {
|
|
467
|
-
unlistenLoad();
|
|
468
|
-
unlistenError();
|
|
469
|
-
if (!isLoaded) {
|
|
470
|
-
this.removeScript(script);
|
|
471
|
-
}
|
|
472
|
-
};
|
|
473
|
-
});
|
|
474
|
-
}
|
|
475
|
-
/**
|
|
476
|
-
* Удаляет скрипт со страницы. При подключённых анимациях Angular узел удаляется при ближайшей проверке изменений.
|
|
477
|
-
*
|
|
478
|
-
* @param script Элемент скрипта.
|
|
479
|
-
*/
|
|
480
|
-
removeScript(script) {
|
|
481
|
-
this.renderer.removeChild(script.parentNode, script);
|
|
482
|
-
}
|
|
483
|
-
/**
|
|
484
|
-
* Создаёт {@link HTMLScriptElement} из строки HTML скрипта.
|
|
485
|
-
*
|
|
486
|
-
* @param scriptString Строка HTML скрипта.
|
|
487
|
-
*/
|
|
488
|
-
createScript(scriptString) {
|
|
489
|
-
const range = this.document.createRange();
|
|
490
|
-
const fragment = range.createContextualFragment(scriptString);
|
|
491
|
-
return fragment.querySelector('script');
|
|
492
|
-
}
|
|
493
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: ScScriptsService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
494
|
-
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: ScScriptsService, providedIn: 'root' }); }
|
|
495
|
-
}
|
|
496
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: ScScriptsService, decorators: [{
|
|
497
|
-
type: Injectable,
|
|
498
|
-
args: [{
|
|
499
|
-
providedIn: 'root',
|
|
500
|
-
}]
|
|
501
|
-
}] });
|
|
502
|
-
|
|
503
434
|
/**
|
|
504
435
|
* Проверяет корректность введенного номера телефона.
|
|
505
436
|
*/
|
|
@@ -4460,6 +4391,29 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
|
|
|
4460
4391
|
}]
|
|
4461
4392
|
}] });
|
|
4462
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
|
+
|
|
4463
4417
|
/**
|
|
4464
4418
|
* Баннер с прокруткой переданных {@link TemplateRef} элементов, и баннеров локации.
|
|
4465
4419
|
*/
|
|
@@ -4640,6 +4594,33 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
|
|
|
4640
4594
|
}]
|
|
4641
4595
|
}] });
|
|
4642
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
|
+
|
|
4643
4624
|
/**
|
|
4644
4625
|
* Компонент диалога для добавления товаров в корзину из CSV файла.
|
|
4645
4626
|
*/
|
|
@@ -4682,19 +4663,7 @@ class ScCartAddProductsFromCsvDialogComponent {
|
|
|
4682
4663
|
/**
|
|
4683
4664
|
* {@link Observable} запроса на добавление товаров из CSV файла.
|
|
4684
4665
|
*/
|
|
4685
|
-
this.submitRequest$ = this.onSubmit$.pipe(map(() => this.control.value), filter(tuiIsPresent), switchMap((file) =>
|
|
4686
|
-
const reader = new FileReader();
|
|
4687
|
-
reader.addEventListener('load', () => {
|
|
4688
|
-
observer.next(new ScUploadedFile(file.name, reader.result));
|
|
4689
|
-
});
|
|
4690
|
-
reader.addEventListener('error', (e) => {
|
|
4691
|
-
observer.error(e);
|
|
4692
|
-
});
|
|
4693
|
-
reader.readAsDataURL(file);
|
|
4694
|
-
return () => {
|
|
4695
|
-
reader.abort();
|
|
4696
|
-
};
|
|
4697
|
-
})), 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(() => {
|
|
4698
4667
|
this.context.$implicit.complete();
|
|
4699
4668
|
}), startWith(null))));
|
|
4700
4669
|
/**
|
|
@@ -4970,7 +4939,7 @@ class ScAddOrEditingCartItemDialogComponent {
|
|
|
4970
4939
|
this.context.$implicit.complete();
|
|
4971
4940
|
}), catchError((error) => {
|
|
4972
4941
|
if (error instanceof HttpErrorResponse) {
|
|
4973
|
-
const { errors, message } = error.error;
|
|
4942
|
+
const { errors, message } = (error.error ?? {});
|
|
4974
4943
|
if (errors && isObject(errors)) {
|
|
4975
4944
|
Object.entries(errors).forEach(([k, v]) => {
|
|
4976
4945
|
this.formComponent().form.get(k)?.setErrors({ serverResponse: v });
|
|
@@ -4982,7 +4951,8 @@ class ScAddOrEditingCartItemDialogComponent {
|
|
|
4982
4951
|
this.formComponent().form.updateValueAndValidity();
|
|
4983
4952
|
this.formComponent().form.markAsDirty();
|
|
4984
4953
|
}
|
|
4985
|
-
|
|
4954
|
+
// Значение вместо пустого потока снимает загрузку, чтобы форму можно было отправить снова.
|
|
4955
|
+
return of(null);
|
|
4986
4956
|
}), startWith(loadingPlaceholder))), share());
|
|
4987
4957
|
/**
|
|
4988
4958
|
* {@link Observable} изменения состояния загрузки данных.
|
|
@@ -5320,6 +5290,10 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
|
|
|
5320
5290
|
class ScInputQuantityComponent extends AbstractTuiNullableControl {
|
|
5321
5291
|
constructor() {
|
|
5322
5292
|
super(...arguments);
|
|
5293
|
+
/**
|
|
5294
|
+
* Минимальное количество знаков после запятой.
|
|
5295
|
+
*/
|
|
5296
|
+
this.minPrecision = 2;
|
|
5323
5297
|
/**
|
|
5324
5298
|
* Шаг увеличения количества. Отвечает за проверку кратности ввода.
|
|
5325
5299
|
*/
|
|
@@ -5363,6 +5337,21 @@ class ScInputQuantityComponent extends AbstractTuiNullableControl {
|
|
|
5363
5337
|
get focused() {
|
|
5364
5338
|
return !!this.numberInput?.focused;
|
|
5365
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
|
+
}
|
|
5366
5355
|
/**
|
|
5367
5356
|
* Элемент формы ввода количества.
|
|
5368
5357
|
*/
|
|
@@ -5435,7 +5424,7 @@ class ScInputQuantityComponent extends AbstractTuiNullableControl {
|
|
|
5435
5424
|
this.clickClearEvent.emit();
|
|
5436
5425
|
}
|
|
5437
5426
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: ScInputQuantityComponent, deps: null, target: i0.ɵɵFactoryTarget.Component }); }
|
|
5438
|
-
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 }); }
|
|
5439
5428
|
}
|
|
5440
5429
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: ScInputQuantityComponent, decorators: [{
|
|
5441
5430
|
type: Component,
|
|
@@ -5452,7 +5441,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
|
|
|
5452
5441
|
...TuiHint,
|
|
5453
5442
|
TuiFieldErrorPipe,
|
|
5454
5443
|
TuiLoader,
|
|
5455
|
-
|
|
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"] }]
|
|
5456
5446
|
}], propDecorators: { numberInput: [{
|
|
5457
5447
|
type: ViewChild,
|
|
5458
5448
|
args: [TuiInputNumberComponent]
|
|
@@ -8878,6 +8868,37 @@ class ScOrderItemComponent extends AbstractScPriceCard {
|
|
|
8878
8868
|
* Признак, что необходимо отобразить элементы редактирования.
|
|
8879
8869
|
*/
|
|
8880
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 ? '' : 'Для изменения количества воспользуйтесь конфигуратором';
|
|
8881
8902
|
}
|
|
8882
8903
|
/**
|
|
8883
8904
|
* Отображает спецификацию.
|
|
@@ -8898,13 +8919,13 @@ class ScOrderItemComponent extends AbstractScPriceCard {
|
|
|
8898
8919
|
}
|
|
8899
8920
|
}
|
|
8900
8921
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: ScOrderItemComponent, deps: null, target: i0.ɵɵFactoryTarget.Component }); }
|
|
8901
|
-
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: [
|
|
8902
8923
|
SEARCH_TERM_PROVIDERS,
|
|
8903
8924
|
tuiTextfieldOptionsProvider({
|
|
8904
8925
|
appearance: signal('textfield'),
|
|
8905
8926
|
}),
|
|
8906
8927
|
tuiHintOptionsProvider({ direction: 'top', showDelay: 100, hideDelay: 100 }),
|
|
8907
|
-
], 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 }); }
|
|
8908
8929
|
}
|
|
8909
8930
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: ScOrderItemComponent, decorators: [{
|
|
8910
8931
|
type: Component,
|
|
@@ -8931,6 +8952,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
|
|
|
8931
8952
|
TuiCurrencyPipe,
|
|
8932
8953
|
TuiFormatNumberPipe,
|
|
8933
8954
|
TuiPreview,
|
|
8955
|
+
TuiBadge,
|
|
8934
8956
|
ScNoindexWrapperComponent,
|
|
8935
8957
|
TuiAppearance,
|
|
8936
8958
|
TuiButtonLoading,
|
|
@@ -8941,7 +8963,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
|
|
|
8941
8963
|
appearance: signal('textfield'),
|
|
8942
8964
|
}),
|
|
8943
8965
|
tuiHintOptionsProvider({ direction: 'top', showDelay: 100, hideDelay: 100 }),
|
|
8944
|
-
], 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" }]
|
|
8945
8967
|
}] });
|
|
8946
8968
|
|
|
8947
8969
|
/* eslint-disable no-param-reassign */
|
|
@@ -8949,6 +8971,29 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
|
|
|
8949
8971
|
* Компонент списка товаров корзины, заказа или черновика.
|
|
8950
8972
|
*/
|
|
8951
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
|
+
*/
|
|
8952
8997
|
constructor() {
|
|
8953
8998
|
/**
|
|
8954
8999
|
* Внутренний сигнал списка элементов корзины (товаров).
|
|
@@ -8962,6 +9007,56 @@ class ScOrderItemsListComponent {
|
|
|
8962
9007
|
* Включить редактирование позиций.
|
|
8963
9008
|
*/
|
|
8964
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
|
+
});
|
|
8965
9060
|
/**
|
|
8966
9061
|
* Внутренняя группа формы для выбора позиций.
|
|
8967
9062
|
*/
|
|
@@ -8977,11 +9072,12 @@ class ScOrderItemsListComponent {
|
|
|
8977
9072
|
initialValue: [],
|
|
8978
9073
|
});
|
|
8979
9074
|
/**
|
|
8980
|
-
* Состояние флага «Выбрать все»: `true`, если выбраны все позиции, `false`, если ни
|
|
9075
|
+
* Состояние флага «Выбрать все»: `true`, если выбраны все доступные для выбора позиции, `false`, если ни одной или выбирать нечего,
|
|
9076
|
+
* и `null`, если только часть.
|
|
8981
9077
|
*/
|
|
8982
9078
|
this.allItemsSelected = computed(() => {
|
|
8983
9079
|
const checkedList = this.itemsIds().map(([, checked]) => checked);
|
|
8984
|
-
if (checkedList.every(Boolean)) {
|
|
9080
|
+
if (checkedList.length > 0 && checkedList.every(Boolean)) {
|
|
8985
9081
|
return true;
|
|
8986
9082
|
}
|
|
8987
9083
|
return checkedList.some(Boolean) ? null : false;
|
|
@@ -9006,7 +9102,7 @@ class ScOrderItemsListComponent {
|
|
|
9006
9102
|
const items = this.itemsSignal();
|
|
9007
9103
|
const selectedItemsIds = this.selectedItemsIds();
|
|
9008
9104
|
const selectable = this.selectable();
|
|
9009
|
-
return
|
|
9105
|
+
return ScOrderItem.getCostSum(items.filter((item) => !selectable || selectedItemsIds.includes(item.id)));
|
|
9010
9106
|
});
|
|
9011
9107
|
/**
|
|
9012
9108
|
* Сервис получения и редактирования списка товаров заказа (корзина / заказ / черновик = заказ).
|
|
@@ -9017,6 +9113,10 @@ class ScOrderItemsListComponent {
|
|
|
9017
9113
|
* Признак, что можно редактировать позиции.
|
|
9018
9114
|
*/
|
|
9019
9115
|
this.canEdit = computed(() => this.editable() && 'updateProduct$' in this.orderItemsListLoader);
|
|
9116
|
+
/**
|
|
9117
|
+
* Признак, что группу можно удалить целиком.
|
|
9118
|
+
*/
|
|
9119
|
+
this.canDeleteGroups = computed(() => this.canEdit() && 'deleteProducts$' in this.orderItemsListLoader);
|
|
9020
9120
|
/**
|
|
9021
9121
|
* Настройки вывода сумм в рублях.
|
|
9022
9122
|
*/
|
|
@@ -9030,6 +9130,21 @@ class ScOrderItemsListComponent {
|
|
|
9030
9130
|
* Предоставляется родителем через токен {@link SC_DIALOG_SERVICE_TOKEN}.
|
|
9031
9131
|
*/
|
|
9032
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
|
+
});
|
|
9033
9148
|
}
|
|
9034
9149
|
/**
|
|
9035
9150
|
* Список элементов корзины (товаров). При установке синхронно обновляет контролы выбора в форме.
|
|
@@ -9040,6 +9155,7 @@ class ScOrderItemsListComponent {
|
|
|
9040
9155
|
}
|
|
9041
9156
|
/**
|
|
9042
9157
|
* Синхронизирует контролы формы выбора с текущим списком товаров: удаляет контролы для отсутствующих id, добавляет для новых.
|
|
9158
|
+
* Контролы меняются без событий, а значение группы пересчитывается один раз в конце: на заявках в сотни позиций это заметно.
|
|
9043
9159
|
*/
|
|
9044
9160
|
syncSelectionControls() {
|
|
9045
9161
|
if (!this.selectable()) {
|
|
@@ -9049,70 +9165,125 @@ class ScOrderItemsListComponent {
|
|
|
9049
9165
|
const currentIds = new Set(currentItems.map((item) => item.id));
|
|
9050
9166
|
Object.keys(this.selectionFormGroup.controls).forEach((name) => {
|
|
9051
9167
|
if (!currentIds.has(name)) {
|
|
9052
|
-
this.selectionFormGroup.removeControl(name);
|
|
9168
|
+
this.selectionFormGroup.removeControl(name, { emitEvent: false });
|
|
9053
9169
|
}
|
|
9054
9170
|
});
|
|
9055
9171
|
currentItems.forEach((item) => {
|
|
9056
|
-
|
|
9057
|
-
|
|
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 });
|
|
9058
9184
|
}
|
|
9059
9185
|
});
|
|
9186
|
+
this.selectionFormGroup.updateValueAndValidity();
|
|
9060
9187
|
}
|
|
9061
9188
|
/**
|
|
9062
|
-
* Выбирает все позиции или снимает выбор со всех.
|
|
9189
|
+
* Выбирает все доступные для выбора позиции или снимает выбор со всех.
|
|
9063
9190
|
*
|
|
9064
9191
|
* @param event Событие изменения флага выбора всех позиций.
|
|
9065
9192
|
*/
|
|
9066
9193
|
selectAllChangeHandler(event) {
|
|
9067
9194
|
const { checked } = event.target;
|
|
9068
|
-
|
|
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);
|
|
9069
9216
|
}
|
|
9070
9217
|
/**
|
|
9071
|
-
* Удаляет
|
|
9218
|
+
* Удаляет позицию, при необходимости после подтверждения. Сообщение об ошибке показывает перехватчик приложения.
|
|
9072
9219
|
*
|
|
9073
9220
|
* @param item Позиция товара для удаления.
|
|
9074
|
-
* @param priceCard Компонент карточки товара.
|
|
9075
9221
|
*/
|
|
9076
|
-
deleteItem(item
|
|
9077
|
-
if (!this.canEdit()) {
|
|
9222
|
+
deleteItem(item) {
|
|
9223
|
+
if (!this.canEdit() || this.pendingItemsIds().has(item.id)) {
|
|
9078
9224
|
return;
|
|
9079
9225
|
}
|
|
9080
|
-
|
|
9081
|
-
|
|
9082
|
-
.
|
|
9083
|
-
.pipe(
|
|
9084
|
-
|
|
9085
|
-
|
|
9086
|
-
});
|
|
9087
|
-
return of();
|
|
9088
|
-
}), finalize(() => {
|
|
9089
|
-
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
|
+
}));
|
|
9090
9232
|
}), takeUntilDestroyed(this.destroyRef))
|
|
9091
|
-
.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 });
|
|
9092
9259
|
}
|
|
9093
9260
|
/**
|
|
9094
|
-
* Обновляет количество товара.
|
|
9261
|
+
* Обновляет количество товара. Количество неактивной позиции не меняется. Пустое или нулевое значение возвращается к сохранённому количеству.
|
|
9095
9262
|
*
|
|
9096
9263
|
* @param newQuantity Новое значение количества товара.
|
|
9097
9264
|
* @param priceCard Компонент карточки товара.
|
|
9098
9265
|
*/
|
|
9099
9266
|
patchItem(newQuantity, priceCard) {
|
|
9100
|
-
|
|
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();
|
|
9101
9274
|
return;
|
|
9102
9275
|
}
|
|
9103
|
-
|
|
9276
|
+
this.setItemPending(orderItem.id, true);
|
|
9104
9277
|
this.orderItemsListLoader
|
|
9105
|
-
.updateProduct$(
|
|
9106
|
-
.pipe(
|
|
9107
|
-
|
|
9108
|
-
priceCard.quantityControl.patchValue(priceCard.orderItem.quantity, {
|
|
9109
|
-
emitEvent: false,
|
|
9110
|
-
});
|
|
9111
|
-
return of();
|
|
9112
|
-
}), finalize(() => {
|
|
9113
|
-
priceCard.showLoader.set(false);
|
|
9278
|
+
.updateProduct$(orderItem.id, { quantity: newQuantity }, this.orderOptions?.orderId())
|
|
9279
|
+
.pipe(finalize(() => {
|
|
9280
|
+
this.setItemPending(orderItem.id, false);
|
|
9114
9281
|
}), takeUntilDestroyed(this.destroyRef))
|
|
9115
|
-
.subscribe(
|
|
9282
|
+
.subscribe({
|
|
9283
|
+
error: () => {
|
|
9284
|
+
priceCard.quantityControl.patchValue((priceCard.orderItem ?? orderItem).quantity, { emitEvent: false });
|
|
9285
|
+
},
|
|
9286
|
+
});
|
|
9116
9287
|
}
|
|
9117
9288
|
/**
|
|
9118
9289
|
* Открывает модальное окно описания товара.
|
|
@@ -9127,7 +9298,7 @@ class ScOrderItemsListComponent {
|
|
|
9127
9298
|
}
|
|
9128
9299
|
}
|
|
9129
9300
|
/**
|
|
9130
|
-
* Открывает модальное окно изменения товара или его конфигуратора.
|
|
9301
|
+
* Открывает модальное окно изменения товара или его конфигуратора. Если параметры позиции меняются в диалоге, окно товара открывается и у штучного товара.
|
|
9131
9302
|
*
|
|
9132
9303
|
* @param item Позиция товара.
|
|
9133
9304
|
*/
|
|
@@ -9136,7 +9307,7 @@ class ScOrderItemsListComponent {
|
|
|
9136
9307
|
if (isConfigurator) {
|
|
9137
9308
|
this.scDialogService.openConfiguratorDialog(item.product, item, this.orderOptions?.orderId());
|
|
9138
9309
|
}
|
|
9139
|
-
else if (ScUnitsHelper.productIsMeasurable(item.product)) {
|
|
9310
|
+
else if (this.measuredInDialog() || ScUnitsHelper.productIsMeasurable(item.product)) {
|
|
9140
9311
|
this.scDialogService.openAddOrEditingCartItemDialog(item.product, item, this.orderOptions?.orderId(), true);
|
|
9141
9312
|
}
|
|
9142
9313
|
}
|
|
@@ -9148,8 +9319,66 @@ class ScOrderItemsListComponent {
|
|
|
9148
9319
|
openPriceHistoryChartDialog(product) {
|
|
9149
9320
|
this.scDialogService.openPriceHistoryChartDialog(product);
|
|
9150
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
|
+
}
|
|
9151
9380
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: ScOrderItemsListComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
9152
|
-
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 }); }
|
|
9153
9382
|
}
|
|
9154
9383
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: ScOrderItemsListComponent, decorators: [{
|
|
9155
9384
|
type: Component,
|
|
@@ -9165,8 +9394,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
|
|
|
9165
9394
|
TuiButton,
|
|
9166
9395
|
TuiButtonLoading,
|
|
9167
9396
|
TuiCurrencyPipe,
|
|
9168
|
-
], 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 (
|
|
9169
|
-
}], 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: [{
|
|
9170
9399
|
type: Input,
|
|
9171
9400
|
args: [{ required: true }]
|
|
9172
9401
|
}] } });
|
|
@@ -9191,6 +9420,18 @@ class ScOrderItemsListWithActionsComponent {
|
|
|
9191
9420
|
* Включить редактирование позиций.
|
|
9192
9421
|
*/
|
|
9193
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('Итого');
|
|
9194
9435
|
/**
|
|
9195
9436
|
* Позиции, доступные для заказа.
|
|
9196
9437
|
*/
|
|
@@ -9214,11 +9455,11 @@ class ScOrderItemsListWithActionsComponent {
|
|
|
9214
9455
|
}, { allowSignalWrites: true });
|
|
9215
9456
|
}
|
|
9216
9457
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: ScOrderItemsListWithActionsComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
9217
|
-
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 }); }
|
|
9218
9459
|
}
|
|
9219
9460
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: ScOrderItemsListWithActionsComponent, decorators: [{
|
|
9220
9461
|
type: Component,
|
|
9221
|
-
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" }]
|
|
9222
9463
|
}], ctorParameters: () => [] });
|
|
9223
9464
|
|
|
9224
9465
|
/**
|
|
@@ -9239,20 +9480,28 @@ class ScNewOrderItemsComponent {
|
|
|
9239
9480
|
*/
|
|
9240
9481
|
this.selectable = input(true);
|
|
9241
9482
|
/**
|
|
9242
|
-
*
|
|
9483
|
+
* Признак, что цена за единицу берётся из позиции, а не из текущей цены товара.
|
|
9243
9484
|
*/
|
|
9244
|
-
this.
|
|
9485
|
+
this.useLinePrice = input(false);
|
|
9245
9486
|
/**
|
|
9246
|
-
*
|
|
9487
|
+
* Подпись итоговой суммы.
|
|
9488
|
+
*/
|
|
9489
|
+
this.totalLabel = input('Итого');
|
|
9490
|
+
/**
|
|
9491
|
+
* Время обработки и принятия заказов (из настроек окружения приложения).
|
|
9492
|
+
*/
|
|
9493
|
+
this.orderProcessingAcceptanceTime = input('');
|
|
9494
|
+
/**
|
|
9495
|
+
* Изменение выбора позиций для оформления.
|
|
9247
9496
|
*/
|
|
9248
9497
|
this.selectedItemsIdsChange = output();
|
|
9249
9498
|
}
|
|
9250
9499
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: ScNewOrderItemsComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
9251
|
-
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 }); }
|
|
9252
9501
|
}
|
|
9253
9502
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: ScNewOrderItemsComponent, decorators: [{
|
|
9254
9503
|
type: Component,
|
|
9255
|
-
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" }]
|
|
9256
9505
|
}] });
|
|
9257
9506
|
|
|
9258
9507
|
/**
|
|
@@ -9520,8 +9769,10 @@ class ScOrderFormComponent {
|
|
|
9520
9769
|
}
|
|
9521
9770
|
/**
|
|
9522
9771
|
* Обрабатывает отправку формы: валидирует и эмитит данные заказа родителю.
|
|
9772
|
+
* Общая ошибка сервера с прошлой отправки (например, 409) не мешает отправить форму снова, ошибки полей остаются до их исправления.
|
|
9523
9773
|
*/
|
|
9524
9774
|
submit() {
|
|
9775
|
+
this.form.updateValueAndValidity({ emitEvent: false });
|
|
9525
9776
|
this.form.markAllAsTouched();
|
|
9526
9777
|
if (!this.form.valid) {
|
|
9527
9778
|
return;
|
|
@@ -9539,7 +9790,7 @@ class ScOrderFormComponent {
|
|
|
9539
9790
|
* @param error Ответ сервера с ошибками.
|
|
9540
9791
|
*/
|
|
9541
9792
|
applyServerErrors(error) {
|
|
9542
|
-
const { errors, message } = error.error;
|
|
9793
|
+
const { errors, message } = (error.error ?? {});
|
|
9543
9794
|
if (errors) {
|
|
9544
9795
|
Object.keys(errors).forEach((key) => {
|
|
9545
9796
|
if (key === 'deliveryDate' && isArray(errors[key])) {
|
|
@@ -9812,61 +10063,840 @@ class ScOrderItemsListByStockComponent {
|
|
|
9812
10063
|
*/
|
|
9813
10064
|
this.selectable = input(false);
|
|
9814
10065
|
/**
|
|
9815
|
-
* Включить редактирование позиций.
|
|
10066
|
+
* Включить редактирование позиций.
|
|
10067
|
+
*/
|
|
10068
|
+
this.editable = input(false);
|
|
10069
|
+
/**
|
|
10070
|
+
* Заголовок для секций «под заказ» и «нет на складе». Если не указан — заголовок не выводится.
|
|
10071
|
+
*/
|
|
10072
|
+
this.sectionHeader = input();
|
|
10073
|
+
/**
|
|
10074
|
+
* Выходные данные при изменении выбора позиций.
|
|
10075
|
+
*/
|
|
10076
|
+
this.selectedItemsIds = model([]);
|
|
10077
|
+
/**
|
|
10078
|
+
* Позиции в наличии на выбранном складе (не под заказ).
|
|
10079
|
+
*/
|
|
10080
|
+
this.itemsInWarehouseStock = computed(() => {
|
|
10081
|
+
const items = this.items();
|
|
10082
|
+
const warehouseId = this.warehouseId();
|
|
10083
|
+
return items.filter((item) => item.product.isWarehouseStockExist(warehouseId) && !item.product.onOrder);
|
|
10084
|
+
});
|
|
10085
|
+
/**
|
|
10086
|
+
* Позиции под заказ.
|
|
10087
|
+
*/
|
|
10088
|
+
this.itemsOnOrder = computed(() => {
|
|
10089
|
+
return this.items().filter((item) => item.product.onOrder);
|
|
10090
|
+
});
|
|
10091
|
+
/**
|
|
10092
|
+
* Позиции, которых нет на выбранном складе (не под заказ).
|
|
10093
|
+
*/
|
|
10094
|
+
this.itemsNotInWarehouseStock = computed(() => {
|
|
10095
|
+
const items = this.items();
|
|
10096
|
+
const warehouseId = this.warehouseId();
|
|
10097
|
+
return items.filter((item) => !item.product.isWarehouseStockExist(warehouseId) && !item.product.onOrder);
|
|
10098
|
+
});
|
|
10099
|
+
}
|
|
10100
|
+
/**
|
|
10101
|
+
* Обработчик изменения выбора позиций.
|
|
10102
|
+
*
|
|
10103
|
+
* @param itemsIds Идентификаторы выбранных позиций.
|
|
10104
|
+
* @param items Позиции.
|
|
10105
|
+
*/
|
|
10106
|
+
selectedItemsHandler(itemsIds, items) {
|
|
10107
|
+
this.selectedItemsIds.update((previous) => [...previous.filter((id) => !items.some((item) => item.id === id) && this.items().some((item) => item.id === id)), ...itemsIds]);
|
|
10108
|
+
}
|
|
10109
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: ScOrderItemsListByStockComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
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 }); }
|
|
10111
|
+
}
|
|
10112
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: ScOrderItemsListByStockComponent, decorators: [{
|
|
10113
|
+
type: Component,
|
|
10114
|
+
args: [{ standalone: true, selector: 'sc-order-items-list-by-stock', imports: [ScOrderItemsListComponent], changeDetection: ChangeDetectionStrategy.OnPush, 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" }]
|
|
10115
|
+
}] });
|
|
10116
|
+
|
|
10117
|
+
/**
|
|
10118
|
+
* Класс-помощник для работы со списком черновиков.
|
|
10119
|
+
*/
|
|
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 {
|
|
10205
|
+
constructor() {
|
|
10206
|
+
/**
|
|
10207
|
+
* Интервалы обновления данных.
|
|
10208
|
+
*/
|
|
10209
|
+
this.updateInterval = inject(SC_UPDATE_INTERVAL);
|
|
10210
|
+
/**
|
|
10211
|
+
* {@link Observable} текущего момента. Первый подписчик запускает таймер, после отписки всех подписчиков таймер останавливается.
|
|
10212
|
+
* Следующие подписчики сразу получают последний момент, а не новый: он может отставать от текущего времени не больше чем
|
|
10213
|
+
* на интервал проверки срока, и метки срока у нового подписчика обновятся на ближайшем тике общего таймера.
|
|
10214
|
+
*/
|
|
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() {
|
|
10251
|
+
/**
|
|
10252
|
+
* Сервис для работы с черновиками заказов.
|
|
10253
|
+
*/
|
|
10254
|
+
this.draftsService = inject(ScOrderDraftsService);
|
|
10255
|
+
/**
|
|
10256
|
+
* Сервис уведомлений.
|
|
10257
|
+
*/
|
|
10258
|
+
this.alerts = inject(TuiAlertService);
|
|
10259
|
+
/**
|
|
10260
|
+
* Сервис текущего момента для меток срока актуализации.
|
|
10261
|
+
*/
|
|
10262
|
+
this.expirationClock = inject(ScDraftExpirationClockService);
|
|
10263
|
+
/**
|
|
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
|
+
* Настройки поля выбора файлов, в том числе наибольший размер файла из настроек приложения.
|
|
9816
10791
|
*/
|
|
9817
|
-
this.
|
|
10792
|
+
this.inputFilesOptions = inject(TUI_INPUT_FILES_OPTIONS);
|
|
9818
10793
|
/**
|
|
9819
|
-
*
|
|
10794
|
+
* Допустимое расширение файла.
|
|
9820
10795
|
*/
|
|
9821
|
-
this.
|
|
10796
|
+
this.accept = '.xlsx';
|
|
9822
10797
|
/**
|
|
9823
|
-
*
|
|
10798
|
+
* Наибольшая длина названия заявки.
|
|
9824
10799
|
*/
|
|
9825
|
-
this.
|
|
10800
|
+
this.nameMaxLength = 255;
|
|
9826
10801
|
/**
|
|
9827
|
-
*
|
|
10802
|
+
* Форма создания черновика. Формат и размер файла проверяются на самом поле: после выбора файла поле выбора убирается вместе со своими проверками.
|
|
9828
10803
|
*/
|
|
9829
|
-
this.
|
|
9830
|
-
|
|
9831
|
-
|
|
9832
|
-
return items.filter((item) => item.product.isWarehouseStockExist(warehouseId) && !item.product.onOrder);
|
|
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) }),
|
|
9833
10807
|
});
|
|
9834
10808
|
/**
|
|
9835
|
-
*
|
|
10809
|
+
* Признак выполнения запроса создания.
|
|
9836
10810
|
*/
|
|
9837
|
-
this.
|
|
9838
|
-
return this.items().filter((item) => item.product.onOrder);
|
|
9839
|
-
});
|
|
10811
|
+
this.submitting = signal(false);
|
|
9840
10812
|
/**
|
|
9841
|
-
*
|
|
10813
|
+
* Признак попытки создать заявку. До неё незаполненное поле файла ошибкой не подсвечивается: поле теряет фокус уже при открытии окна выбора файла.
|
|
9842
10814
|
*/
|
|
9843
|
-
this.
|
|
9844
|
-
|
|
9845
|
-
|
|
9846
|
-
|
|
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
|
+
},
|
|
9847
10848
|
});
|
|
9848
10849
|
}
|
|
9849
10850
|
/**
|
|
9850
|
-
*
|
|
10851
|
+
* Показывает ошибку создания в форме: ошибки проверки данных у полей, ошибку чтения файла у поля файла, остальные ошибкой формы (401 обрабатывает авторизация).
|
|
10852
|
+
* Диалог остаётся открытым.
|
|
9851
10853
|
*
|
|
9852
|
-
* @param
|
|
9853
|
-
* @param items Позиции.
|
|
10854
|
+
* @param error Ошибка чтения файла или запроса.
|
|
9854
10855
|
*/
|
|
9855
|
-
|
|
9856
|
-
|
|
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
|
+
});
|
|
9857
10874
|
}
|
|
9858
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type:
|
|
9859
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.14", type:
|
|
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 }); }
|
|
9860
10882
|
}
|
|
9861
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type:
|
|
10883
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: ScProfstroyDraftCreateDialogComponent, decorators: [{
|
|
9862
10884
|
type: Component,
|
|
9863
|
-
args: [{ standalone: true, selector: 'sc-
|
|
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" }]
|
|
9864
10891
|
}] });
|
|
9865
10892
|
|
|
9866
10893
|
/**
|
|
9867
10894
|
* Компонент страницы черновика заказа (аналог корзины для одного черновика).
|
|
9868
10895
|
*/
|
|
9869
10896
|
class ScDraftComponent {
|
|
10897
|
+
/**
|
|
10898
|
+
* Инициализирует экземпляр класса {@link ScDraftComponent}.
|
|
10899
|
+
*/
|
|
9870
10900
|
constructor() {
|
|
9871
10901
|
/**
|
|
9872
10902
|
* Сервис маршрутизации.
|
|
@@ -9880,6 +10910,10 @@ class ScDraftComponent {
|
|
|
9880
10910
|
* Сервис для работы с черновиками заказов.
|
|
9881
10911
|
*/
|
|
9882
10912
|
this.draftsService = inject(ScOrderDraftsService);
|
|
10913
|
+
/**
|
|
10914
|
+
* Сервис уведомлений.
|
|
10915
|
+
*/
|
|
10916
|
+
this.alerts = inject(TuiAlertService);
|
|
9883
10917
|
/**
|
|
9884
10918
|
* Сигнал с данными текущего черновика заказа (из параметра orderId в URL).
|
|
9885
10919
|
*/
|
|
@@ -9900,6 +10934,45 @@ class ScDraftComponent {
|
|
|
9900
10934
|
* Текущий ключ маршрута.
|
|
9901
10935
|
*/
|
|
9902
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
|
+
});
|
|
9903
10976
|
/**
|
|
9904
10977
|
* Ссылка для автоматического управления уничтожением зависимостей.
|
|
9905
10978
|
*/
|
|
@@ -9908,6 +10981,7 @@ class ScDraftComponent {
|
|
|
9908
10981
|
* Сервис установки SEO-данных приложения.
|
|
9909
10982
|
*/
|
|
9910
10983
|
this.seoService = inject(ScSeoService);
|
|
10984
|
+
this.watchProfstroyProcessing();
|
|
9911
10985
|
}
|
|
9912
10986
|
/** @inheritDoc */
|
|
9913
10987
|
ngOnInit() {
|
|
@@ -9928,7 +11002,32 @@ class ScDraftComponent {
|
|
|
9928
11002
|
this.router.navigate(['orders', 'new', this.draft()?.id, 'draft'], { queryParams: { itemsIds: this.selectedItemsIds() } }).catch(noop);
|
|
9929
11003
|
}
|
|
9930
11004
|
/**
|
|
9931
|
-
*
|
|
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
|
+
* Если удалить не удалось, пользователь остаётся на странице, сообщение об ошибке показывает перехватчик приложения.
|
|
9932
11031
|
*/
|
|
9933
11032
|
clearDraft() {
|
|
9934
11033
|
const { id } = this.draft() ?? {};
|
|
@@ -9944,29 +11043,86 @@ class ScDraftComponent {
|
|
|
9944
11043
|
no: 'Нет, оставить',
|
|
9945
11044
|
},
|
|
9946
11045
|
})
|
|
9947
|
-
.pipe(filter(Boolean), switchMap(() =>
|
|
9948
|
-
.
|
|
9949
|
-
this.
|
|
9950
|
-
|
|
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,
|
|
11058
|
+
});
|
|
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);
|
|
9951
11090
|
});
|
|
9952
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
|
+
}
|
|
9953
11101
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: ScDraftComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
9954
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: [
|
|
9955
11103
|
{
|
|
9956
11104
|
provide: SC_ORDER_LOADER,
|
|
9957
11105
|
useExisting: ScOrderDraftsService,
|
|
9958
11106
|
},
|
|
9959
|
-
], 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 }); }
|
|
9960
11108
|
}
|
|
9961
11109
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: ScDraftComponent, decorators: [{
|
|
9962
11110
|
type: Component,
|
|
9963
|
-
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: [
|
|
9964
11120
|
{
|
|
9965
11121
|
provide: SC_ORDER_LOADER,
|
|
9966
11122
|
useExisting: ScOrderDraftsService,
|
|
9967
11123
|
},
|
|
9968
|
-
], changeDetection: ChangeDetectionStrategy.OnPush, template: "@let items = draft()?.products;\n\n@if (items) {\n
|
|
9969
|
-
}] });
|
|
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: () => [] });
|
|
9970
11126
|
|
|
9971
11127
|
/**
|
|
9972
11128
|
* Компонент статуса оплаты заказа.
|
|
@@ -11121,14 +12277,18 @@ class ScPreviewSampleModule {
|
|
|
11121
12277
|
TuiHovered,
|
|
11122
12278
|
TuiButton,
|
|
11123
12279
|
TuiButtonLoading,
|
|
11124
|
-
TuiLabel
|
|
12280
|
+
TuiLabel,
|
|
12281
|
+
RecaptchaFormsModule,
|
|
12282
|
+
RecaptchaModule], exports: [ScPreviewSampleComponent] }); }
|
|
11125
12283
|
static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: ScPreviewSampleModule, imports: [CommonModule,
|
|
11126
12284
|
ReactiveFormsModule,
|
|
11127
12285
|
TuiError,
|
|
11128
12286
|
ScFormFieldsModule,
|
|
11129
12287
|
TuiInputModule,
|
|
11130
12288
|
TuiInputPhoneModule, i2$1.TuiCarouselComponent, TuiAvatar,
|
|
11131
|
-
TuiButtonLoading
|
|
12289
|
+
TuiButtonLoading,
|
|
12290
|
+
RecaptchaFormsModule,
|
|
12291
|
+
RecaptchaModule] }); }
|
|
11132
12292
|
}
|
|
11133
12293
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: ScPreviewSampleModule, decorators: [{
|
|
11134
12294
|
type: NgModule,
|
|
@@ -11148,6 +12308,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
|
|
|
11148
12308
|
TuiButton,
|
|
11149
12309
|
TuiButtonLoading,
|
|
11150
12310
|
TuiLabel,
|
|
12311
|
+
RecaptchaFormsModule,
|
|
12312
|
+
RecaptchaModule,
|
|
11151
12313
|
],
|
|
11152
12314
|
exports: [ScPreviewSampleComponent],
|
|
11153
12315
|
}]
|
|
@@ -11167,255 +12329,6 @@ const scGetCurrentRoute = (route) => {
|
|
|
11167
12329
|
return currentRoute;
|
|
11168
12330
|
};
|
|
11169
12331
|
|
|
11170
|
-
/**
|
|
11171
|
-
* Токен времени ожидания загрузки скрипта Yandex SmartCaptcha в миллисекундах.
|
|
11172
|
-
*/
|
|
11173
|
-
const SC_SMART_CAPTCHA_LOAD_TIMEOUT = new InjectionToken('SC_SMART_CAPTCHA_LOAD_TIMEOUT', {
|
|
11174
|
-
factory: () => 20_000,
|
|
11175
|
-
});
|
|
11176
|
-
|
|
11177
|
-
/**
|
|
11178
|
-
* Сервис загрузки скрипта Yandex SmartCaptcha.
|
|
11179
|
-
*/
|
|
11180
|
-
class ScSmartCaptchaLoaderService {
|
|
11181
|
-
constructor() {
|
|
11182
|
-
/**
|
|
11183
|
-
* Сервис для вставки внешних скриптов.
|
|
11184
|
-
*/
|
|
11185
|
-
this.scriptsService = inject(ScScriptsService);
|
|
11186
|
-
/**
|
|
11187
|
-
* Окно браузера с API Yandex SmartCaptcha.
|
|
11188
|
-
*/
|
|
11189
|
-
this.window = inject(WA_WINDOW);
|
|
11190
|
-
/**
|
|
11191
|
-
* Признак того, что приложение запущено в браузере.
|
|
11192
|
-
*/
|
|
11193
|
-
this.isBrowser = inject(IS_BROWSER);
|
|
11194
|
-
/**
|
|
11195
|
-
* Время ожидания загрузки скрипта в миллисекундах.
|
|
11196
|
-
*/
|
|
11197
|
-
this.loadTimeout = inject(SC_SMART_CAPTCHA_LOAD_TIMEOUT);
|
|
11198
|
-
/**
|
|
11199
|
-
* Имя функции окна, которую скрипт вызывает после загрузки.
|
|
11200
|
-
*/
|
|
11201
|
-
this.onloadCallbackName = 'scSmartCaptchaOnload';
|
|
11202
|
-
/**
|
|
11203
|
-
* Адрес скрипта.
|
|
11204
|
-
*/
|
|
11205
|
-
this.scriptUrl = `https://smartcaptcha.cloud.yandex.ru/captcha.js?render=onload&onload=${this.onloadCallbackName}`;
|
|
11206
|
-
/**
|
|
11207
|
-
* Загрузка API, общая для всех виджетов. Скрипт загружается один раз; отписка и истечение ожидания у подписчика загрузку не прерывают, после ошибки следующая подписка загружает скрипт заново.
|
|
11208
|
-
*/
|
|
11209
|
-
this.loading$ = defer(() => this.load$()).pipe(shareReplay(1));
|
|
11210
|
-
/**
|
|
11211
|
-
* {@link Observable} API Yandex SmartCaptcha. Значения и ошибки приходят вне зоны Angular, ожидание первого значения ограничено {@link SC_SMART_CAPTCHA_LOAD_TIMEOUT}.
|
|
11212
|
-
*/
|
|
11213
|
-
this.api$ = this.loading$.pipe(timeout({ first: this.loadTimeout }), tuiZonefree());
|
|
11214
|
-
}
|
|
11215
|
-
/**
|
|
11216
|
-
* Возвращает API Yandex SmartCaptcha, при необходимости загружая скрипт. На сервере ничего не возвращает.
|
|
11217
|
-
*/
|
|
11218
|
-
load$() {
|
|
11219
|
-
if (!this.isBrowser) {
|
|
11220
|
-
return EMPTY;
|
|
11221
|
-
}
|
|
11222
|
-
if (this.window.smartCaptcha) {
|
|
11223
|
-
return of(this.window.smartCaptcha);
|
|
11224
|
-
}
|
|
11225
|
-
return merge(this.onload$(), this.scriptsService.loadScript$(this.scriptUrl).pipe(ignoreElements()));
|
|
11226
|
-
}
|
|
11227
|
-
/**
|
|
11228
|
-
* Ждёт вызова функции из параметра `onload` адреса скрипта и возвращает API, которое скрипт к этому моменту создал.
|
|
11229
|
-
*/
|
|
11230
|
-
onload$() {
|
|
11231
|
-
return new Observable((subscriber) => {
|
|
11232
|
-
this.window[this.onloadCallbackName] = () => {
|
|
11233
|
-
if (this.window.smartCaptcha) {
|
|
11234
|
-
subscriber.next(this.window.smartCaptcha);
|
|
11235
|
-
subscriber.complete();
|
|
11236
|
-
}
|
|
11237
|
-
else {
|
|
11238
|
-
subscriber.error(new Error('Yandex SmartCaptcha API is unavailable'));
|
|
11239
|
-
}
|
|
11240
|
-
};
|
|
11241
|
-
return () => {
|
|
11242
|
-
this.window[this.onloadCallbackName] = undefined;
|
|
11243
|
-
};
|
|
11244
|
-
});
|
|
11245
|
-
}
|
|
11246
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: ScSmartCaptchaLoaderService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
11247
|
-
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: ScSmartCaptchaLoaderService, providedIn: 'root' }); }
|
|
11248
|
-
}
|
|
11249
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: ScSmartCaptchaLoaderService, decorators: [{
|
|
11250
|
-
type: Injectable,
|
|
11251
|
-
args: [{
|
|
11252
|
-
providedIn: 'root',
|
|
11253
|
-
}]
|
|
11254
|
-
}] });
|
|
11255
|
-
|
|
11256
|
-
/**
|
|
11257
|
-
* Токен настроек Yandex SmartCaptcha. Задаётся приложением.
|
|
11258
|
-
*/
|
|
11259
|
-
const SC_SMART_CAPTCHA_SETTINGS = new InjectionToken('SC_SMART_CAPTCHA_SETTINGS');
|
|
11260
|
-
|
|
11261
|
-
/* eslint-disable no-use-before-define */
|
|
11262
|
-
/**
|
|
11263
|
-
* Виджет Yandex SmartCaptcha. Значение поля ввода - токен прохождения проверки, `null` пока проверка не пройдена или токен истёк.
|
|
11264
|
-
*/
|
|
11265
|
-
class ScSmartCaptchaComponent {
|
|
11266
|
-
/**
|
|
11267
|
-
* Инициализирует экземпляр класса {@link ScSmartCaptchaComponent}.
|
|
11268
|
-
*/
|
|
11269
|
-
constructor() {
|
|
11270
|
-
/**
|
|
11271
|
-
* Контейнер, в котором отрисовывается виджет.
|
|
11272
|
-
*/
|
|
11273
|
-
this.container = viewChild.required('container');
|
|
11274
|
-
/**
|
|
11275
|
-
* Настройки Yandex SmartCaptcha.
|
|
11276
|
-
*/
|
|
11277
|
-
this.settings = inject(SC_SMART_CAPTCHA_SETTINGS);
|
|
11278
|
-
/**
|
|
11279
|
-
* Сервис загрузки скрипта Yandex SmartCaptcha.
|
|
11280
|
-
*/
|
|
11281
|
-
this.loader = inject(ScSmartCaptchaLoaderService);
|
|
11282
|
-
/**
|
|
11283
|
-
* {@link NgZone} для отрисовки виджета вне зоны Angular и возврата его событий в зону.
|
|
11284
|
-
*/
|
|
11285
|
-
this.ngZone = inject(NgZone);
|
|
11286
|
-
/**
|
|
11287
|
-
* Ссылка для автоматического управления уничтожением зависимостей.
|
|
11288
|
-
*/
|
|
11289
|
-
this.destroyRef = inject(DestroyRef);
|
|
11290
|
-
/**
|
|
11291
|
-
* Признак того, что капчу не удалось загрузить.
|
|
11292
|
-
*/
|
|
11293
|
-
this.hasError = signal(false);
|
|
11294
|
-
/**
|
|
11295
|
-
* Функции отписки от событий виджета.
|
|
11296
|
-
*/
|
|
11297
|
-
this.unsubscribers = [];
|
|
11298
|
-
/**
|
|
11299
|
-
* Обработчик изменения значения поля ввода.
|
|
11300
|
-
*/
|
|
11301
|
-
this.onChange = noop;
|
|
11302
|
-
/**
|
|
11303
|
-
* Обработчик касания поля ввода.
|
|
11304
|
-
*/
|
|
11305
|
-
this.onTouched = noop;
|
|
11306
|
-
afterNextRender(() => {
|
|
11307
|
-
this.load();
|
|
11308
|
-
});
|
|
11309
|
-
this.destroyRef.onDestroy(() => {
|
|
11310
|
-
this.destroyWidget();
|
|
11311
|
-
});
|
|
11312
|
-
}
|
|
11313
|
-
/** @inheritDoc */
|
|
11314
|
-
writeValue(token) {
|
|
11315
|
-
const { api, widgetId } = this;
|
|
11316
|
-
if (!token && api && widgetId !== undefined) {
|
|
11317
|
-
this.ngZone.runOutsideAngular(() => {
|
|
11318
|
-
api.reset(widgetId);
|
|
11319
|
-
});
|
|
11320
|
-
}
|
|
11321
|
-
}
|
|
11322
|
-
/** @inheritDoc */
|
|
11323
|
-
registerOnChange(onChange) {
|
|
11324
|
-
this.onChange = onChange;
|
|
11325
|
-
}
|
|
11326
|
-
/** @inheritDoc */
|
|
11327
|
-
registerOnTouched(onTouched) {
|
|
11328
|
-
this.onTouched = onTouched;
|
|
11329
|
-
}
|
|
11330
|
-
/**
|
|
11331
|
-
* Повторяет загрузку капчи после ошибки.
|
|
11332
|
-
*/
|
|
11333
|
-
retry() {
|
|
11334
|
-
this.hasError.set(false);
|
|
11335
|
-
this.load();
|
|
11336
|
-
}
|
|
11337
|
-
/**
|
|
11338
|
-
* Загружает API Yandex SmartCaptcha и отрисовывает виджет (API приходит вне зоны Angular). При ошибке загрузки или отрисовки показывает сообщение.
|
|
11339
|
-
*/
|
|
11340
|
-
load() {
|
|
11341
|
-
this.loader.api$
|
|
11342
|
-
.pipe(tap((api) => {
|
|
11343
|
-
this.render(api);
|
|
11344
|
-
}), takeUntilDestroyed(this.destroyRef))
|
|
11345
|
-
.subscribe({
|
|
11346
|
-
error: () => {
|
|
11347
|
-
this.showError();
|
|
11348
|
-
},
|
|
11349
|
-
});
|
|
11350
|
-
}
|
|
11351
|
-
/**
|
|
11352
|
-
* Отрисовывает виджет и подписывается на его события.
|
|
11353
|
-
*
|
|
11354
|
-
* @param api API Yandex SmartCaptcha.
|
|
11355
|
-
*/
|
|
11356
|
-
render(api) {
|
|
11357
|
-
const widgetId = api.render(this.container().nativeElement, { sitekey: this.settings.sitekey, hl: this.settings.language });
|
|
11358
|
-
this.api = api;
|
|
11359
|
-
this.widgetId = widgetId;
|
|
11360
|
-
this.unsubscribers = [
|
|
11361
|
-
api.subscribe(widgetId, 'success', (token) => {
|
|
11362
|
-
this.updateValue(typeof token === 'string' ? token : null);
|
|
11363
|
-
}),
|
|
11364
|
-
api.subscribe(widgetId, 'token-expired', () => {
|
|
11365
|
-
this.updateValue(null);
|
|
11366
|
-
api.reset(widgetId);
|
|
11367
|
-
}),
|
|
11368
|
-
api.subscribe(widgetId, 'javascript-error', () => {
|
|
11369
|
-
this.showError();
|
|
11370
|
-
}),
|
|
11371
|
-
];
|
|
11372
|
-
}
|
|
11373
|
-
/**
|
|
11374
|
-
* Передаёт форме новое значение токена.
|
|
11375
|
-
*
|
|
11376
|
-
* @param token Токен прохождения проверки.
|
|
11377
|
-
*/
|
|
11378
|
-
updateValue(token) {
|
|
11379
|
-
this.ngZone.run(() => {
|
|
11380
|
-
this.onChange(token);
|
|
11381
|
-
this.onTouched();
|
|
11382
|
-
});
|
|
11383
|
-
}
|
|
11384
|
-
/**
|
|
11385
|
-
* Удаляет виджет, очищает токен и показывает сообщение об ошибке.
|
|
11386
|
-
*/
|
|
11387
|
-
showError() {
|
|
11388
|
-
this.destroyWidget();
|
|
11389
|
-
this.ngZone.run(() => {
|
|
11390
|
-
this.hasError.set(true);
|
|
11391
|
-
this.onChange(null);
|
|
11392
|
-
});
|
|
11393
|
-
}
|
|
11394
|
-
/**
|
|
11395
|
-
* Удаляет виджет вместе с созданными им элементами и обработчиками.
|
|
11396
|
-
*/
|
|
11397
|
-
destroyWidget() {
|
|
11398
|
-
this.unsubscribers.forEach((unsubscribe) => {
|
|
11399
|
-
unsubscribe();
|
|
11400
|
-
});
|
|
11401
|
-
this.unsubscribers = [];
|
|
11402
|
-
const { api, widgetId } = this;
|
|
11403
|
-
if (api && widgetId !== undefined) {
|
|
11404
|
-
this.ngZone.runOutsideAngular(() => {
|
|
11405
|
-
api.destroy(widgetId);
|
|
11406
|
-
});
|
|
11407
|
-
}
|
|
11408
|
-
this.api = undefined;
|
|
11409
|
-
this.widgetId = undefined;
|
|
11410
|
-
}
|
|
11411
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: ScSmartCaptchaComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
11412
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.14", type: ScSmartCaptchaComponent, isStandalone: true, selector: "sc-smart-captcha", providers: [tuiProvide(NG_VALUE_ACCESSOR, ScSmartCaptchaComponent, true)], viewQueries: [{ propertyName: "container", first: true, predicate: ["container"], descendants: true, isSignal: true }], ngImport: i0, template: "<div\n #container\n class=\"min-h-[7.375rem]\"\n [class.hidden]=\"hasError()\"\n></div>\n@if (hasError()) {\n <p\n role=\"alert\"\n class=\"text-body-s text-tui-text-negative\"\n >\n \u041D\u0435 \u0443\u0434\u0430\u043B\u043E\u0441\u044C \u0437\u0430\u0433\u0440\u0443\u0437\u0438\u0442\u044C \u043F\u0440\u043E\u0432\u0435\u0440\u043A\u0443 \u00AB\u042F \u043D\u0435 \u0440\u043E\u0431\u043E\u0442\u00BB.\n <button\n tuiLink\n type=\"button\"\n (click)=\"retry()\"\n >\n \u041F\u043E\u0432\u0442\u043E\u0440\u0438\u0442\u044C\n </button>\n </p>\n}\n", dependencies: [{ kind: "directive", type: TuiLink, selector: "a[tuiLink], button[tuiLink]", inputs: ["pseudo"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
11413
|
-
}
|
|
11414
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: ScSmartCaptchaComponent, decorators: [{
|
|
11415
|
-
type: Component,
|
|
11416
|
-
args: [{ standalone: true, selector: 'sc-smart-captcha', imports: [TuiLink], providers: [tuiProvide(NG_VALUE_ACCESSOR, ScSmartCaptchaComponent, true)], changeDetection: ChangeDetectionStrategy.OnPush, template: "<div\n #container\n class=\"min-h-[7.375rem]\"\n [class.hidden]=\"hasError()\"\n></div>\n@if (hasError()) {\n <p\n role=\"alert\"\n class=\"text-body-s text-tui-text-negative\"\n >\n \u041D\u0435 \u0443\u0434\u0430\u043B\u043E\u0441\u044C \u0437\u0430\u0433\u0440\u0443\u0437\u0438\u0442\u044C \u043F\u0440\u043E\u0432\u0435\u0440\u043A\u0443 \u00AB\u042F \u043D\u0435 \u0440\u043E\u0431\u043E\u0442\u00BB.\n <button\n tuiLink\n type=\"button\"\n (click)=\"retry()\"\n >\n \u041F\u043E\u0432\u0442\u043E\u0440\u0438\u0442\u044C\n </button>\n </p>\n}\n" }]
|
|
11417
|
-
}], ctorParameters: () => [] });
|
|
11418
|
-
|
|
11419
12332
|
/**
|
|
11420
12333
|
* Компонент формы обратной связи.
|
|
11421
12334
|
*/
|
|
@@ -11487,7 +12400,7 @@ class ScFeedbackFormComponent {
|
|
|
11487
12400
|
}
|
|
11488
12401
|
}));
|
|
11489
12402
|
/**
|
|
11490
|
-
* Поле ввода
|
|
12403
|
+
* Поле ввода для recaptcha.
|
|
11491
12404
|
*/
|
|
11492
12405
|
this.recaptchaControl = new FormControl(null, Validators.required);
|
|
11493
12406
|
/**
|
|
@@ -11529,10 +12442,8 @@ class ScFeedbackFormComponent {
|
|
|
11529
12442
|
}),
|
|
11530
12443
|
// eslint-disable-next-line rxjs/no-implicit-any-catch
|
|
11531
12444
|
catchError((error) => {
|
|
11532
|
-
// Токен капчи одноразовый: для повторной отправки нужна новая проверка.
|
|
11533
|
-
this.recaptchaControl.reset();
|
|
11534
12445
|
this.form.markAllAsTouched();
|
|
11535
|
-
const { errors, message } =
|
|
12446
|
+
const { errors, message } = error.error;
|
|
11536
12447
|
if (errors) {
|
|
11537
12448
|
Object.keys(errors).forEach((key) => {
|
|
11538
12449
|
const control = this.form.get(key);
|
|
@@ -11569,6 +12480,16 @@ class ScFeedbackFormComponent {
|
|
|
11569
12480
|
* Список стран.
|
|
11570
12481
|
*/
|
|
11571
12482
|
this.countries = getCountries();
|
|
12483
|
+
/**
|
|
12484
|
+
* Это решение ошибки #4940, было @see [предложено](https://github.com/DethAriel/ng-recaptcha/issues/123#issuecomment-426112101) автором {@link RecaptchaComponent}.
|
|
12485
|
+
*/
|
|
12486
|
+
RecaptchaComponent.prototype.ngOnDestroy = function () {
|
|
12487
|
+
// eslint-disable-next-line @typescript-eslint/dot-notation
|
|
12488
|
+
if (this['subscription']) {
|
|
12489
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-call, @typescript-eslint/dot-notation
|
|
12490
|
+
this['subscription'].unsubscribe();
|
|
12491
|
+
}
|
|
12492
|
+
};
|
|
11572
12493
|
merge(this.user$, this.accepted$.pipe(tap((files) => {
|
|
11573
12494
|
this.form.controls.files.reset([]);
|
|
11574
12495
|
this.onUploadFile(files);
|
|
@@ -11692,7 +12613,7 @@ class ScFeedbackFormComponent {
|
|
|
11692
12613
|
return ScPhoneService.isValidPhoneNumberForMessenger(value);
|
|
11693
12614
|
}
|
|
11694
12615
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: ScFeedbackFormComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
11695
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.14", type: ScFeedbackFormComponent, isStandalone: true, selector: "sc-feedback-form", inputs: { slugInput: { classPropertyName: "slugInput", publicName: "slug", isSignal: true, isRequired: false, transformFunction: null }, fieldsInput: { classPropertyName: "fieldsInput", publicName: "fields", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { clickOffer: "clickOffer", sendSuccess: "sendSuccess" }, ngImport: i0, template: "@let fieldsList = fields();\n<form\n *ngIf=\"fieldsList.length > 0\"\n [formGroup]=\"form\"\n (ngSubmit)=\"onSubmit.next()\"\n class=\"flex flex-col justify-between gap-4\"\n>\n <!-- \u0424\u0418\u041E -->\n <label\n *ngIf=\"fieldsList.includes('name')\"\n tuiLabel\n >\n \u0424\u0418\u041E\n <tui-input formControlName=\"name\"> \u0423\u043A\u0430\u0436\u0438\u0442\u0435 \u0412\u0430\u0448\u0438 \u0424.\u0418.\u041E. </tui-input>\n <tui-error\n formControlName=\"name\"\n [error]=\"[] | tuiFieldError | async\"\n />\n </label>\n\n <!-- \u0422\u0435\u043B\u0435\u0444\u043E\u043D -->\n @if (fieldsList.includes('phone')) {\n <label tuiLabel>\n \u0422\u0435\u043B\u0435\u0444\u043E\u043D\n <tui-textfield>\n <input\n formControlName=\"phone\"\n tuiInputPhoneInternational\n [countrySearch]=\"true\"\n [countries]=\"(countries | tuiSortCountries | async) || []\"\n autocomplete=\"phone\"\n />\n </tui-textfield>\n <tui-error\n formControlName=\"phone\"\n [error]=\"[] | tuiFieldError | async\"\n />\n </label>\n\n <label\n *ngIf=\"fieldsList.includes('isShouldUseMessenger') && form.controls.phone.value && isValidPhoneNumberForMessenger(form.controls.phone.value)\"\n tuiLabel\n >\n <input\n formControlName=\"isShouldUseMessenger\"\n tuiCheckbox\n type=\"checkbox\"\n />\n <span tuiTitle> \u041D\u0430\u043F\u0438\u0441\u0430\u0442\u044C \u0432 \u043C\u0435\u0441\u0441\u0435\u043D\u0434\u0436\u0435\u0440\u0430\u0445, \u0430 \u043D\u0435 \u0437\u0432\u043E\u043D\u0438\u0442\u044C </span>\n </label>\n }\n <!-- \u0410\u0434\u0440\u0435\u0441 \u044D\u043B\u0435\u043A\u0442\u0440\u043E\u043D\u043D\u043E\u0439 \u043F\u043E\u0447\u0442\u044B -->\n <label\n *ngIf=\"fieldsList.includes('email')\"\n tuiLabel\n >\n \u0410\u0434\u0440\u0435\u0441 \u044D\u043B\u0435\u043A\u0442\u0440\u043E\u043D\u043D\u043E\u0439 \u043F\u043E\u0447\u0442\u044B\n <tui-input formControlName=\"email\">\n \u0410\u0434\u0440\u0435\u0441 \u044D\u043B\u0435\u043A\u0442\u0440\u043E\u043D\u043D\u043E\u0439 \u043F\u043E\u0447\u0442\u044B\n <sc-suggestion-field\n *tuiDataList\n [type]=\"suggestionType.email\"\n />\n </tui-input>\n <tui-error\n formControlName=\"email\"\n [error]=\"[] | tuiFieldError | async\"\n />\n </label>\n\n <!-- \u041A\u043E\u043C\u043C\u0435\u043D\u0442\u0430\u0440\u0438\u0439 -->\n <label\n *ngIf=\"fieldsList.includes('message')\"\n tuiLabel\n >\n \u041A\u043E\u043C\u043C\u0435\u043D\u0442\u0430\u0440\u0438\u0439\n <tui-textarea formControlName=\"message\"> \u041A\u043E\u043C\u043C\u0435\u043D\u0442\u0430\u0440\u0438\u0439 </tui-textarea>\n <tui-error\n formControlName=\"message\"\n [error]=\"[] | tuiFieldError | async\"\n />\n </label>\n\n <!-- \u0424\u0430\u0439\u043B\u044B -->\n <div\n *ngIf=\"fieldsList.includes('files')\"\n class=\"flex flex-col gap-1\"\n >\n <label tuiInputFiles>\n <input\n #validator=\"tuiInputFilesValidator\"\n accept=\".doc,.docx,.pdf,image/*\"\n tuiInputFiles\n [formControl]=\"loadedFile\"\n [multiple]=\"true\"\n (reject)=\"onReject($event)\"\n />\n </label>\n\n <tui-error\n *ngIf=\"loadedFile.invalid\"\n [error]=\"['maxLength'] | tuiFieldError | async\"\n [formControl]=\"loadedFile\"\n />\n\n @let accepted = accepted$ | async;\n <tui-files\n *ngIf=\"accepted?.length || rejected.length\"\n class=\"tui-space_top-1\"\n >\n <tui-file\n *ngFor=\"let file of accepted\"\n [file]=\"file\"\n (remove)=\"onRemove(file)\"\n />\n <tui-file\n *ngFor=\"let file of rejected\"\n state=\"error\"\n [file]=\"(file | tuiFileRejected: validator | async) || file\"\n (remove)=\"onRemove(file)\"\n />\n </tui-files>\n </div>\n\n <!-- \u041A\u0430\u043F\u0447\u0430 -->\n @if (form.get('recaptcha'); as recaptcha) {\n <div\n class=\"mb-3 max-h-[7.5rem] overflow-hidden duration-500\"\n [ngClass]=\"{ '!max-h-0 !-m-2': recaptcha.valid }\"\n [attr.inert]=\"recaptcha.valid ? '' : null\"\n >\n <sc-smart-captcha formControlName=\"recaptcha\" />\n </div>\n @if (recaptcha.getError('serverResponse'); as serverErrors) {\n <tui-error [error]=\"serverErrors.join(' ')\" />\n }\n }\n\n <!-- \u042D\u043B\u0435\u043C\u0435\u043D\u0442\u044B \u0443\u043F\u0440\u0430\u0432\u043B\u0435\u043D\u0438\u044F -->\n <div class=\"flex flex-wrap items-center gap-3\">\n <button\n tuiButton\n [disabled]=\"form.invalid\"\n [loading]=\"!!(loading$ | async)\"\n type=\"submit\"\n iconStart=\"@tui.sc.send\"\n class=\"self-center\"\n >\n \u041E\u0442\u043F\u0440\u0430\u0432\u0438\u0442\u044C\n </button>\n <div class=\"grow basis-60 text-body-s\">\n \u041D\u0430\u0436\u0438\u043C\u0430\u044F \u043D\u0430 \u043A\u043D\u043E\u043F\u043A\u0443, \u0432\u044B \u0441\u043E\u0433\u043B\u0430\u0448\u0430\u0435\u0442\u0435\u0441\u044C \u0441\n <button\n tuiLink\n [pseudo]=\"true\"\n type=\"button\"\n (click)=\"context?.data?.onClickOfferHandler(); clickOffer.emit()\"\n >\n \u041F\u043E\u043B\u0438\u0442\u0438\u043A\u043E\u0439 \u043A\u043E\u043D\u0444\u0438\u0434\u0435\u043D\u0446\u0438\u0430\u043B\u044C\u043D\u043E\u0441\u0442\u0438\n </button>\n </div>\n </div>\n</form>\n", dependencies: [{ kind: "pipe", type: AsyncPipe, name: "async" }, { 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.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.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["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: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: NgFor, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "component", type: TuiError, selector: "tui-error", inputs: ["error"] }, { kind: "pipe", type: TuiFieldErrorPipe, name: "tuiFieldError" }, { kind: "ngmodule", type: ScFormFieldsModule }, { kind: "component", type: ScSuggestionFieldComponent, selector: "sc-suggestion-field", inputs: ["type"], outputs: ["selectedClick"] }, { kind: "ngmodule", type: TuiInputModule }, { kind: "component", type: i3.TuiInputComponent, selector: "tui-input" }, { kind: "directive", type: i3.TuiInputDirective, selector: "tui-input" }, { kind: "ngmodule", type: TuiInputPhoneModule }, { 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: "directive", type: TuiLabel, selector: "label[tuiLabel]" }, { kind: "component", type: ScSmartCaptchaComponent, selector: "sc-smart-captcha" }, { kind: "directive", type: TuiLink, selector: "a[tuiLink], button[tuiLink]", inputs: ["pseudo"] }, { kind: "directive", type: NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: TuiTextareaModule }, { kind: "component", type: i3.TuiTextareaComponent, selector: "tui-textarea", inputs: ["rows", "maxLength", "expandable"] }, { kind: "directive", type: i3.TuiTextareaDirective, selector: "tui-textarea" }, { kind: "component", type: TuiCheckbox, selector: "input[type=\"checkbox\"][tuiCheckbox]", inputs: ["size"] }, { kind: "directive", type: TuiTitle, selector: "[tuiTitle]", inputs: ["tuiTitle"] }, { 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: "component", type: TuiInputPhoneInternational, selector: "input[tuiInputPhoneInternational]", inputs: ["countrySearch", "countries", "countryIsoCode"], outputs: ["countryIsoCodeChange"] }, { kind: "component", type: i1$2.TuiTextfieldComponent, selector: "tui-textfield:not([multi])" }, { kind: "pipe", type: TuiSortCountriesPipe, name: "tuiSortCountries" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
12616
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.14", type: ScFeedbackFormComponent, isStandalone: true, selector: "sc-feedback-form", inputs: { slugInput: { classPropertyName: "slugInput", publicName: "slug", isSignal: true, isRequired: false, transformFunction: null }, fieldsInput: { classPropertyName: "fieldsInput", publicName: "fields", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { clickOffer: "clickOffer", sendSuccess: "sendSuccess" }, ngImport: i0, template: "@let fieldsList = fields();\n<form\n *ngIf=\"fieldsList.length > 0\"\n [formGroup]=\"form\"\n (ngSubmit)=\"onSubmit.next()\"\n class=\"flex flex-col justify-between gap-4\"\n>\n <!-- \u0424\u0418\u041E -->\n <label\n *ngIf=\"fieldsList.includes('name')\"\n tuiLabel\n >\n \u0424\u0418\u041E\n <tui-input formControlName=\"name\"> \u0423\u043A\u0430\u0436\u0438\u0442\u0435 \u0412\u0430\u0448\u0438 \u0424.\u0418.\u041E. </tui-input>\n <tui-error\n formControlName=\"name\"\n [error]=\"[] | tuiFieldError | async\"\n />\n </label>\n\n <!-- \u0422\u0435\u043B\u0435\u0444\u043E\u043D -->\n @if (fieldsList.includes('phone')) {\n <label tuiLabel>\n \u0422\u0435\u043B\u0435\u0444\u043E\u043D\n <tui-textfield>\n <input\n formControlName=\"phone\"\n tuiInputPhoneInternational\n [countrySearch]=\"true\"\n [countries]=\"(countries | tuiSortCountries | async) || []\"\n autocomplete=\"phone\"\n />\n </tui-textfield>\n <tui-error\n formControlName=\"phone\"\n [error]=\"[] | tuiFieldError | async\"\n />\n </label>\n\n <label\n *ngIf=\"fieldsList.includes('isShouldUseMessenger') && form.controls.phone.value && isValidPhoneNumberForMessenger(form.controls.phone.value)\"\n tuiLabel\n >\n <input\n formControlName=\"isShouldUseMessenger\"\n tuiCheckbox\n type=\"checkbox\"\n />\n <span tuiTitle> \u041D\u0430\u043F\u0438\u0441\u0430\u0442\u044C \u0432 \u043C\u0435\u0441\u0441\u0435\u043D\u0434\u0436\u0435\u0440\u0430\u0445, \u0430 \u043D\u0435 \u0437\u0432\u043E\u043D\u0438\u0442\u044C </span>\n </label>\n }\n <!-- \u0410\u0434\u0440\u0435\u0441 \u044D\u043B\u0435\u043A\u0442\u0440\u043E\u043D\u043D\u043E\u0439 \u043F\u043E\u0447\u0442\u044B -->\n <label\n *ngIf=\"fieldsList.includes('email')\"\n tuiLabel\n >\n \u0410\u0434\u0440\u0435\u0441 \u044D\u043B\u0435\u043A\u0442\u0440\u043E\u043D\u043D\u043E\u0439 \u043F\u043E\u0447\u0442\u044B\n <tui-input formControlName=\"email\">\n \u0410\u0434\u0440\u0435\u0441 \u044D\u043B\u0435\u043A\u0442\u0440\u043E\u043D\u043D\u043E\u0439 \u043F\u043E\u0447\u0442\u044B\n <sc-suggestion-field\n *tuiDataList\n [type]=\"suggestionType.email\"\n />\n </tui-input>\n <tui-error\n formControlName=\"email\"\n [error]=\"[] | tuiFieldError | async\"\n />\n </label>\n\n <!-- \u041A\u043E\u043C\u043C\u0435\u043D\u0442\u0430\u0440\u0438\u0439 -->\n <label\n *ngIf=\"fieldsList.includes('message')\"\n tuiLabel\n >\n \u041A\u043E\u043C\u043C\u0435\u043D\u0442\u0430\u0440\u0438\u0439\n <tui-textarea formControlName=\"message\"> \u041A\u043E\u043C\u043C\u0435\u043D\u0442\u0430\u0440\u0438\u0439 </tui-textarea>\n <tui-error\n formControlName=\"message\"\n [error]=\"[] | tuiFieldError | async\"\n />\n </label>\n\n <!-- \u0424\u0430\u0439\u043B\u044B -->\n <div\n *ngIf=\"fieldsList.includes('files')\"\n class=\"flex flex-col gap-1\"\n >\n <label tuiInputFiles>\n <input\n #validator=\"tuiInputFilesValidator\"\n accept=\".doc,.docx,.pdf,image/*\"\n tuiInputFiles\n [formControl]=\"loadedFile\"\n [multiple]=\"true\"\n (reject)=\"onReject($event)\"\n />\n </label>\n\n <tui-error\n *ngIf=\"loadedFile.invalid\"\n [error]=\"['maxLength'] | tuiFieldError | async\"\n [formControl]=\"loadedFile\"\n />\n\n @let accepted = accepted$ | async;\n <tui-files\n *ngIf=\"accepted?.length || rejected.length\"\n class=\"tui-space_top-1\"\n >\n <tui-file\n *ngFor=\"let file of accepted\"\n [file]=\"file\"\n (remove)=\"onRemove(file)\"\n />\n <tui-file\n *ngFor=\"let file of rejected\"\n state=\"error\"\n [file]=\"(file | tuiFileRejected: validator | async) || file\"\n (remove)=\"onRemove(file)\"\n />\n </tui-files>\n </div>\n\n <!-- recaptcha -->\n @if (form.get('recaptcha')) {\n <div\n class=\"mb-3 max-h-20 overflow-hidden duration-500\"\n [ngClass]=\"{ '!max-h-0 !-m-2': form.get('recaptcha')?.valid }\"\n >\n <re-captcha formControlName=\"recaptcha\" />\n </div>\n }\n\n <!-- \u042D\u043B\u0435\u043C\u0435\u043D\u0442\u044B \u0443\u043F\u0440\u0430\u0432\u043B\u0435\u043D\u0438\u044F -->\n <div class=\"flex flex-wrap items-center gap-3\">\n <button\n tuiButton\n [disabled]=\"form.invalid\"\n [loading]=\"!!(loading$ | async)\"\n type=\"submit\"\n iconStart=\"@tui.sc.send\"\n class=\"self-center\"\n >\n \u041E\u0442\u043F\u0440\u0430\u0432\u0438\u0442\u044C\n </button>\n <div class=\"grow basis-60 text-body-s\">\n \u041D\u0430\u0436\u0438\u043C\u0430\u044F \u043D\u0430 \u043A\u043D\u043E\u043F\u043A\u0443, \u0432\u044B \u0441\u043E\u0433\u043B\u0430\u0448\u0430\u0435\u0442\u0435\u0441\u044C \u0441\n <button\n tuiLink\n [pseudo]=\"true\"\n type=\"button\"\n (click)=\"context?.data?.onClickOfferHandler(); clickOffer.emit()\"\n >\n \u041F\u043E\u043B\u0438\u0442\u0438\u043A\u043E\u0439 \u043A\u043E\u043D\u0444\u0438\u0434\u0435\u043D\u0446\u0438\u0430\u043B\u044C\u043D\u043E\u0441\u0442\u0438\n </button>\n </div>\n </div>\n</form>\n", dependencies: [{ kind: "pipe", type: AsyncPipe, name: "async" }, { 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.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.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["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: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: NgFor, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "component", type: TuiError, selector: "tui-error", inputs: ["error"] }, { kind: "pipe", type: TuiFieldErrorPipe, name: "tuiFieldError" }, { kind: "ngmodule", type: ScFormFieldsModule }, { kind: "component", type: ScSuggestionFieldComponent, selector: "sc-suggestion-field", inputs: ["type"], outputs: ["selectedClick"] }, { kind: "ngmodule", type: TuiInputModule }, { kind: "component", type: i3.TuiInputComponent, selector: "tui-input" }, { kind: "directive", type: i3.TuiInputDirective, selector: "tui-input" }, { kind: "ngmodule", type: TuiInputPhoneModule }, { 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: "directive", type: TuiLabel, selector: "label[tuiLabel]" }, { kind: "ngmodule", type: RecaptchaFormsModule }, { kind: "directive", type: i4$3.RecaptchaValueAccessorDirective, selector: "re-captcha[formControlName],re-captcha[formControl],re-captcha[ngModel]" }, { kind: "ngmodule", type: RecaptchaModule }, { kind: "component", type: i4$3.RecaptchaComponent, selector: "re-captcha", inputs: ["id", "siteKey", "theme", "type", "size", "tabIndex", "badge", "errorMode"], outputs: ["resolved", "error", "errored"], exportAs: ["reCaptcha"] }, { kind: "directive", type: TuiLink, selector: "a[tuiLink], button[tuiLink]", inputs: ["pseudo"] }, { kind: "directive", type: NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: TuiTextareaModule }, { kind: "component", type: i3.TuiTextareaComponent, selector: "tui-textarea", inputs: ["rows", "maxLength", "expandable"] }, { kind: "directive", type: i3.TuiTextareaDirective, selector: "tui-textarea" }, { kind: "component", type: TuiCheckbox, selector: "input[type=\"checkbox\"][tuiCheckbox]", inputs: ["size"] }, { kind: "directive", type: TuiTitle, selector: "[tuiTitle]", inputs: ["tuiTitle"] }, { 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: "component", type: TuiInputPhoneInternational, selector: "input[tuiInputPhoneInternational]", inputs: ["countrySearch", "countries", "countryIsoCode"], outputs: ["countryIsoCodeChange"] }, { kind: "component", type: i1$2.TuiTextfieldComponent, selector: "tui-textfield:not([multi])" }, { kind: "pipe", type: TuiSortCountriesPipe, name: "tuiSortCountries" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
11696
12617
|
}
|
|
11697
12618
|
__decorate([
|
|
11698
12619
|
tuiPure
|
|
@@ -11712,7 +12633,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
|
|
|
11712
12633
|
TuiButton,
|
|
11713
12634
|
TuiButtonLoading,
|
|
11714
12635
|
TuiLabel,
|
|
11715
|
-
|
|
12636
|
+
RecaptchaFormsModule,
|
|
12637
|
+
RecaptchaModule,
|
|
11716
12638
|
TuiLink,
|
|
11717
12639
|
NgIf,
|
|
11718
12640
|
TuiTextareaModule,
|
|
@@ -11722,7 +12644,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
|
|
|
11722
12644
|
TuiInputPhoneInternational,
|
|
11723
12645
|
...TuiTextfield,
|
|
11724
12646
|
TuiSortCountriesPipe,
|
|
11725
|
-
], changeDetection: ChangeDetectionStrategy.OnPush, template: "@let fieldsList = fields();\n<form\n *ngIf=\"fieldsList.length > 0\"\n [formGroup]=\"form\"\n (ngSubmit)=\"onSubmit.next()\"\n class=\"flex flex-col justify-between gap-4\"\n>\n <!-- \u0424\u0418\u041E -->\n <label\n *ngIf=\"fieldsList.includes('name')\"\n tuiLabel\n >\n \u0424\u0418\u041E\n <tui-input formControlName=\"name\"> \u0423\u043A\u0430\u0436\u0438\u0442\u0435 \u0412\u0430\u0448\u0438 \u0424.\u0418.\u041E. </tui-input>\n <tui-error\n formControlName=\"name\"\n [error]=\"[] | tuiFieldError | async\"\n />\n </label>\n\n <!-- \u0422\u0435\u043B\u0435\u0444\u043E\u043D -->\n @if (fieldsList.includes('phone')) {\n <label tuiLabel>\n \u0422\u0435\u043B\u0435\u0444\u043E\u043D\n <tui-textfield>\n <input\n formControlName=\"phone\"\n tuiInputPhoneInternational\n [countrySearch]=\"true\"\n [countries]=\"(countries | tuiSortCountries | async) || []\"\n autocomplete=\"phone\"\n />\n </tui-textfield>\n <tui-error\n formControlName=\"phone\"\n [error]=\"[] | tuiFieldError | async\"\n />\n </label>\n\n <label\n *ngIf=\"fieldsList.includes('isShouldUseMessenger') && form.controls.phone.value && isValidPhoneNumberForMessenger(form.controls.phone.value)\"\n tuiLabel\n >\n <input\n formControlName=\"isShouldUseMessenger\"\n tuiCheckbox\n type=\"checkbox\"\n />\n <span tuiTitle> \u041D\u0430\u043F\u0438\u0441\u0430\u0442\u044C \u0432 \u043C\u0435\u0441\u0441\u0435\u043D\u0434\u0436\u0435\u0440\u0430\u0445, \u0430 \u043D\u0435 \u0437\u0432\u043E\u043D\u0438\u0442\u044C </span>\n </label>\n }\n <!-- \u0410\u0434\u0440\u0435\u0441 \u044D\u043B\u0435\u043A\u0442\u0440\u043E\u043D\u043D\u043E\u0439 \u043F\u043E\u0447\u0442\u044B -->\n <label\n *ngIf=\"fieldsList.includes('email')\"\n tuiLabel\n >\n \u0410\u0434\u0440\u0435\u0441 \u044D\u043B\u0435\u043A\u0442\u0440\u043E\u043D\u043D\u043E\u0439 \u043F\u043E\u0447\u0442\u044B\n <tui-input formControlName=\"email\">\n \u0410\u0434\u0440\u0435\u0441 \u044D\u043B\u0435\u043A\u0442\u0440\u043E\u043D\u043D\u043E\u0439 \u043F\u043E\u0447\u0442\u044B\n <sc-suggestion-field\n *tuiDataList\n [type]=\"suggestionType.email\"\n />\n </tui-input>\n <tui-error\n formControlName=\"email\"\n [error]=\"[] | tuiFieldError | async\"\n />\n </label>\n\n <!-- \u041A\u043E\u043C\u043C\u0435\u043D\u0442\u0430\u0440\u0438\u0439 -->\n <label\n *ngIf=\"fieldsList.includes('message')\"\n tuiLabel\n >\n \u041A\u043E\u043C\u043C\u0435\u043D\u0442\u0430\u0440\u0438\u0439\n <tui-textarea formControlName=\"message\"> \u041A\u043E\u043C\u043C\u0435\u043D\u0442\u0430\u0440\u0438\u0439 </tui-textarea>\n <tui-error\n formControlName=\"message\"\n [error]=\"[] | tuiFieldError | async\"\n />\n </label>\n\n <!-- \u0424\u0430\u0439\u043B\u044B -->\n <div\n *ngIf=\"fieldsList.includes('files')\"\n class=\"flex flex-col gap-1\"\n >\n <label tuiInputFiles>\n <input\n #validator=\"tuiInputFilesValidator\"\n accept=\".doc,.docx,.pdf,image/*\"\n tuiInputFiles\n [formControl]=\"loadedFile\"\n [multiple]=\"true\"\n (reject)=\"onReject($event)\"\n />\n </label>\n\n <tui-error\n *ngIf=\"loadedFile.invalid\"\n [error]=\"['maxLength'] | tuiFieldError | async\"\n [formControl]=\"loadedFile\"\n />\n\n @let accepted = accepted$ | async;\n <tui-files\n *ngIf=\"accepted?.length || rejected.length\"\n class=\"tui-space_top-1\"\n >\n <tui-file\n *ngFor=\"let file of accepted\"\n [file]=\"file\"\n (remove)=\"onRemove(file)\"\n />\n <tui-file\n *ngFor=\"let file of rejected\"\n state=\"error\"\n [file]=\"(file | tuiFileRejected: validator | async) || file\"\n (remove)=\"onRemove(file)\"\n />\n </tui-files>\n </div>\n\n <!--
|
|
12647
|
+
], changeDetection: ChangeDetectionStrategy.OnPush, template: "@let fieldsList = fields();\n<form\n *ngIf=\"fieldsList.length > 0\"\n [formGroup]=\"form\"\n (ngSubmit)=\"onSubmit.next()\"\n class=\"flex flex-col justify-between gap-4\"\n>\n <!-- \u0424\u0418\u041E -->\n <label\n *ngIf=\"fieldsList.includes('name')\"\n tuiLabel\n >\n \u0424\u0418\u041E\n <tui-input formControlName=\"name\"> \u0423\u043A\u0430\u0436\u0438\u0442\u0435 \u0412\u0430\u0448\u0438 \u0424.\u0418.\u041E. </tui-input>\n <tui-error\n formControlName=\"name\"\n [error]=\"[] | tuiFieldError | async\"\n />\n </label>\n\n <!-- \u0422\u0435\u043B\u0435\u0444\u043E\u043D -->\n @if (fieldsList.includes('phone')) {\n <label tuiLabel>\n \u0422\u0435\u043B\u0435\u0444\u043E\u043D\n <tui-textfield>\n <input\n formControlName=\"phone\"\n tuiInputPhoneInternational\n [countrySearch]=\"true\"\n [countries]=\"(countries | tuiSortCountries | async) || []\"\n autocomplete=\"phone\"\n />\n </tui-textfield>\n <tui-error\n formControlName=\"phone\"\n [error]=\"[] | tuiFieldError | async\"\n />\n </label>\n\n <label\n *ngIf=\"fieldsList.includes('isShouldUseMessenger') && form.controls.phone.value && isValidPhoneNumberForMessenger(form.controls.phone.value)\"\n tuiLabel\n >\n <input\n formControlName=\"isShouldUseMessenger\"\n tuiCheckbox\n type=\"checkbox\"\n />\n <span tuiTitle> \u041D\u0430\u043F\u0438\u0441\u0430\u0442\u044C \u0432 \u043C\u0435\u0441\u0441\u0435\u043D\u0434\u0436\u0435\u0440\u0430\u0445, \u0430 \u043D\u0435 \u0437\u0432\u043E\u043D\u0438\u0442\u044C </span>\n </label>\n }\n <!-- \u0410\u0434\u0440\u0435\u0441 \u044D\u043B\u0435\u043A\u0442\u0440\u043E\u043D\u043D\u043E\u0439 \u043F\u043E\u0447\u0442\u044B -->\n <label\n *ngIf=\"fieldsList.includes('email')\"\n tuiLabel\n >\n \u0410\u0434\u0440\u0435\u0441 \u044D\u043B\u0435\u043A\u0442\u0440\u043E\u043D\u043D\u043E\u0439 \u043F\u043E\u0447\u0442\u044B\n <tui-input formControlName=\"email\">\n \u0410\u0434\u0440\u0435\u0441 \u044D\u043B\u0435\u043A\u0442\u0440\u043E\u043D\u043D\u043E\u0439 \u043F\u043E\u0447\u0442\u044B\n <sc-suggestion-field\n *tuiDataList\n [type]=\"suggestionType.email\"\n />\n </tui-input>\n <tui-error\n formControlName=\"email\"\n [error]=\"[] | tuiFieldError | async\"\n />\n </label>\n\n <!-- \u041A\u043E\u043C\u043C\u0435\u043D\u0442\u0430\u0440\u0438\u0439 -->\n <label\n *ngIf=\"fieldsList.includes('message')\"\n tuiLabel\n >\n \u041A\u043E\u043C\u043C\u0435\u043D\u0442\u0430\u0440\u0438\u0439\n <tui-textarea formControlName=\"message\"> \u041A\u043E\u043C\u043C\u0435\u043D\u0442\u0430\u0440\u0438\u0439 </tui-textarea>\n <tui-error\n formControlName=\"message\"\n [error]=\"[] | tuiFieldError | async\"\n />\n </label>\n\n <!-- \u0424\u0430\u0439\u043B\u044B -->\n <div\n *ngIf=\"fieldsList.includes('files')\"\n class=\"flex flex-col gap-1\"\n >\n <label tuiInputFiles>\n <input\n #validator=\"tuiInputFilesValidator\"\n accept=\".doc,.docx,.pdf,image/*\"\n tuiInputFiles\n [formControl]=\"loadedFile\"\n [multiple]=\"true\"\n (reject)=\"onReject($event)\"\n />\n </label>\n\n <tui-error\n *ngIf=\"loadedFile.invalid\"\n [error]=\"['maxLength'] | tuiFieldError | async\"\n [formControl]=\"loadedFile\"\n />\n\n @let accepted = accepted$ | async;\n <tui-files\n *ngIf=\"accepted?.length || rejected.length\"\n class=\"tui-space_top-1\"\n >\n <tui-file\n *ngFor=\"let file of accepted\"\n [file]=\"file\"\n (remove)=\"onRemove(file)\"\n />\n <tui-file\n *ngFor=\"let file of rejected\"\n state=\"error\"\n [file]=\"(file | tuiFileRejected: validator | async) || file\"\n (remove)=\"onRemove(file)\"\n />\n </tui-files>\n </div>\n\n <!-- recaptcha -->\n @if (form.get('recaptcha')) {\n <div\n class=\"mb-3 max-h-20 overflow-hidden duration-500\"\n [ngClass]=\"{ '!max-h-0 !-m-2': form.get('recaptcha')?.valid }\"\n >\n <re-captcha formControlName=\"recaptcha\" />\n </div>\n }\n\n <!-- \u042D\u043B\u0435\u043C\u0435\u043D\u0442\u044B \u0443\u043F\u0440\u0430\u0432\u043B\u0435\u043D\u0438\u044F -->\n <div class=\"flex flex-wrap items-center gap-3\">\n <button\n tuiButton\n [disabled]=\"form.invalid\"\n [loading]=\"!!(loading$ | async)\"\n type=\"submit\"\n iconStart=\"@tui.sc.send\"\n class=\"self-center\"\n >\n \u041E\u0442\u043F\u0440\u0430\u0432\u0438\u0442\u044C\n </button>\n <div class=\"grow basis-60 text-body-s\">\n \u041D\u0430\u0436\u0438\u043C\u0430\u044F \u043D\u0430 \u043A\u043D\u043E\u043F\u043A\u0443, \u0432\u044B \u0441\u043E\u0433\u043B\u0430\u0448\u0430\u0435\u0442\u0435\u0441\u044C \u0441\n <button\n tuiLink\n [pseudo]=\"true\"\n type=\"button\"\n (click)=\"context?.data?.onClickOfferHandler(); clickOffer.emit()\"\n >\n \u041F\u043E\u043B\u0438\u0442\u0438\u043A\u043E\u0439 \u043A\u043E\u043D\u0444\u0438\u0434\u0435\u043D\u0446\u0438\u0430\u043B\u044C\u043D\u043E\u0441\u0442\u0438\n </button>\n </div>\n </div>\n</form>\n" }]
|
|
11726
12648
|
}], ctorParameters: () => [], propDecorators: { isValidPhoneNumberForMessenger: [] } });
|
|
11727
12649
|
|
|
11728
12650
|
/**
|
|
@@ -12309,5 +13231,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
|
|
|
12309
13231
|
* Generated bundle index. Do not edit.
|
|
12310
13232
|
*/
|
|
12311
13233
|
|
|
12312
|
-
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,
|
|
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 };
|
|
12313
13235
|
//# sourceMappingURL=snabcentr-client-ui.mjs.map
|