ngx-t-forms 2.0.25 → 2.0.26

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.
Files changed (67) hide show
  1. package/fesm2022/{ngx-t-forms-calculated-field-rules.component-DiNSTxsW.mjs → ngx-t-forms-calculated-field-rules.component-Cs_Lhz_D.mjs} +5 -5
  2. package/fesm2022/{ngx-t-forms-calculated-field-rules.component-DiNSTxsW.mjs.map → ngx-t-forms-calculated-field-rules.component-Cs_Lhz_D.mjs.map} +1 -1
  3. package/fesm2022/{ngx-t-forms-chip-options-creator-editor.component-3FkGwVWB.mjs → ngx-t-forms-chip-options-creator-editor.component-DkTpG5yQ.mjs} +2 -2
  4. package/fesm2022/{ngx-t-forms-chip-options-creator-editor.component-3FkGwVWB.mjs.map → ngx-t-forms-chip-options-creator-editor.component-DkTpG5yQ.mjs.map} +1 -1
  5. package/fesm2022/{ngx-t-forms-config-mscoa-additional-inputs.component-tHaWIt1o.mjs → ngx-t-forms-config-mscoa-additional-inputs.component-DJ9d3Oy9.mjs} +4 -4
  6. package/fesm2022/{ngx-t-forms-config-mscoa-additional-inputs.component-tHaWIt1o.mjs.map → ngx-t-forms-config-mscoa-additional-inputs.component-DJ9d3Oy9.mjs.map} +1 -1
  7. package/fesm2022/{ngx-t-forms-data-source-picker.component-CxG1tQfC.mjs → ngx-t-forms-data-source-picker.component-CFQ_GVPG.mjs} +7 -7
  8. package/fesm2022/{ngx-t-forms-data-source-picker.component-CxG1tQfC.mjs.map → ngx-t-forms-data-source-picker.component-CFQ_GVPG.mjs.map} +1 -1
  9. package/fesm2022/{ngx-t-forms-document-list-label-config-editor.component-CsqYlgS6.mjs → ngx-t-forms-document-list-label-config-editor.component-CPF_BLRA.mjs} +6 -6
  10. package/fesm2022/{ngx-t-forms-document-list-label-config-editor.component-CsqYlgS6.mjs.map → ngx-t-forms-document-list-label-config-editor.component-CPF_BLRA.mjs.map} +1 -1
  11. package/fesm2022/{ngx-t-forms-form-input-selector.component-D-nVF7t7.mjs → ngx-t-forms-form-input-selector.component-C38FXT55.mjs} +5 -5
  12. package/fesm2022/{ngx-t-forms-form-input-selector.component-D-nVF7t7.mjs.map → ngx-t-forms-form-input-selector.component-C38FXT55.mjs.map} +1 -1
  13. package/fesm2022/{ngx-t-forms-form-json-view.component-B01nOklu.mjs → ngx-t-forms-form-json-view.component-C7i6JZ-l.mjs} +2 -2
  14. package/fesm2022/{ngx-t-forms-form-json-view.component-B01nOklu.mjs.map → ngx-t-forms-form-json-view.component-C7i6JZ-l.mjs.map} +1 -1
  15. package/fesm2022/ngx-t-forms-form-section-stepper.component-CJz0yr78.mjs +264 -0
  16. package/fesm2022/ngx-t-forms-form-section-stepper.component-CJz0yr78.mjs.map +1 -0
  17. package/fesm2022/ngx-t-forms-forms-builder-menu.component-G3Agd-vE.mjs +349 -0
  18. package/fesm2022/ngx-t-forms-forms-builder-menu.component-G3Agd-vE.mjs.map +1 -0
  19. package/fesm2022/{ngx-t-forms-input-editor.component-DyQBfgio.mjs → ngx-t-forms-input-editor.component-B12zbXE1.mjs} +5 -5
  20. package/fesm2022/{ngx-t-forms-input-editor.component-DyQBfgio.mjs.map → ngx-t-forms-input-editor.component-B12zbXE1.mjs.map} +1 -1
  21. package/fesm2022/{ngx-t-forms-mat-chip-list-editor.component-BCoTHeK3.mjs → ngx-t-forms-mat-chip-list-editor.component-dIY3BX1u.mjs} +4 -4
  22. package/fesm2022/{ngx-t-forms-mat-chip-list-editor.component-BCoTHeK3.mjs.map → ngx-t-forms-mat-chip-list-editor.component-dIY3BX1u.mjs.map} +1 -1
  23. package/fesm2022/{ngx-t-forms-mat-slider-editor.component-WDd5lc_2.mjs → ngx-t-forms-mat-slider-editor.component-yMRCg8fo.mjs} +2 -2
  24. package/fesm2022/{ngx-t-forms-mat-slider-editor.component-WDd5lc_2.mjs.map → ngx-t-forms-mat-slider-editor.component-yMRCg8fo.mjs.map} +1 -1
  25. package/fesm2022/{ngx-t-forms-mat-slider-toggle-editor.component-DGac4Eja.mjs → ngx-t-forms-mat-slider-toggle-editor.component-0aUVP9A6.mjs} +4 -4
  26. package/fesm2022/ngx-t-forms-mat-slider-toggle-editor.component-0aUVP9A6.mjs.map +1 -0
  27. package/fesm2022/{ngx-t-forms-missing-form-configs.component-sJVu5SkY.mjs → ngx-t-forms-missing-form-configs.component-CsPMQZqg.mjs} +2 -2
  28. package/fesm2022/{ngx-t-forms-missing-form-configs.component-sJVu5SkY.mjs.map → ngx-t-forms-missing-form-configs.component-CsPMQZqg.mjs.map} +1 -1
  29. package/fesm2022/{ngx-t-forms-mscoa-chart-toolbar.component-CF5DxLZG.mjs → ngx-t-forms-mscoa-chart-toolbar.component-D8a9-6lt.mjs} +2 -2
  30. package/fesm2022/{ngx-t-forms-mscoa-chart-toolbar.component-CF5DxLZG.mjs.map → ngx-t-forms-mscoa-chart-toolbar.component-D8a9-6lt.mjs.map} +1 -1
  31. package/fesm2022/{ngx-t-forms-mscoa-error-display.component-D9zKeT7Z.mjs → ngx-t-forms-mscoa-error-display.component-DwbyXd_D.mjs} +2 -2
  32. package/fesm2022/{ngx-t-forms-mscoa-error-display.component-D9zKeT7Z.mjs.map → ngx-t-forms-mscoa-error-display.component-DwbyXd_D.mjs.map} +1 -1
  33. package/fesm2022/{ngx-t-forms-mscoa-segment-config.component-BIr1g5cG.mjs → ngx-t-forms-mscoa-segment-config.component-BXPghOuH.mjs} +4 -4
  34. package/fesm2022/{ngx-t-forms-mscoa-segment-config.component-BIr1g5cG.mjs.map → ngx-t-forms-mscoa-segment-config.component-BXPghOuH.mjs.map} +1 -1
  35. package/fesm2022/{ngx-t-forms-mscoa-temporary-hint.component-DgdZwE72.mjs → ngx-t-forms-mscoa-temporary-hint.component-CBRfWvfD.mjs} +2 -2
  36. package/fesm2022/{ngx-t-forms-mscoa-temporary-hint.component-DgdZwE72.mjs.map → ngx-t-forms-mscoa-temporary-hint.component-CBRfWvfD.mjs.map} +1 -1
  37. package/fesm2022/{ngx-t-forms-ngx-t-forms-A9_4GxP6.mjs → ngx-t-forms-ngx-t-forms-CD9InaXz.mjs} +99 -73
  38. package/fesm2022/ngx-t-forms-ngx-t-forms-CD9InaXz.mjs.map +1 -0
  39. package/fesm2022/{ngx-t-forms-pipeline-generator.component-CwCwl53b.mjs → ngx-t-forms-pipeline-generator.component-QE-I58e4.mjs} +5 -5
  40. package/fesm2022/{ngx-t-forms-pipeline-generator.component-CwCwl53b.mjs.map → ngx-t-forms-pipeline-generator.component-QE-I58e4.mjs.map} +1 -1
  41. package/fesm2022/{ngx-t-forms-record-list-manager.component-BYyr0yzb.mjs → ngx-t-forms-record-list-manager.component-D4wlLtdc.mjs} +2 -2
  42. package/fesm2022/{ngx-t-forms-record-list-manager.component-BYyr0yzb.mjs.map → ngx-t-forms-record-list-manager.component-D4wlLtdc.mjs.map} +1 -1
  43. package/fesm2022/ngx-t-forms-required-inputs.component-BoqmVDp2.mjs +192 -0
  44. package/fesm2022/ngx-t-forms-required-inputs.component-BoqmVDp2.mjs.map +1 -0
  45. package/fesm2022/{ngx-t-forms-rest-api-call-setup.component-CQeww1qM.mjs → ngx-t-forms-rest-api-call-setup.component-CtfudSGT.mjs} +2 -2
  46. package/fesm2022/{ngx-t-forms-rest-api-call-setup.component-CQeww1qM.mjs.map → ngx-t-forms-rest-api-call-setup.component-CtfudSGT.mjs.map} +1 -1
  47. package/fesm2022/{ngx-t-forms-section-report.component-DGwgq4QR.mjs → ngx-t-forms-section-report.component-Dntm1m5b.mjs} +2 -2
  48. package/fesm2022/{ngx-t-forms-section-report.component-DGwgq4QR.mjs.map → ngx-t-forms-section-report.component-Dntm1m5b.mjs.map} +1 -1
  49. package/fesm2022/{ngx-t-forms-selection-options-editor.component-CqAxZnEp.mjs → ngx-t-forms-selection-options-editor.component-Cc_qCtW2.mjs} +4 -4
  50. package/fesm2022/{ngx-t-forms-selection-options-editor.component-CqAxZnEp.mjs.map → ngx-t-forms-selection-options-editor.component-Cc_qCtW2.mjs.map} +1 -1
  51. package/fesm2022/{ngx-t-forms-t-workflow-picker.component-Cz6YZMia.mjs → ngx-t-forms-t-workflow-picker.component-xvX8t0pu.mjs} +4 -4
  52. package/fesm2022/{ngx-t-forms-t-workflow-picker.component-Cz6YZMia.mjs.map → ngx-t-forms-t-workflow-picker.component-xvX8t0pu.mjs.map} +1 -1
  53. package/fesm2022/{ngx-t-forms-validators-config.component-CL73KUxw.mjs → ngx-t-forms-validators-config.component-D6JG1p81.mjs} +6 -6
  54. package/fesm2022/{ngx-t-forms-validators-config.component-CL73KUxw.mjs.map → ngx-t-forms-validators-config.component-D6JG1p81.mjs.map} +1 -1
  55. package/fesm2022/ngx-t-forms.mjs +1 -1
  56. package/lib/components/form-builder/elements/form-submissions-config/form-submissions-config.component.d.ts +1 -1
  57. package/lib/components/forms/forms.component.d.ts +7 -0
  58. package/lib/components/t-dynamic-data-edit/elements/required-inputs/required-inputs.component.d.ts +1 -0
  59. package/package.json +1 -1
  60. package/fesm2022/ngx-t-forms-form-section-stepper.component-Bg9eGmgN.mjs +0 -267
  61. package/fesm2022/ngx-t-forms-form-section-stepper.component-Bg9eGmgN.mjs.map +0 -1
  62. package/fesm2022/ngx-t-forms-forms-builder-menu.component-jn9-yVbI.mjs +0 -348
  63. package/fesm2022/ngx-t-forms-forms-builder-menu.component-jn9-yVbI.mjs.map +0 -1
  64. package/fesm2022/ngx-t-forms-mat-slider-toggle-editor.component-DGac4Eja.mjs.map +0 -1
  65. package/fesm2022/ngx-t-forms-ngx-t-forms-A9_4GxP6.mjs.map +0 -1
  66. package/fesm2022/ngx-t-forms-required-inputs.component-pv5pqFhh.mjs +0 -188
  67. package/fesm2022/ngx-t-forms-required-inputs.component-pv5pqFhh.mjs.map +0 -1
@@ -0,0 +1,349 @@
1
+ import * as i2$2 from '@angular/common';
2
+ import { CommonModule } from '@angular/common';
3
+ import * as i0 from '@angular/core';
4
+ import { inject, Component, computed } from '@angular/core';
5
+ import { g as getAvatar, F as FormsStoreService, M as MatModulesModule, D as DaysAgoPipe, N as NGX_T_FORMS_CONFIG_TOKEN, t as testAgainstItem, a as assignDeepPropertyToObject, T as TDynamicDataEditComponent } from './ngx-t-forms-ngx-t-forms-CD9InaXz.mjs';
6
+ import { map, combineLatest, take } from 'rxjs';
7
+ import * as i1$1 from '@angular/material/dialog';
8
+ import { MatDialogRef, MAT_DIALOG_DATA, MatDialog } from '@angular/material/dialog';
9
+ import { MatSnackBar } from '@angular/material/snack-bar';
10
+ import * as i2 from '@angular/material/button';
11
+ import * as i2$1 from '@angular/material/icon';
12
+ import * as i3 from '@angular/material/progress-spinner';
13
+ import * as i8 from '@angular/material/tooltip';
14
+ import { ElementEditorTypes, SpecialElementKeys, validateApiDataFetchingConfiguration } from 'ngx-t-forms-types';
15
+ import { toSignal } from '@angular/core/rxjs-interop';
16
+ import * as i1 from '@angular/cdk/clipboard';
17
+ import * as i4 from '@angular/material/divider';
18
+ import * as i5 from '@angular/material/expansion';
19
+ import * as i2$3 from '@angular/material/select';
20
+ import * as i3$2 from '@angular/material/badge';
21
+ import * as i7 from '@angular/material/list';
22
+ import * as i2$5 from '@angular/material/menu';
23
+ import * as i2$4 from '@angular/cdk/drag-drop';
24
+ import * as i3$1 from '@angular/material/card';
25
+ import * as i10 from '@angular/material/toolbar';
26
+
27
+ class SelectFormTemplateComponent {
28
+ constructor(store) {
29
+ this.store = store;
30
+ this.selectForms$ = this.store.selectors.selectForms$;
31
+ this.selectLoadingForms$ = this.store.selectors.selectLoadingForms$;
32
+ this.dialogRef = inject((MatDialogRef));
33
+ this.data = inject(MAT_DIALOG_DATA);
34
+ this.dialog = inject(MatDialog);
35
+ this.snackBar = inject(MatSnackBar);
36
+ }
37
+ ngOnInit() {
38
+ this.store.effects.loadForms$();
39
+ }
40
+ close() {
41
+ this.dialogRef.close();
42
+ }
43
+ selectForm(form) {
44
+ this.store.actionsFormBuilder.applyFormTemplate(form);
45
+ this.dialogRef.close(form);
46
+ }
47
+ getFormAvatar(formTitle) {
48
+ return getAvatar(formTitle);
49
+ }
50
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: SelectFormTemplateComponent, deps: [{ token: FormsStoreService }], target: i0.ɵɵFactoryTarget.Component }); }
51
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.17", type: SelectFormTemplateComponent, isStandalone: true, selector: "lib-select-form-template", ngImport: i0, template: "<div class=\"template-select-dialog\">\r\n <!-- Header: icon + title + subtitle per design rule \u00A74 -->\r\n <header class=\"dialog-header\">\r\n <div class=\"header-icon\" aria-hidden=\"true\">\r\n <mat-icon>description</mat-icon>\r\n </div>\r\n <div class=\"header-text\">\r\n <h1 class=\"header-title\">Choose a Form Template</h1>\r\n <p class=\"header-subtitle\">\r\n Select a pre-built form template to start with. You can customize and save it as your own form after copying it to the editor.\r\n </p>\r\n </div>\r\n </header>\r\n\r\n <section class=\"dialog-content\">\r\n @if (selectLoadingForms$ | async) {\r\n <div class=\"loading-state\">\r\n <mat-spinner diameter=\"40\" aria-label=\"Loading form templates\"></mat-spinner>\r\n <span class=\"loading-text\">Loading form templates...</span>\r\n </div>\r\n } @else {\r\n <ul class=\"form-template-list\" aria-label=\"Available form templates\">\r\n @for (form of selectForms$ | async; track form._id) {\r\n <li>\r\n <button\r\n type=\"button\"\r\n class=\"form-list-item\"\r\n (click)=\"selectForm(form)\"\r\n [matTooltip]=\"'Click to select this template'\"\r\n [attr.aria-label]=\"'Select template: ' + form.formTitle\">\r\n <img\r\n class=\"form-avatar\"\r\n [class.form-avatar--error]=\"form.errors.length > 0\"\r\n [src]=\"getFormAvatar(form.formTitle)\"\r\n [alt]=\"''\"\r\n aria-hidden=\"true\">\r\n <div class=\"form-item-content\">\r\n <span class=\"form-title\">{{ form.formTitle | titlecase }}</span>\r\n <div class=\"form-metadata\">\r\n @if (form.errors.length > 0) {\r\n <span class=\"metadata-item\">\r\n <mat-icon color=\"warn\" aria-hidden=\"true\">error</mat-icon>\r\n <small>{{ form.errors.length }}</small>\r\n </span>\r\n <span class=\"metadata-divider\" aria-hidden=\"true\">\u2022</span>\r\n }\r\n <span class=\"metadata-item\">\r\n <mat-icon aria-hidden=\"true\">add_circle_outline</mat-icon>\r\n <small>{{ form.createdAt | daysAgo }}</small>\r\n </span>\r\n <span class=\"metadata-divider\" aria-hidden=\"true\">\u2022</span>\r\n <span class=\"metadata-item\">\r\n <mat-icon aria-hidden=\"true\">update</mat-icon>\r\n <small>{{ form.updatedAt | daysAgo }}</small>\r\n </span>\r\n </div>\r\n </div>\r\n </button>\r\n </li>\r\n }\r\n </ul>\r\n\r\n @if ((selectForms$ | async)?.length === 0) {\r\n <div class=\"empty-state\">\r\n <mat-icon class=\"empty-state-icon\" aria-hidden=\"true\">folder_off</mat-icon>\r\n <p>No form templates available.</p>\r\n </div>\r\n }\r\n }\r\n </section>\r\n\r\n <footer class=\"dialog-actions\">\r\n <button mat-button (click)=\"close()\" aria-label=\"Close dialog\">\r\n Close\r\n </button>\r\n </footer>\r\n</div>\r\n", styles: ["@charset \"UTF-8\";.template-select-dialog{display:flex;flex-direction:column;min-width:28rem;max-width:36rem;max-height:calc(100vh - 24px);background:var(--mat-sys-surface, var(--mat-app-surface));overflow:hidden}.dialog-header{display:flex;align-items:flex-start;gap:1rem;padding:1.5rem 1.5rem 0;flex-shrink:0}.header-icon{width:2.75rem;height:2.75rem;border-radius:8px;background:color-mix(in srgb,var(--mat-sys-primary, #6750a4) 12%,transparent);display:flex;align-items:center;justify-content:center;flex-shrink:0}.header-icon mat-icon{font-size:1.5rem;width:1.5rem;height:1.5rem;color:var(--mat-sys-primary, #6750a4)}.header-text{flex:1;min-width:0}.header-title{font-size:1.25rem;font-weight:600;letter-spacing:-.01em;color:var(--mat-sys-on-surface, #1c1b1f);margin:0;line-height:1.3}.header-subtitle{font-size:.875rem;color:var(--mat-sys-on-surface-variant, #49454f);line-height:1.4;margin-top:.25rem;margin-bottom:0}.dialog-content{padding:1rem 1.5rem 1.5rem;overflow-y:auto;max-height:70vh;flex:1;min-height:0}.loading-state{display:flex;flex-direction:column;align-items:center;justify-content:center;padding:1.5rem;gap:1rem;min-height:12rem}.loading-text{font-size:.875rem;color:var(--mat-sys-on-surface-variant, #49454f)}.form-template-list{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:.5rem}.form-list-item{display:flex;align-items:flex-start;gap:1rem;width:100%;padding:.5rem 1.25rem;min-height:2.25rem;border:1px solid color-mix(in srgb,var(--mat-sys-outline, #79747e) 15%,transparent);border-radius:8px;background:var(--mat-sys-surface-container, var(--mat-app-surface-container-low));color:var(--mat-sys-on-surface, #1c1b1f);font-size:.875rem;text-align:left;cursor:pointer;transition:border-color .15s,box-shadow .15s,background-color .15s;box-shadow:0 2px 8px #00000014}.form-list-item:hover{border-color:var(--mat-sys-primary, #6750a4);background:color-mix(in srgb,var(--mat-sys-primary, #6750a4) 6%,transparent)}.form-list-item:focus-visible{outline:none;border-color:var(--mat-sys-primary, #6750a4);box-shadow:0 0 0 2px color-mix(in srgb,var(--mat-sys-primary, #6750a4) 25%,transparent)}.form-list-item:active{background:color-mix(in srgb,var(--mat-sys-primary, #6750a4) 12%,transparent)}@media (prefers-color-scheme: dark){.form-list-item{box-shadow:0 1px 3px #0000004d}}.form-avatar{width:2.5rem;height:2.5rem;border-radius:6px;object-fit:cover;flex-shrink:0;display:block}.form-avatar.form-avatar--error{outline:2px solid var(--mat-sys-error, #b3261e);outline-offset:2px}.form-item-content{flex:1;min-width:0;display:flex;flex-direction:column;gap:.25rem}.form-title{font-weight:600;font-size:.9375rem}.form-metadata{display:flex;align-items:center;flex-wrap:wrap;gap:.25rem;font-size:.75rem;color:var(--mat-sys-on-surface-variant, #49454f);line-height:1.35}.form-metadata .metadata-divider{margin:0 .25rem;opacity:.7}.form-metadata .metadata-item{display:inline-flex;align-items:center;gap:.25rem}.form-metadata .metadata-item mat-icon{font-size:.875rem;width:.875rem;height:.875rem}.empty-state{display:flex;flex-direction:column;align-items:center;justify-content:center;gap:.5rem;padding:1.5rem;background:var(--mat-sys-surface-container, var(--mat-app-surface-container-low));border-radius:8px;text-align:center}.empty-state p{font-size:.875rem;color:var(--mat-sys-on-surface-variant, #49454f);margin:0}.empty-state-icon{font-size:2rem;width:2rem;height:2rem;color:var(--mat-sys-on-surface-variant, #49454f);opacity:.7}.dialog-actions{display:flex;justify-content:flex-end;align-items:center;gap:.75rem;padding:1rem 1.5rem 1.25rem;flex-shrink:0;border-top:1px solid color-mix(in srgb,var(--mat-sys-outline, #79747e) 15%,transparent)}\n"], dependencies: [{ kind: "ngmodule", type: MatModulesModule }, { kind: "component", type: i2.MatButton, selector: " button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button] ", exportAs: ["matButton"] }, { kind: "component", type: i2$1.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "component", type: i3.MatProgressSpinner, selector: "mat-progress-spinner, mat-spinner", inputs: ["color", "mode", "value", "diameter", "strokeWidth"], exportAs: ["matProgressSpinner"] }, { kind: "directive", type: i8.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { kind: "ngmodule", type: CommonModule }, { kind: "pipe", type: i2$2.AsyncPipe, name: "async" }, { kind: "pipe", type: i2$2.TitleCasePipe, name: "titlecase" }, { kind: "pipe", type: DaysAgoPipe, name: "daysAgo" }] }); }
52
+ }
53
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: SelectFormTemplateComponent, decorators: [{
54
+ type: Component,
55
+ args: [{ selector: 'lib-select-form-template', standalone: true, imports: [MatModulesModule, CommonModule, DaysAgoPipe], template: "<div class=\"template-select-dialog\">\r\n <!-- Header: icon + title + subtitle per design rule \u00A74 -->\r\n <header class=\"dialog-header\">\r\n <div class=\"header-icon\" aria-hidden=\"true\">\r\n <mat-icon>description</mat-icon>\r\n </div>\r\n <div class=\"header-text\">\r\n <h1 class=\"header-title\">Choose a Form Template</h1>\r\n <p class=\"header-subtitle\">\r\n Select a pre-built form template to start with. You can customize and save it as your own form after copying it to the editor.\r\n </p>\r\n </div>\r\n </header>\r\n\r\n <section class=\"dialog-content\">\r\n @if (selectLoadingForms$ | async) {\r\n <div class=\"loading-state\">\r\n <mat-spinner diameter=\"40\" aria-label=\"Loading form templates\"></mat-spinner>\r\n <span class=\"loading-text\">Loading form templates...</span>\r\n </div>\r\n } @else {\r\n <ul class=\"form-template-list\" aria-label=\"Available form templates\">\r\n @for (form of selectForms$ | async; track form._id) {\r\n <li>\r\n <button\r\n type=\"button\"\r\n class=\"form-list-item\"\r\n (click)=\"selectForm(form)\"\r\n [matTooltip]=\"'Click to select this template'\"\r\n [attr.aria-label]=\"'Select template: ' + form.formTitle\">\r\n <img\r\n class=\"form-avatar\"\r\n [class.form-avatar--error]=\"form.errors.length > 0\"\r\n [src]=\"getFormAvatar(form.formTitle)\"\r\n [alt]=\"''\"\r\n aria-hidden=\"true\">\r\n <div class=\"form-item-content\">\r\n <span class=\"form-title\">{{ form.formTitle | titlecase }}</span>\r\n <div class=\"form-metadata\">\r\n @if (form.errors.length > 0) {\r\n <span class=\"metadata-item\">\r\n <mat-icon color=\"warn\" aria-hidden=\"true\">error</mat-icon>\r\n <small>{{ form.errors.length }}</small>\r\n </span>\r\n <span class=\"metadata-divider\" aria-hidden=\"true\">\u2022</span>\r\n }\r\n <span class=\"metadata-item\">\r\n <mat-icon aria-hidden=\"true\">add_circle_outline</mat-icon>\r\n <small>{{ form.createdAt | daysAgo }}</small>\r\n </span>\r\n <span class=\"metadata-divider\" aria-hidden=\"true\">\u2022</span>\r\n <span class=\"metadata-item\">\r\n <mat-icon aria-hidden=\"true\">update</mat-icon>\r\n <small>{{ form.updatedAt | daysAgo }}</small>\r\n </span>\r\n </div>\r\n </div>\r\n </button>\r\n </li>\r\n }\r\n </ul>\r\n\r\n @if ((selectForms$ | async)?.length === 0) {\r\n <div class=\"empty-state\">\r\n <mat-icon class=\"empty-state-icon\" aria-hidden=\"true\">folder_off</mat-icon>\r\n <p>No form templates available.</p>\r\n </div>\r\n }\r\n }\r\n </section>\r\n\r\n <footer class=\"dialog-actions\">\r\n <button mat-button (click)=\"close()\" aria-label=\"Close dialog\">\r\n Close\r\n </button>\r\n </footer>\r\n</div>\r\n", styles: ["@charset \"UTF-8\";.template-select-dialog{display:flex;flex-direction:column;min-width:28rem;max-width:36rem;max-height:calc(100vh - 24px);background:var(--mat-sys-surface, var(--mat-app-surface));overflow:hidden}.dialog-header{display:flex;align-items:flex-start;gap:1rem;padding:1.5rem 1.5rem 0;flex-shrink:0}.header-icon{width:2.75rem;height:2.75rem;border-radius:8px;background:color-mix(in srgb,var(--mat-sys-primary, #6750a4) 12%,transparent);display:flex;align-items:center;justify-content:center;flex-shrink:0}.header-icon mat-icon{font-size:1.5rem;width:1.5rem;height:1.5rem;color:var(--mat-sys-primary, #6750a4)}.header-text{flex:1;min-width:0}.header-title{font-size:1.25rem;font-weight:600;letter-spacing:-.01em;color:var(--mat-sys-on-surface, #1c1b1f);margin:0;line-height:1.3}.header-subtitle{font-size:.875rem;color:var(--mat-sys-on-surface-variant, #49454f);line-height:1.4;margin-top:.25rem;margin-bottom:0}.dialog-content{padding:1rem 1.5rem 1.5rem;overflow-y:auto;max-height:70vh;flex:1;min-height:0}.loading-state{display:flex;flex-direction:column;align-items:center;justify-content:center;padding:1.5rem;gap:1rem;min-height:12rem}.loading-text{font-size:.875rem;color:var(--mat-sys-on-surface-variant, #49454f)}.form-template-list{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:.5rem}.form-list-item{display:flex;align-items:flex-start;gap:1rem;width:100%;padding:.5rem 1.25rem;min-height:2.25rem;border:1px solid color-mix(in srgb,var(--mat-sys-outline, #79747e) 15%,transparent);border-radius:8px;background:var(--mat-sys-surface-container, var(--mat-app-surface-container-low));color:var(--mat-sys-on-surface, #1c1b1f);font-size:.875rem;text-align:left;cursor:pointer;transition:border-color .15s,box-shadow .15s,background-color .15s;box-shadow:0 2px 8px #00000014}.form-list-item:hover{border-color:var(--mat-sys-primary, #6750a4);background:color-mix(in srgb,var(--mat-sys-primary, #6750a4) 6%,transparent)}.form-list-item:focus-visible{outline:none;border-color:var(--mat-sys-primary, #6750a4);box-shadow:0 0 0 2px color-mix(in srgb,var(--mat-sys-primary, #6750a4) 25%,transparent)}.form-list-item:active{background:color-mix(in srgb,var(--mat-sys-primary, #6750a4) 12%,transparent)}@media (prefers-color-scheme: dark){.form-list-item{box-shadow:0 1px 3px #0000004d}}.form-avatar{width:2.5rem;height:2.5rem;border-radius:6px;object-fit:cover;flex-shrink:0;display:block}.form-avatar.form-avatar--error{outline:2px solid var(--mat-sys-error, #b3261e);outline-offset:2px}.form-item-content{flex:1;min-width:0;display:flex;flex-direction:column;gap:.25rem}.form-title{font-weight:600;font-size:.9375rem}.form-metadata{display:flex;align-items:center;flex-wrap:wrap;gap:.25rem;font-size:.75rem;color:var(--mat-sys-on-surface-variant, #49454f);line-height:1.35}.form-metadata .metadata-divider{margin:0 .25rem;opacity:.7}.form-metadata .metadata-item{display:inline-flex;align-items:center;gap:.25rem}.form-metadata .metadata-item mat-icon{font-size:.875rem;width:.875rem;height:.875rem}.empty-state{display:flex;flex-direction:column;align-items:center;justify-content:center;gap:.5rem;padding:1.5rem;background:var(--mat-sys-surface-container, var(--mat-app-surface-container-low));border-radius:8px;text-align:center}.empty-state p{font-size:.875rem;color:var(--mat-sys-on-surface-variant, #49454f);margin:0}.empty-state-icon{font-size:2rem;width:2rem;height:2rem;color:var(--mat-sys-on-surface-variant, #49454f);opacity:.7}.dialog-actions{display:flex;justify-content:flex-end;align-items:center;gap:.75rem;padding:1rem 1.5rem 1.25rem;flex-shrink:0;border-top:1px solid color-mix(in srgb,var(--mat-sys-outline, #79747e) 15%,transparent)}\n"] }]
56
+ }], ctorParameters: () => [{ type: FormsStoreService }] });
57
+
58
+ function SubmissionAPIListItemConfig(environment) {
59
+ return [
60
+ {
61
+ id: '16140923-0542-4d5d-9b63-186c1e91b1a7',
62
+ name: SpecialElementKeys.MatOptionsApiCall,
63
+ editType: ElementEditorTypes.ApiCall,
64
+ required: true,
65
+ postmanCollectionConfig: {
66
+ collectionUrl: environment['POSTMAN_COLLECTION_URL'],
67
+ collectionKey: environment['POSTMAN_COLLECTION_KEY']
68
+ },
69
+ hint: `APIs are loaded from Postman collection. Configure required headers, payload format, and response mapping before use. Ensure API is documented in Postman.`,
70
+ deepBind: ['data'],
71
+ label: 'Select form submission api',
72
+ },
73
+ {
74
+ name: SpecialElementKeys.Default,
75
+ deepBind: [
76
+ 'data',
77
+ 'backEndConfig',
78
+ 'minimumInputRequired',
79
+ ],
80
+ additionalTest: [
81
+ {
82
+ testType: 'exists',
83
+ deepBind: [
84
+ 'data',
85
+ 'backEndConfig',
86
+ 'minimumInputRequired',
87
+ ],
88
+ }
89
+ ],
90
+ hint: `These are payload fields that are required to be filled in order to fetch data from the selected source`,
91
+ editType: ElementEditorTypes.RequiredInputs,
92
+ label: 'Set up required inputs',
93
+ id: "2d0f1e18-897b-4c28-97bc-3078be09f8ee"
94
+ },
95
+ {
96
+ name: SpecialElementKeys.Default,
97
+ deepBind: ['data', 'projectFormData'],
98
+ editType: ElementEditorTypes.Input,
99
+ label: 'Project form data',
100
+ hint: 'JSON template for the payload. Use {$formValue} to include submitted form data. Example: {"data": {$formValue}}',
101
+ id: "$2d0f1e18-897b-4c28-97bc-3078be09f8ee"
102
+ },
103
+ ];
104
+ }
105
+ const SubmissionAPIOtherConfig = [
106
+ {
107
+ id: "7acaa5d1-fb37-4dc0-9f1a-53b6f36534e0",
108
+ name: SpecialElementKeys.Default,
109
+ deepBind: ['submissionHandle', 'submissionMessage'],
110
+ editType: ElementEditorTypes.Input,
111
+ label: "Submission Message",
112
+ hint: "Message shown to the user after successful form submission.",
113
+ },
114
+ {
115
+ id: "5636c16c-be58-48d9-b701-81fe05573a39",
116
+ name: SpecialElementKeys.Default,
117
+ deepBind: ['submissionHandle', 'innerComponentShowSubmitButton'],
118
+ editType: ElementEditorTypes.Toggle,
119
+ label: "Show submit button in inner component form",
120
+ },
121
+ {
122
+ id: "c9103faa-eeba-4cf3-b3d0-f25fd05ccb3c",
123
+ name: SpecialElementKeys.Default,
124
+ deepBind: ['systemDefault'],
125
+ editType: ElementEditorTypes.Toggle,
126
+ label: "Is system default",
127
+ }
128
+ ];
129
+ const SubmissionDialogConfig = [
130
+ {
131
+ titleIcon: 'send',
132
+ id: '44023662-7343-4c93-999d-fc03c54438c3',
133
+ title: 'Form Submission Endpoint',
134
+ description: `Choose where to send your form data after submission. Connect to your database, data pipeline, or
135
+ third-party service.`,
136
+ primaryKey: 'submissionAPI',
137
+ elements: SubmissionAPIOtherConfig
138
+ },
139
+ {
140
+ titleIcon: 'rule',
141
+ title: 'Data Validation Rules',
142
+ description: `Define validation rules to ensure data quality before form submission.
143
+ <br>
144
+ <strong>Note:</strong> All validation endpoints must return a boolean value -
145
+ <code>true</code> indicates valid data that will be submitted.`,
146
+ primaryKey: 'canSubmitAPI',
147
+ id: 'acac6045-5b13-4c8d-a7ee-da194ebbad41',
148
+ }
149
+ ];
150
+
151
+ class FormSubmissionsConfigComponent {
152
+ getPanelError(panel) {
153
+ return computed(() => {
154
+ const form = this.form();
155
+ if (panel.primaryKey === 'submissionAPI') {
156
+ return (form?.submissionHandle?.submissionAPI || []).length === 0 ? 'At least one submission API is required' : undefined;
157
+ }
158
+ return undefined;
159
+ });
160
+ }
161
+ getFormApis(key) {
162
+ return computed(() => {
163
+ const form = this.form();
164
+ return form?.submissionHandle?.[key] || [];
165
+ });
166
+ }
167
+ // private dialogRef: MatDialogRef<FormSubmissionsConfigComponent>,
168
+ constructor() {
169
+ this.NGX_T_FORMS_CONFIG = inject(NGX_T_FORMS_CONFIG_TOKEN);
170
+ this.panels = computed(() => {
171
+ return SubmissionDialogConfig.map((panel) => {
172
+ const error = this.getPanelError(panel)();
173
+ return ({
174
+ ...panel,
175
+ error
176
+ });
177
+ });
178
+ });
179
+ this._data = inject(MAT_DIALOG_DATA);
180
+ this.store = this._data.store;
181
+ this.form = toSignal(this.store.selectors.selectFormInEdit$.pipe(map((form) => form?.form)), {
182
+ initialValue: undefined
183
+ });
184
+ this.formInputs = toSignal(this.store.formBuilderSelectors.getAllFormInputs$, {
185
+ initialValue: []
186
+ });
187
+ this.keyInEdit = undefined;
188
+ this.formValidationError = toSignal(this.store.formBuilderSelectors.formErrors$, {
189
+ initialValue: []
190
+ });
191
+ this.computedErrors = (element) => {
192
+ return computed(() => {
193
+ const allErrors = this.formValidationError();
194
+ const errors = allErrors?.filter(error => error.key === element.deepBind?.join('.')) || [];
195
+ return [...[], ...errors];
196
+ });
197
+ };
198
+ this.formInputChange = (value, ele) => this.store.actionsFormBuilder.formInputChange(ele, value);
199
+ this.matDialog = inject(MatDialog);
200
+ }
201
+ get submissionApiListItemConfigElements() {
202
+ return SubmissionAPIListItemConfig(this.NGX_T_FORMS_CONFIG).filter((el) => {
203
+ if (!el.additionalTest) {
204
+ return true;
205
+ }
206
+ return el.additionalTest.every((test) => {
207
+ return testAgainstItem(test, this.dataPlaceholder || { data: {} });
208
+ });
209
+ });
210
+ }
211
+ validationErrors(element) {
212
+ const val = validateApiDataFetchingConfiguration((this.dataPlaceholder || {})).map((error) => ({
213
+ message: error.message,
214
+ key: 'data.' + error.key
215
+ }));
216
+ const errors = val?.filter(error => error.key === element.deepBind?.join('.')) || [];
217
+ return errors;
218
+ }
219
+ elementBlur(event, element) {
220
+ console.info("[Admin Panel Input Element Editor] element blur", event);
221
+ }
222
+ inputChange(event, element) {
223
+ console.info("[Admin Panel Input Element Editor] input changed", event);
224
+ this.elementConfigurationChanged(event, element);
225
+ // this.elementConfigurationChanged(event?.target?.value, element)
226
+ }
227
+ passFormChanges(event, element) {
228
+ console.error("[Admin Panel Input Element Editor] pass form changes", event);
229
+ this.formInputChange(event, element);
230
+ }
231
+ elementConfigurationChanged(value, ele) {
232
+ const applyTo = { ...this.dataPlaceholder };
233
+ if (ele.clearOnChange) {
234
+ ele.clearOnChange.forEach((clear) => {
235
+ assignDeepPropertyToObject(applyTo, clear, undefined);
236
+ });
237
+ }
238
+ if (ele.deepBind) {
239
+ assignDeepPropertyToObject(applyTo, ele.deepBind, value);
240
+ }
241
+ this.dataPlaceholder = {
242
+ ...applyTo
243
+ };
244
+ // const formClass= this.formBuilder$$.value
245
+ // const form = formClass?.form
246
+ // if (form) {
247
+ // formClass?.refreshController(form)
248
+ // }
249
+ // this.formBuilder$$.value?.syncInputController();
250
+ // this.appStatesService.TFormBuilderService$.next(formClass)
251
+ }
252
+ addNewSubmitApi(submision) {
253
+ this.dataPlaceholder = {
254
+ data: {}
255
+ };
256
+ this.keyInEdit = submision;
257
+ }
258
+ saveSubmitApi() {
259
+ this.store.actionsFormBuilder.saveSubmitApi(this.dataPlaceholder, this.keyInEdit);
260
+ this.dataPlaceholder = undefined;
261
+ this.keyInEdit = undefined;
262
+ }
263
+ removeSubmissionApi(api, key) {
264
+ this.store.actionsFormBuilder.removeSubmissionApi(api, key);
265
+ this.dataPlaceholder = undefined;
266
+ this.keyInEdit = undefined;
267
+ }
268
+ cancelSubmitApi() {
269
+ this.dataPlaceholder = undefined;
270
+ this.keyInEdit = undefined;
271
+ }
272
+ close() {
273
+ //close mat dialog
274
+ this.matDialog.closeAll();
275
+ }
276
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: FormSubmissionsConfigComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
277
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.17", type: FormSubmissionsConfigComponent, isStandalone: true, selector: "app-form-submissions-config", ngImport: i0, template: "<div class=\"submissions-dialog\">\r\n <!-- Header: icon + title + subtitle per design rule \u00A74 -->\r\n <header class=\"dialog-header\">\r\n <div class=\"header-icon\" aria-hidden=\"true\">\r\n <mat-icon>settings</mat-icon>\r\n </div>\r\n <div class=\"header-text\">\r\n <h1 class=\"header-title\">Form Submission Configuration</h1>\r\n <p class=\"header-subtitle\">Configure how your form is submitted and validated before submission.</p>\r\n </div>\r\n <button mat-icon-button mat-dialog-close [attr.aria-label]=\"'Close dialog'\" class=\"header-close\">\r\n <mat-icon>close</mat-icon>\r\n </button>\r\n </header>\r\n\r\n <section class=\"dialog-content\">\r\n <mat-accordion multi>\r\n @for (panel of panels(); track panel.id) {\r\n <mat-expansion-panel [expanded]=\"true\" class=\"config-panel\">\r\n <mat-expansion-panel-header class=\"panel-header\">\r\n <mat-panel-title class=\"panel-title\">\r\n <mat-icon class=\"panel-icon\" [color]=\"'primary'\">{{ panel.titleIcon }}</mat-icon>\r\n {{ panel.title }}\r\n </mat-panel-title>\r\n </mat-expansion-panel-header>\r\n\r\n <div class=\"panel-content\">\r\n <p class=\"panel-description\" [innerHTML]=\"panel.description\"></p>\r\n\r\n <!-- Dynamic form elements (submission message, toggles, etc.) -->\r\n @if ((panel?.elements || []).length > 0) {\r\n <div class=\"edit-section\">\r\n @for (edit of (panel?.elements || []); track edit.id) {\r\n <div class=\"edit-field\">\r\n <lib-t-dynamic-data-edit\r\n [editorConfig]=\"edit\"\r\n [data]=\"form()\"\r\n [validationErrors]=\"(computedErrors(edit)()) || []\"\r\n (valueChange)=\"passFormChanges($event, edit)\"\r\n [formInputs]=\"(formInputs()) || []\"\r\n (blur)=\"elementBlur($event, edit)\">\r\n </lib-t-dynamic-data-edit>\r\n </div>\r\n }\r\n </div>\r\n }\r\n\r\n <!-- API endpoints list -->\r\n <div class=\"api-section\">\r\n <h3 class=\"api-section-header\" [class.api-section-header--error]=\"panel.error\">\r\n {{ panel.primaryKey === 'submissionAPI' ? 'Submission Endpoints' : 'Validation Endpoints' }}\r\n </h3>\r\n\r\n @if ((getFormApis(panel.primaryKey)()).length === 0) {\r\n <div class=\"empty-state\" [class.empty-state--error]=\"panel.error\">\r\n <mat-icon class=\"empty-state-icon\">add_link</mat-icon>\r\n <p>\r\n {{ panel.primaryKey === 'submissionAPI'\r\n ? 'No submission endpoints configured. Add one to send form data.'\r\n : 'No validation endpoints configured. Add one to validate data before submission.' }}\r\n </p>\r\n </div>\r\n }\r\n\r\n @for (api of getFormApis(panel.primaryKey)(); track api._id) {\r\n <div class=\"api-list-item\">\r\n <span class=\"api-method-tag\" [class.api-method-tag--post]=\"api.httpMethod === 'POST'\">\r\n {{ api.httpMethod }}\r\n </span>\r\n <div class=\"api-details\">\r\n <span class=\"api-name\">{{ api.name }}</span>\r\n <span class=\"api-endpoint\" [matTooltip]=\"api.httpEndPoint\" matTooltipPosition=\"above\">\r\n {{ api.httpEndPoint }}\r\n </span>\r\n </div>\r\n <div class=\"api-item-actions\">\r\n <button\r\n [cdkCopyToClipboard]=\"api.httpEndPoint || ''\"\r\n mat-icon-button\r\n matTooltip=\"Copy URL\"\r\n [attr.aria-label]=\"'Copy endpoint URL'\"\r\n class=\"api-copy-btn\"\r\n (click)=\"$event.stopPropagation()\">\r\n <mat-icon>content_copy</mat-icon>\r\n </button>\r\n <button\r\n (click)=\"removeSubmissionApi(api,panel.primaryKey)\"\r\n mat-icon-button\r\n matTooltip=\"Remove endpoint\"\r\n [attr.aria-label]=\"'Remove endpoint ' + api.name\"\r\n class=\"api-remove-btn\">\r\n <mat-icon>delete</mat-icon>\r\n </button>\r\n </div>\r\n </div>\r\n <mat-divider></mat-divider>\r\n }\r\n\r\n @if (keyInEdit !== panel.primaryKey) {\r\n <div class=\"api-actions\">\r\n <button\r\n (click)=\"addNewSubmitApi(panel.primaryKey)\"\r\n mat-stroked-button\r\n color=\"primary\"\r\n class=\"add-endpoint-btn\">\r\n <mat-icon>add</mat-icon>\r\n Add new endpoint\r\n </button>\r\n </div>\r\n }\r\n\r\n @if (panel.error) {\r\n <mat-error class=\"panel-error\">{{ panel.error }}</mat-error>\r\n }\r\n </div>\r\n\r\n <!-- Inline edit form for new/editing endpoint -->\r\n @if (keyInEdit === panel.primaryKey) {\r\n <div class=\"edit-section edit-section--inline\">\r\n @for (element of submissionApiListItemConfigElements; track element.id) {\r\n <div class=\"edit-field\">\r\n <lib-t-dynamic-data-edit\r\n [editorConfig]=\"element\"\r\n [data]=\"dataPlaceholder\"\r\n [validationErrors]=\"validationErrors(element)\"\r\n (valueChange)=\"inputChange($event, element)\"\r\n [formInputs]=\"(formInputs()) || []\"\r\n (blur)=\"elementBlur($event, element)\">\r\n </lib-t-dynamic-data-edit>\r\n </div>\r\n }\r\n <mat-divider></mat-divider>\r\n <div class=\"inline-edit-actions\">\r\n <button mat-flat-button (click)=\"saveSubmitApi()\" color=\"primary\">Save</button>\r\n <button mat-button (click)=\"cancelSubmitApi()\" color=\"warn\">Cancel</button>\r\n </div>\r\n </div>\r\n }\r\n </div>\r\n </mat-expansion-panel>\r\n }\r\n </mat-accordion>\r\n </section>\r\n</div>\r\n", styles: ["@charset \"UTF-8\";.submissions-dialog{display:flex;flex-direction:column;min-width:28rem;max-width:36rem;max-height:calc(100vh - 24px);background:var(--mat-sys-surface, var(--mat-app-surface));border:1px solid var(--mat-sys-outline-variant, var(--mat-divider-color));overflow:hidden}.dialog-header{display:flex;align-items:flex-start;gap:1rem;padding:1.5rem 1.5rem 0;flex-shrink:0}.header-icon{width:2.75rem;height:2.75rem;border-radius:8px;background:color-mix(in srgb,var(--mat-sys-primary, #6750a4) 12%,transparent);display:flex;align-items:center;justify-content:center;flex-shrink:0}.header-icon mat-icon{font-size:1.5rem;width:1.5rem;height:1.5rem;color:var(--mat-sys-primary, #6750a4)}.header-text{flex:1;min-width:0}.header-title{font-size:1.25rem;font-weight:600;letter-spacing:-.01em;color:var(--mat-sys-on-surface, #1c1b1f);margin:0;line-height:1.3}.header-subtitle{font-size:.875rem;color:var(--mat-sys-on-surface-variant, #49454f);line-height:1.4;margin-top:.25rem;margin-bottom:0}.header-close{margin-top:-.5rem;margin-right:-.5rem}.dialog-content{padding:1rem 1.5rem 1.5rem;overflow-y:auto;max-height:70vh;flex:1;min-height:0}.config-panel{border-radius:12px;box-shadow:0 2px 8px #00000014;border:1px solid color-mix(in srgb,var(--mat-sys-outline, #79747e) 15%,transparent);overflow:hidden;margin-bottom:1rem}.config-panel:last-child{margin-bottom:0}.panel-header{background:color-mix(in srgb,var(--mat-sys-primary, #6750a4) 4%,transparent);border-bottom:1px solid color-mix(in srgb,var(--mat-sys-outline, #79747e) 10%,transparent)}.panel-title{display:flex;align-items:center;gap:.75rem;font-weight:600}.panel-icon{margin-right:0;font-size:1.25rem;width:1.25rem;height:1.25rem}.panel-content{padding:1.25rem;display:flex;flex-direction:column;gap:1rem;background:var(--mat-sys-surface-container, var(--mat-sys-surface))}.panel-description{font-size:.8125rem;color:var(--mat-sys-on-surface-variant, #49454f);line-height:1.35;margin:0 0 1rem}.edit-section{background:var(--mat-sys-surface-container-high, var(--mat-sys-surface-container));border-radius:8px;padding:1rem;border:1px solid color-mix(in srgb,var(--mat-sys-outline, #79747e) 10%,transparent)}.edit-section--inline{margin-top:.5rem}.edit-field{margin-bottom:1rem}.edit-field:last-child{margin-bottom:0}.api-section{background:var(--mat-sys-surface-container-high, var(--mat-sys-surface-container));border-radius:8px;padding:1rem;border:1px solid color-mix(in srgb,var(--mat-sys-outline, #79747e) 10%,transparent)}.api-section-header{font-size:.75rem;font-weight:500;text-transform:uppercase;letter-spacing:.04em;color:var(--mat-sys-on-surface-variant, #49454f);margin:0 0 1rem;padding-left:.25rem}.api-section-header--error{color:var(--mat-sys-error, #b3261e)}.empty-state{display:flex;align-items:center;gap:.5rem;padding:1rem;background:var(--mat-sys-surface-container-highest, #e8e0ec);border-radius:8px;font-size:.875rem;color:var(--mat-sys-on-surface-variant, #49454f);margin:0 0 1rem}.empty-state p{margin:0;flex:1}.empty-state--error{color:var(--mat-sys-error, #b3261e)}.empty-state-icon{font-size:1.25rem;width:1.25rem;height:1.25rem;opacity:.8}.api-list-item{display:flex;align-items:center;gap:1rem;padding:.5rem 0;min-height:2.75rem}.api-method-tag{flex-shrink:0;padding:.25rem .5rem;font-size:.75rem;font-weight:600;border-radius:6px;text-align:center;min-width:3rem;background:color-mix(in srgb,var(--mat-sys-primary, #6750a4) 10%,transparent);color:var(--mat-sys-primary, #6750a4)}.api-method-tag--post{background:color-mix(in srgb,#2e7d32 12%,transparent);color:#2e7d32}.api-details{flex:1;min-width:0;display:flex;flex-direction:column;gap:.125rem}.api-name{font-size:.875rem;font-weight:600;color:var(--mat-sys-on-surface, #1c1b1f);line-height:1.3}.api-endpoint{font-size:.75rem;color:var(--mat-sys-on-surface-variant, #49454f);opacity:.85;line-height:1.3;max-width:18rem;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;cursor:help}.api-item-actions{display:flex;align-items:center;gap:.25rem;flex-shrink:0}.api-copy-btn:hover mat-icon,.api-remove-btn:hover mat-icon{color:var(--mat-sys-primary, #6750a4)}.api-remove-btn:hover mat-icon{color:var(--mat-sys-error, #b3261e)}.api-actions{margin-top:1rem}.add-endpoint-btn{width:100%;border-style:dashed;padding:.5rem .875rem;font-size:.8125rem}.add-endpoint-btn mat-icon{font-size:1.25rem;width:1.25rem;height:1.25rem;margin-right:.5rem}.panel-error{margin-top:.5rem;font-size:.75rem}.inline-edit-actions{display:flex;justify-content:flex-end;gap:.75rem;padding-top:1rem;margin-top:.5rem}@media (prefers-color-scheme: dark){.config-panel{box-shadow:0 1px 3px #0000004d}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: MatModulesModule }, { kind: "directive", type: i1.CdkCopyToClipboard, selector: "[cdkCopyToClipboard]", inputs: ["cdkCopyToClipboard", "cdkCopyToClipboardAttempts"], outputs: ["cdkCopyToClipboardCopied"] }, { kind: "component", type: i2.MatButton, selector: " button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button] ", exportAs: ["matButton"] }, { kind: "component", type: i2.MatIconButton, selector: "button[mat-icon-button]", exportAs: ["matButton"] }, { kind: "directive", type: i1$1.MatDialogClose, selector: "[mat-dialog-close], [matDialogClose]", inputs: ["aria-label", "type", "mat-dialog-close", "matDialogClose"], exportAs: ["matDialogClose"] }, { kind: "component", type: i4.MatDivider, selector: "mat-divider", inputs: ["vertical", "inset"] }, { kind: "directive", type: i5.MatAccordion, selector: "mat-accordion", inputs: ["hideToggle", "displayMode", "togglePosition"], exportAs: ["matAccordion"] }, { kind: "component", type: i5.MatExpansionPanel, selector: "mat-expansion-panel", inputs: ["hideToggle", "togglePosition"], outputs: ["afterExpand", "afterCollapse"], exportAs: ["matExpansionPanel"] }, { kind: "component", type: i5.MatExpansionPanelHeader, selector: "mat-expansion-panel-header", inputs: ["expandedHeight", "collapsedHeight", "tabIndex"] }, { kind: "directive", type: i5.MatExpansionPanelTitle, selector: "mat-panel-title" }, { kind: "component", type: i2$1.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "directive", type: i2$3.MatError, selector: "mat-error, [matError]", inputs: ["id"] }, { kind: "directive", type: i8.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { kind: "component", type: TDynamicDataEditComponent, selector: "lib-t-dynamic-data-edit", inputs: ["editorConfig", "formInputs", "data", "validationErrors"], outputs: ["valueChange", "blur"] }] }); }
278
+ }
279
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: FormSubmissionsConfigComponent, decorators: [{
280
+ type: Component,
281
+ args: [{ selector: 'app-form-submissions-config', standalone: true, imports: [CommonModule, MatModulesModule, TDynamicDataEditComponent], template: "<div class=\"submissions-dialog\">\r\n <!-- Header: icon + title + subtitle per design rule \u00A74 -->\r\n <header class=\"dialog-header\">\r\n <div class=\"header-icon\" aria-hidden=\"true\">\r\n <mat-icon>settings</mat-icon>\r\n </div>\r\n <div class=\"header-text\">\r\n <h1 class=\"header-title\">Form Submission Configuration</h1>\r\n <p class=\"header-subtitle\">Configure how your form is submitted and validated before submission.</p>\r\n </div>\r\n <button mat-icon-button mat-dialog-close [attr.aria-label]=\"'Close dialog'\" class=\"header-close\">\r\n <mat-icon>close</mat-icon>\r\n </button>\r\n </header>\r\n\r\n <section class=\"dialog-content\">\r\n <mat-accordion multi>\r\n @for (panel of panels(); track panel.id) {\r\n <mat-expansion-panel [expanded]=\"true\" class=\"config-panel\">\r\n <mat-expansion-panel-header class=\"panel-header\">\r\n <mat-panel-title class=\"panel-title\">\r\n <mat-icon class=\"panel-icon\" [color]=\"'primary'\">{{ panel.titleIcon }}</mat-icon>\r\n {{ panel.title }}\r\n </mat-panel-title>\r\n </mat-expansion-panel-header>\r\n\r\n <div class=\"panel-content\">\r\n <p class=\"panel-description\" [innerHTML]=\"panel.description\"></p>\r\n\r\n <!-- Dynamic form elements (submission message, toggles, etc.) -->\r\n @if ((panel?.elements || []).length > 0) {\r\n <div class=\"edit-section\">\r\n @for (edit of (panel?.elements || []); track edit.id) {\r\n <div class=\"edit-field\">\r\n <lib-t-dynamic-data-edit\r\n [editorConfig]=\"edit\"\r\n [data]=\"form()\"\r\n [validationErrors]=\"(computedErrors(edit)()) || []\"\r\n (valueChange)=\"passFormChanges($event, edit)\"\r\n [formInputs]=\"(formInputs()) || []\"\r\n (blur)=\"elementBlur($event, edit)\">\r\n </lib-t-dynamic-data-edit>\r\n </div>\r\n }\r\n </div>\r\n }\r\n\r\n <!-- API endpoints list -->\r\n <div class=\"api-section\">\r\n <h3 class=\"api-section-header\" [class.api-section-header--error]=\"panel.error\">\r\n {{ panel.primaryKey === 'submissionAPI' ? 'Submission Endpoints' : 'Validation Endpoints' }}\r\n </h3>\r\n\r\n @if ((getFormApis(panel.primaryKey)()).length === 0) {\r\n <div class=\"empty-state\" [class.empty-state--error]=\"panel.error\">\r\n <mat-icon class=\"empty-state-icon\">add_link</mat-icon>\r\n <p>\r\n {{ panel.primaryKey === 'submissionAPI'\r\n ? 'No submission endpoints configured. Add one to send form data.'\r\n : 'No validation endpoints configured. Add one to validate data before submission.' }}\r\n </p>\r\n </div>\r\n }\r\n\r\n @for (api of getFormApis(panel.primaryKey)(); track api._id) {\r\n <div class=\"api-list-item\">\r\n <span class=\"api-method-tag\" [class.api-method-tag--post]=\"api.httpMethod === 'POST'\">\r\n {{ api.httpMethod }}\r\n </span>\r\n <div class=\"api-details\">\r\n <span class=\"api-name\">{{ api.name }}</span>\r\n <span class=\"api-endpoint\" [matTooltip]=\"api.httpEndPoint\" matTooltipPosition=\"above\">\r\n {{ api.httpEndPoint }}\r\n </span>\r\n </div>\r\n <div class=\"api-item-actions\">\r\n <button\r\n [cdkCopyToClipboard]=\"api.httpEndPoint || ''\"\r\n mat-icon-button\r\n matTooltip=\"Copy URL\"\r\n [attr.aria-label]=\"'Copy endpoint URL'\"\r\n class=\"api-copy-btn\"\r\n (click)=\"$event.stopPropagation()\">\r\n <mat-icon>content_copy</mat-icon>\r\n </button>\r\n <button\r\n (click)=\"removeSubmissionApi(api,panel.primaryKey)\"\r\n mat-icon-button\r\n matTooltip=\"Remove endpoint\"\r\n [attr.aria-label]=\"'Remove endpoint ' + api.name\"\r\n class=\"api-remove-btn\">\r\n <mat-icon>delete</mat-icon>\r\n </button>\r\n </div>\r\n </div>\r\n <mat-divider></mat-divider>\r\n }\r\n\r\n @if (keyInEdit !== panel.primaryKey) {\r\n <div class=\"api-actions\">\r\n <button\r\n (click)=\"addNewSubmitApi(panel.primaryKey)\"\r\n mat-stroked-button\r\n color=\"primary\"\r\n class=\"add-endpoint-btn\">\r\n <mat-icon>add</mat-icon>\r\n Add new endpoint\r\n </button>\r\n </div>\r\n }\r\n\r\n @if (panel.error) {\r\n <mat-error class=\"panel-error\">{{ panel.error }}</mat-error>\r\n }\r\n </div>\r\n\r\n <!-- Inline edit form for new/editing endpoint -->\r\n @if (keyInEdit === panel.primaryKey) {\r\n <div class=\"edit-section edit-section--inline\">\r\n @for (element of submissionApiListItemConfigElements; track element.id) {\r\n <div class=\"edit-field\">\r\n <lib-t-dynamic-data-edit\r\n [editorConfig]=\"element\"\r\n [data]=\"dataPlaceholder\"\r\n [validationErrors]=\"validationErrors(element)\"\r\n (valueChange)=\"inputChange($event, element)\"\r\n [formInputs]=\"(formInputs()) || []\"\r\n (blur)=\"elementBlur($event, element)\">\r\n </lib-t-dynamic-data-edit>\r\n </div>\r\n }\r\n <mat-divider></mat-divider>\r\n <div class=\"inline-edit-actions\">\r\n <button mat-flat-button (click)=\"saveSubmitApi()\" color=\"primary\">Save</button>\r\n <button mat-button (click)=\"cancelSubmitApi()\" color=\"warn\">Cancel</button>\r\n </div>\r\n </div>\r\n }\r\n </div>\r\n </mat-expansion-panel>\r\n }\r\n </mat-accordion>\r\n </section>\r\n</div>\r\n", styles: ["@charset \"UTF-8\";.submissions-dialog{display:flex;flex-direction:column;min-width:28rem;max-width:36rem;max-height:calc(100vh - 24px);background:var(--mat-sys-surface, var(--mat-app-surface));border:1px solid var(--mat-sys-outline-variant, var(--mat-divider-color));overflow:hidden}.dialog-header{display:flex;align-items:flex-start;gap:1rem;padding:1.5rem 1.5rem 0;flex-shrink:0}.header-icon{width:2.75rem;height:2.75rem;border-radius:8px;background:color-mix(in srgb,var(--mat-sys-primary, #6750a4) 12%,transparent);display:flex;align-items:center;justify-content:center;flex-shrink:0}.header-icon mat-icon{font-size:1.5rem;width:1.5rem;height:1.5rem;color:var(--mat-sys-primary, #6750a4)}.header-text{flex:1;min-width:0}.header-title{font-size:1.25rem;font-weight:600;letter-spacing:-.01em;color:var(--mat-sys-on-surface, #1c1b1f);margin:0;line-height:1.3}.header-subtitle{font-size:.875rem;color:var(--mat-sys-on-surface-variant, #49454f);line-height:1.4;margin-top:.25rem;margin-bottom:0}.header-close{margin-top:-.5rem;margin-right:-.5rem}.dialog-content{padding:1rem 1.5rem 1.5rem;overflow-y:auto;max-height:70vh;flex:1;min-height:0}.config-panel{border-radius:12px;box-shadow:0 2px 8px #00000014;border:1px solid color-mix(in srgb,var(--mat-sys-outline, #79747e) 15%,transparent);overflow:hidden;margin-bottom:1rem}.config-panel:last-child{margin-bottom:0}.panel-header{background:color-mix(in srgb,var(--mat-sys-primary, #6750a4) 4%,transparent);border-bottom:1px solid color-mix(in srgb,var(--mat-sys-outline, #79747e) 10%,transparent)}.panel-title{display:flex;align-items:center;gap:.75rem;font-weight:600}.panel-icon{margin-right:0;font-size:1.25rem;width:1.25rem;height:1.25rem}.panel-content{padding:1.25rem;display:flex;flex-direction:column;gap:1rem;background:var(--mat-sys-surface-container, var(--mat-sys-surface))}.panel-description{font-size:.8125rem;color:var(--mat-sys-on-surface-variant, #49454f);line-height:1.35;margin:0 0 1rem}.edit-section{background:var(--mat-sys-surface-container-high, var(--mat-sys-surface-container));border-radius:8px;padding:1rem;border:1px solid color-mix(in srgb,var(--mat-sys-outline, #79747e) 10%,transparent)}.edit-section--inline{margin-top:.5rem}.edit-field{margin-bottom:1rem}.edit-field:last-child{margin-bottom:0}.api-section{background:var(--mat-sys-surface-container-high, var(--mat-sys-surface-container));border-radius:8px;padding:1rem;border:1px solid color-mix(in srgb,var(--mat-sys-outline, #79747e) 10%,transparent)}.api-section-header{font-size:.75rem;font-weight:500;text-transform:uppercase;letter-spacing:.04em;color:var(--mat-sys-on-surface-variant, #49454f);margin:0 0 1rem;padding-left:.25rem}.api-section-header--error{color:var(--mat-sys-error, #b3261e)}.empty-state{display:flex;align-items:center;gap:.5rem;padding:1rem;background:var(--mat-sys-surface-container-highest, #e8e0ec);border-radius:8px;font-size:.875rem;color:var(--mat-sys-on-surface-variant, #49454f);margin:0 0 1rem}.empty-state p{margin:0;flex:1}.empty-state--error{color:var(--mat-sys-error, #b3261e)}.empty-state-icon{font-size:1.25rem;width:1.25rem;height:1.25rem;opacity:.8}.api-list-item{display:flex;align-items:center;gap:1rem;padding:.5rem 0;min-height:2.75rem}.api-method-tag{flex-shrink:0;padding:.25rem .5rem;font-size:.75rem;font-weight:600;border-radius:6px;text-align:center;min-width:3rem;background:color-mix(in srgb,var(--mat-sys-primary, #6750a4) 10%,transparent);color:var(--mat-sys-primary, #6750a4)}.api-method-tag--post{background:color-mix(in srgb,#2e7d32 12%,transparent);color:#2e7d32}.api-details{flex:1;min-width:0;display:flex;flex-direction:column;gap:.125rem}.api-name{font-size:.875rem;font-weight:600;color:var(--mat-sys-on-surface, #1c1b1f);line-height:1.3}.api-endpoint{font-size:.75rem;color:var(--mat-sys-on-surface-variant, #49454f);opacity:.85;line-height:1.3;max-width:18rem;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;cursor:help}.api-item-actions{display:flex;align-items:center;gap:.25rem;flex-shrink:0}.api-copy-btn:hover mat-icon,.api-remove-btn:hover mat-icon{color:var(--mat-sys-primary, #6750a4)}.api-remove-btn:hover mat-icon{color:var(--mat-sys-error, #b3261e)}.api-actions{margin-top:1rem}.add-endpoint-btn{width:100%;border-style:dashed;padding:.5rem .875rem;font-size:.8125rem}.add-endpoint-btn mat-icon{font-size:1.25rem;width:1.25rem;height:1.25rem;margin-right:.5rem}.panel-error{margin-top:.5rem;font-size:.75rem}.inline-edit-actions{display:flex;justify-content:flex-end;gap:.75rem;padding-top:1rem;margin-top:.5rem}@media (prefers-color-scheme: dark){.config-panel{box-shadow:0 1px 3px #0000004d}}\n"] }]
282
+ }], ctorParameters: () => [] });
283
+
284
+ class FormsBuilderMenuComponent {
285
+ constructor(store) {
286
+ this.store = store;
287
+ this.form$ = this.store.selectors.selectFormInEdit$.pipe(map((form) => form?.form));
288
+ this.showJson = false;
289
+ this.snackBar = inject(MatSnackBar);
290
+ this.formCopy$ = this.form$.pipe(map((form) => JSON.stringify(form || {})));
291
+ this.dialog = inject(MatDialog);
292
+ this.applyFormTemplate = (form) => this.store.actionsFormBuilder.applyFormTemplate(form);
293
+ this.errors$ = this.store.formBuilderSelectors.formErrors$;
294
+ this.configHasErrors$ = this.errors$.pipe(map(errors => errors?.some(error => error.key.split('.')?.[0] === 'submissionHandle')));
295
+ this.items$ = this.configHasErrors$.pipe(map(configHasErrors => {
296
+ return [
297
+ {
298
+ label: "Templates",
299
+ icon: "content_copy",
300
+ hint: "Select a form to apply as a template",
301
+ action: () => this.openDialog()
302
+ },
303
+ {
304
+ label: "Submission Config",
305
+ hint: "Setup form submission api and other configurations",
306
+ icon: "api",
307
+ errors: configHasErrors,
308
+ action: () => this.openFormSubmissionDialog()
309
+ },
310
+ ];
311
+ }));
312
+ this.vm$ = combineLatest([
313
+ this.items$,
314
+ this.form$,
315
+ this.formCopy$,
316
+ this
317
+ .configHasErrors$
318
+ ]).pipe(map(([items, form, formCopy, configHasErrors]) => ({ items, form, formCopy, configHasErrors })));
319
+ }
320
+ openDialog() {
321
+ const dialogRef = this.dialog.open(SelectFormTemplateComponent);
322
+ dialogRef.afterClosed().pipe(take(1)).subscribe(form => {
323
+ if (form) {
324
+ this.applyFormTemplate(form);
325
+ }
326
+ });
327
+ }
328
+ openFormSubmissionDialog() {
329
+ const dialogRef = this.dialog.open(FormSubmissionsConfigComponent, {
330
+ data: {
331
+ store: this.store
332
+ }
333
+ });
334
+ dialogRef.afterClosed().pipe(take(1)).subscribe(data => {
335
+ if (data) {
336
+ console.log(data);
337
+ }
338
+ });
339
+ }
340
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: FormsBuilderMenuComponent, deps: [{ token: FormsStoreService }], target: i0.ɵɵFactoryTarget.Component }); }
341
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.17", type: FormsBuilderMenuComponent, isStandalone: true, selector: "app-forms-builder-menu", ngImport: i0, template: "\r\n<ng-container *ngIf=\"( vm$|async) as vm\">\r\n\r\n <button [matBadge]=\"'!'\" matBadgeColor=\"warn\" [matTooltip]=\"vm.configHasErrors === false?'\r\n Click to view form configuration \r\n ':'Click to fix or add missing form configurations'\" [matBadgeHidden]=\"!!(vm.configHasErrors) === false\"\r\n [matMenuTriggerFor]=\"configMenu\" mat-flat-button>\r\n Config\r\n <mat-icon>\r\n settings_suggest\r\n </mat-icon>\r\n </button>\r\n \r\n <mat-menu #configMenu=\"matMenu\">\r\n \r\n \r\n <div mat-subheader style=\"padding-left: 16px;\">Form edit options</div>\r\n @for(item of vm.items;track item){\r\n <button [matTooltip]=\"item.hint\" matTooltipPosition=\"right\" style=\"padding-top:4px;padding-bottom:4px\"\r\n (click)=\"item?.action()\" mat-menu-item>\r\n <mat-icon>{{item.icon}}</mat-icon>\r\n \r\n <span>\r\n @if (item.errors) {\r\n <div style=\"line-height: normal;\"ks>\r\n {{item.label}}\r\n </div>\r\n <div style=\"line-height: normal;\">\r\n \r\n <mat-error>\r\n <small>\r\n Invalid configurations\r\n </small>\r\n \r\n </mat-error>\r\n </div>\r\n }@else {\r\n \r\n {{item.label}}\r\n \r\n }\r\n \r\n \r\n </span>\r\n \r\n </button>\r\n <mat-divider></mat-divider>\r\n }\r\n <button [matTooltip]=\"showJson?'Hide json':'Show json'\" (click)=\"showJson= !showJson\" mat-menu-item>\r\n <mat-icon>data_object</mat-icon>\r\n \r\n <span>\r\n <div>\r\n Show json\r\n </div>\r\n \r\n \r\n \r\n </span>\r\n </button>\r\n \r\n </mat-menu>\r\n \r\n <div *ngIf=\"showJson\" class=\"DragBoundary\">\r\n @defer (on viewport) {\r\n <mat-card cdkDragBoundary=\"DragBoundary\" cdkDrag class=\"menuCard\">\r\n <mat-toolbar\r\n style=\"background: var(--mat-sidenav-content-background-color);color:var(--mdc-filled-button-label-text-color, var(--mat-app-on-primary))\">\r\n <span>\r\n Json Form Value\r\n </span>\r\n <span class=\"spacer\"></span>\r\n \r\n <mat-icon cdkDragHandle style=\"\r\n margin:8px;\r\n color:var(--mat-divider-color, var(--mat-app-outline))\">\r\n drag_indicator\r\n </mat-icon>\r\n </mat-toolbar>\r\n <mat-divider style=\"margin-bottom: 12px;\"></mat-divider>\r\n \r\n \r\n <mat-card-content style=\" overflow-y: auto;\r\n max-height: calc(100vh - 150px);\">\r\n <app-form-json-view></app-form-json-view>\r\n </mat-card-content>\r\n <mat-card-actions>\r\n <span class=\"spacer\"></span>\r\n <button (click)=\"showJson = false\" mat-flat-button>\r\n Close\r\n </button>\r\n <button color=\"primary\" [cdkCopyToClipboard]=\"(vm.formCopy)||''\" matTooltip=\"Copy json to clipboard\"\r\n mat-flat-button>\r\n Copy\r\n </button>\r\n \r\n </mat-card-actions>\r\n \r\n \r\n </mat-card>\r\n }@placeholder {\r\n <div style=\"padding: 50px;display:flex;justify-content:center;align-items:center; text-align: center;\">\r\n <mat-spinner diameter=\"20\" />\r\n </div>\r\n \r\n }\r\n </div>\r\n</ng-container>\r\n", styles: [".menuCard{width:fit-content;margin:0;resize:horizontal;z-index:2}.DragBoundary{position:absolute;left:0;top:74px;width:100%;height:calc(100% - 74px)}\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: MatModulesModule }, { kind: "directive", type: i3$2.MatBadge, selector: "[matBadge]", inputs: ["matBadgeColor", "matBadgeOverlap", "matBadgeDisabled", "matBadgePosition", "matBadge", "matBadgeDescription", "matBadgeSize", "matBadgeHidden"] }, { kind: "component", type: i2.MatButton, selector: " button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button] ", exportAs: ["matButton"] }, { kind: "component", type: i4.MatDivider, selector: "mat-divider", inputs: ["vertical", "inset"] }, { kind: "component", type: i2$1.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "directive", type: i7.MatListSubheaderCssMatStyler, selector: "[mat-subheader], [matSubheader]" }, { kind: "component", type: i2$5.MatMenu, selector: "mat-menu", inputs: ["backdropClass", "aria-label", "aria-labelledby", "aria-describedby", "xPosition", "yPosition", "overlapTrigger", "hasBackdrop", "class", "classList"], outputs: ["closed", "close"], exportAs: ["matMenu"] }, { kind: "component", type: i2$5.MatMenuItem, selector: "[mat-menu-item]", inputs: ["role", "disabled", "disableRipple"], exportAs: ["matMenuItem"] }, { kind: "directive", type: i2$5.MatMenuTrigger, selector: "[mat-menu-trigger-for], [matMenuTriggerFor]", inputs: ["mat-menu-trigger-for", "matMenuTriggerFor", "matMenuTriggerData", "matMenuTriggerRestoreFocus"], outputs: ["menuOpened", "onMenuOpen", "menuClosed", "onMenuClose"], exportAs: ["matMenuTrigger"] }, { kind: "component", type: i3.MatProgressSpinner, selector: "mat-progress-spinner, mat-spinner", inputs: ["color", "mode", "value", "diameter", "strokeWidth"], exportAs: ["matProgressSpinner"] }, { kind: "directive", type: i2$3.MatError, selector: "mat-error, [matError]", inputs: ["id"] }, { kind: "directive", type: i8.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }], deferBlockDependencies: [() => [i1.CdkCopyToClipboard, import('@angular/cdk/drag-drop').then(m => m.CdkDrag), i2$4.CdkDragHandle, i2.MatButton, i3$1.MatCard, i3$1.MatCardActions, i3$1.MatCardContent, i4.MatDivider, i2$1.MatIcon, i10.MatToolbar, i8.MatTooltip, import('./ngx-t-forms-form-json-view.component-C7i6JZ-l.mjs').then(m => m.FormJsonViewComponent)]] }); }
342
+ }
343
+ i0.ɵɵngDeclareClassMetadataAsync({ minVersion: "18.0.0", version: "19.2.17", ngImport: i0, type: FormsBuilderMenuComponent, resolveDeferredDeps: () => [import('@angular/cdk/drag-drop').then(m => m.CdkDrag), import('./ngx-t-forms-form-json-view.component-C7i6JZ-l.mjs').then(m => m.FormJsonViewComponent)], resolveMetadata: (CdkDrag, FormJsonViewComponent) => ({ decorators: [{
344
+ type: Component,
345
+ args: [{ selector: 'app-forms-builder-menu', standalone: true, imports: [CommonModule, MatModulesModule, FormJsonViewComponent, CdkDrag], template: "\r\n<ng-container *ngIf=\"( vm$|async) as vm\">\r\n\r\n <button [matBadge]=\"'!'\" matBadgeColor=\"warn\" [matTooltip]=\"vm.configHasErrors === false?'\r\n Click to view form configuration \r\n ':'Click to fix or add missing form configurations'\" [matBadgeHidden]=\"!!(vm.configHasErrors) === false\"\r\n [matMenuTriggerFor]=\"configMenu\" mat-flat-button>\r\n Config\r\n <mat-icon>\r\n settings_suggest\r\n </mat-icon>\r\n </button>\r\n \r\n <mat-menu #configMenu=\"matMenu\">\r\n \r\n \r\n <div mat-subheader style=\"padding-left: 16px;\">Form edit options</div>\r\n @for(item of vm.items;track item){\r\n <button [matTooltip]=\"item.hint\" matTooltipPosition=\"right\" style=\"padding-top:4px;padding-bottom:4px\"\r\n (click)=\"item?.action()\" mat-menu-item>\r\n <mat-icon>{{item.icon}}</mat-icon>\r\n \r\n <span>\r\n @if (item.errors) {\r\n <div style=\"line-height: normal;\"ks>\r\n {{item.label}}\r\n </div>\r\n <div style=\"line-height: normal;\">\r\n \r\n <mat-error>\r\n <small>\r\n Invalid configurations\r\n </small>\r\n \r\n </mat-error>\r\n </div>\r\n }@else {\r\n \r\n {{item.label}}\r\n \r\n }\r\n \r\n \r\n </span>\r\n \r\n </button>\r\n <mat-divider></mat-divider>\r\n }\r\n <button [matTooltip]=\"showJson?'Hide json':'Show json'\" (click)=\"showJson= !showJson\" mat-menu-item>\r\n <mat-icon>data_object</mat-icon>\r\n \r\n <span>\r\n <div>\r\n Show json\r\n </div>\r\n \r\n \r\n \r\n </span>\r\n </button>\r\n \r\n </mat-menu>\r\n \r\n <div *ngIf=\"showJson\" class=\"DragBoundary\">\r\n @defer (on viewport) {\r\n <mat-card cdkDragBoundary=\"DragBoundary\" cdkDrag class=\"menuCard\">\r\n <mat-toolbar\r\n style=\"background: var(--mat-sidenav-content-background-color);color:var(--mdc-filled-button-label-text-color, var(--mat-app-on-primary))\">\r\n <span>\r\n Json Form Value\r\n </span>\r\n <span class=\"spacer\"></span>\r\n \r\n <mat-icon cdkDragHandle style=\"\r\n margin:8px;\r\n color:var(--mat-divider-color, var(--mat-app-outline))\">\r\n drag_indicator\r\n </mat-icon>\r\n </mat-toolbar>\r\n <mat-divider style=\"margin-bottom: 12px;\"></mat-divider>\r\n \r\n \r\n <mat-card-content style=\" overflow-y: auto;\r\n max-height: calc(100vh - 150px);\">\r\n <app-form-json-view></app-form-json-view>\r\n </mat-card-content>\r\n <mat-card-actions>\r\n <span class=\"spacer\"></span>\r\n <button (click)=\"showJson = false\" mat-flat-button>\r\n Close\r\n </button>\r\n <button color=\"primary\" [cdkCopyToClipboard]=\"(vm.formCopy)||''\" matTooltip=\"Copy json to clipboard\"\r\n mat-flat-button>\r\n Copy\r\n </button>\r\n \r\n </mat-card-actions>\r\n \r\n \r\n </mat-card>\r\n }@placeholder {\r\n <div style=\"padding: 50px;display:flex;justify-content:center;align-items:center; text-align: center;\">\r\n <mat-spinner diameter=\"20\" />\r\n </div>\r\n \r\n }\r\n </div>\r\n</ng-container>\r\n", styles: [".menuCard{width:fit-content;margin:0;resize:horizontal;z-index:2}.DragBoundary{position:absolute;left:0;top:74px;width:100%;height:calc(100% - 74px)}\n"] }]
346
+ }], ctorParameters: () => [{ type: FormsStoreService }], propDecorators: null }) });
347
+
348
+ export { FormsBuilderMenuComponent };
349
+ //# sourceMappingURL=ngx-t-forms-forms-builder-menu.component-G3Agd-vE.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ngx-t-forms-forms-builder-menu.component-G3Agd-vE.mjs","sources":["../../../projects/ngx-t-forms/src/lib/components/form-builder/elements/select-form-template/select-form-template.component.ts","../../../projects/ngx-t-forms/src/lib/components/form-builder/elements/select-form-template/select-form-template.component.html","../../../projects/ngx-t-forms/src/lib/components/form-builder/elements/form-submissions-config/config.ts","../../../projects/ngx-t-forms/src/lib/components/form-builder/elements/form-submissions-config/form-submissions-config.component.ts","../../../projects/ngx-t-forms/src/lib/components/form-builder/elements/form-submissions-config/form-submissions-config.component.html","../../../projects/ngx-t-forms/src/lib/components/form-builder/elements/forms-builder-menu/forms-builder-menu.component.ts","../../../projects/ngx-t-forms/src/lib/components/form-builder/elements/forms-builder-menu/forms-builder-menu.component.html"],"sourcesContent":["import { Component, inject, OnInit } from '@angular/core';\r\nimport { MatModulesModule } from '../../../../shared/modules/mat-modules.module';\r\nimport { CommonModule } from '@angular/common';\r\nimport { MatDialog, MatDialogRef } from '@angular/material/dialog';\r\nimport {MAT_DIALOG_DATA} from '@angular/material/dialog';\r\nimport { FormsStoreService } from '../../../forms/store/forms-store.service';\r\nimport { getAvatar } from '../../../../shared/functions/getAvatar';\r\n\r\nimport { DaysAgoPipe } from '../../../../../lib/shared/pipes/date/days-ago.pipe';\r\nimport { FormInterface } from 'ngx-t-forms-types';\r\nimport { MatSnackBar } from '@angular/material/snack-bar';\r\n@Component({\r\n selector: 'lib-select-form-template',\r\n standalone: true,\r\n imports: [MatModulesModule, CommonModule, DaysAgoPipe],\r\n templateUrl: './select-form-template.component.html',\r\n styleUrl: './select-form-template.component.scss'\r\n})\r\nexport class SelectFormTemplateComponent implements OnInit {\r\n\r\nselectForms$ = this.store.selectors.selectForms$\r\nselectLoadingForms$ = this.store.selectors.selectLoadingForms$\r\nreadonly dialogRef = inject(MatDialogRef< SelectFormTemplateComponent>);\r\nreadonly data = inject<any>(MAT_DIALOG_DATA);\r\nconstructor(private store:FormsStoreService){\r\n\r\n}\r\n ngOnInit(): void {\r\n this.store.effects.loadForms$()\r\n }\r\nclose(){\r\n this.dialogRef.close();\r\n}\r\n\r\n dialog = inject(MatDialog);\r\n snackBar = inject(MatSnackBar);\r\nselectForm(form:FormInterface){\r\n this.store.actionsFormBuilder.applyFormTemplate(form)\r\n this.dialogRef.close(form) \r\n}\r\n getFormAvatar(formTitle:string){\r\n return getAvatar(formTitle)\r\n }\r\n}\r\n","<div class=\"template-select-dialog\">\r\n <!-- Header: icon + title + subtitle per design rule §4 -->\r\n <header class=\"dialog-header\">\r\n <div class=\"header-icon\" aria-hidden=\"true\">\r\n <mat-icon>description</mat-icon>\r\n </div>\r\n <div class=\"header-text\">\r\n <h1 class=\"header-title\">Choose a Form Template</h1>\r\n <p class=\"header-subtitle\">\r\n Select a pre-built form template to start with. You can customize and save it as your own form after copying it to the editor.\r\n </p>\r\n </div>\r\n </header>\r\n\r\n <section class=\"dialog-content\">\r\n @if (selectLoadingForms$ | async) {\r\n <div class=\"loading-state\">\r\n <mat-spinner diameter=\"40\" aria-label=\"Loading form templates\"></mat-spinner>\r\n <span class=\"loading-text\">Loading form templates...</span>\r\n </div>\r\n } @else {\r\n <ul class=\"form-template-list\" aria-label=\"Available form templates\">\r\n @for (form of selectForms$ | async; track form._id) {\r\n <li>\r\n <button\r\n type=\"button\"\r\n class=\"form-list-item\"\r\n (click)=\"selectForm(form)\"\r\n [matTooltip]=\"'Click to select this template'\"\r\n [attr.aria-label]=\"'Select template: ' + form.formTitle\">\r\n <img\r\n class=\"form-avatar\"\r\n [class.form-avatar--error]=\"form.errors.length > 0\"\r\n [src]=\"getFormAvatar(form.formTitle)\"\r\n [alt]=\"''\"\r\n aria-hidden=\"true\">\r\n <div class=\"form-item-content\">\r\n <span class=\"form-title\">{{ form.formTitle | titlecase }}</span>\r\n <div class=\"form-metadata\">\r\n @if (form.errors.length > 0) {\r\n <span class=\"metadata-item\">\r\n <mat-icon color=\"warn\" aria-hidden=\"true\">error</mat-icon>\r\n <small>{{ form.errors.length }}</small>\r\n </span>\r\n <span class=\"metadata-divider\" aria-hidden=\"true\">•</span>\r\n }\r\n <span class=\"metadata-item\">\r\n <mat-icon aria-hidden=\"true\">add_circle_outline</mat-icon>\r\n <small>{{ form.createdAt | daysAgo }}</small>\r\n </span>\r\n <span class=\"metadata-divider\" aria-hidden=\"true\">•</span>\r\n <span class=\"metadata-item\">\r\n <mat-icon aria-hidden=\"true\">update</mat-icon>\r\n <small>{{ form.updatedAt | daysAgo }}</small>\r\n </span>\r\n </div>\r\n </div>\r\n </button>\r\n </li>\r\n }\r\n </ul>\r\n\r\n @if ((selectForms$ | async)?.length === 0) {\r\n <div class=\"empty-state\">\r\n <mat-icon class=\"empty-state-icon\" aria-hidden=\"true\">folder_off</mat-icon>\r\n <p>No form templates available.</p>\r\n </div>\r\n }\r\n }\r\n </section>\r\n\r\n <footer class=\"dialog-actions\">\r\n <button mat-button (click)=\"close()\" aria-label=\"Close dialog\">\r\n Close\r\n </button>\r\n </footer>\r\n</div>\r\n","\r\n\r\nimport { ConfigurationValidTestInterface, ElementEditorInnerSectionElementInterface, ElementEditorTypes, NgxTFormsConfig, SpecialElementKeys } from \"ngx-t-forms-types\"\r\n\r\n\r\n\r\n\r\nexport function SubmissionAPIListItemConfig (environment: NgxTFormsConfig): ElementEditorInnerSectionElementInterface[]{\r\n return [\r\n\r\n {\r\n id: '16140923-0542-4d5d-9b63-186c1e91b1a7',\r\n name: SpecialElementKeys.MatOptionsApiCall,\r\n editType: ElementEditorTypes.ApiCall,\r\n required: true,\r\n postmanCollectionConfig: {\r\n collectionUrl: environment['POSTMAN_COLLECTION_URL'] as string,\r\n collectionKey: environment['POSTMAN_COLLECTION_KEY'] as string\r\n },\r\n hint: `APIs are loaded from Postman collection. Configure required headers, payload format, and response mapping before use. Ensure API is documented in Postman.`,\r\n deepBind: ['data'],\r\n label: 'Select form submission api',\r\n },\r\n \r\n {\r\n name: SpecialElementKeys.Default,\r\n deepBind: [\r\n\r\n 'data',\r\n 'backEndConfig',\r\n 'minimumInputRequired',\r\n ],\r\n additionalTest: [\r\n {\r\n testType: 'exists',\r\n deepBind: [\r\n\r\n 'data',\r\n 'backEndConfig',\r\n 'minimumInputRequired',\r\n ],\r\n } as ConfigurationValidTestInterface\r\n\r\n ],\r\n hint: `These are payload fields that are required to be filled in order to fetch data from the selected source`,\r\n editType: ElementEditorTypes.RequiredInputs,\r\n label: 'Set up required inputs',\r\n id: \"2d0f1e18-897b-4c28-97bc-3078be09f8ee\"\r\n },\r\n {\r\n name: SpecialElementKeys.Default,\r\n deepBind: ['data','projectFormData'],\r\n editType: ElementEditorTypes.Input,\r\n label: 'Project form data',\r\n hint: 'JSON template for the payload. Use {$formValue} to include submitted form data. Example: {\"data\": {$formValue}}',\r\n id: \"$2d0f1e18-897b-4c28-97bc-3078be09f8ee\"\r\n },\r\n ]}\r\nexport const SubmissionAPIOtherConfig : ElementEditorInnerSectionElementInterface[] = [\r\n {\r\n id:\"7acaa5d1-fb37-4dc0-9f1a-53b6f36534e0\",\r\n name:SpecialElementKeys.Default,\r\n deepBind:['submissionHandle','submissionMessage'],\r\n editType:ElementEditorTypes.Input,\r\n label:\"Submission Message\",\r\n hint:\"Message shown to the user after successful form submission.\",\r\n },\r\n {\r\n id:\"5636c16c-be58-48d9-b701-81fe05573a39\",\r\n name:SpecialElementKeys.Default,\r\n deepBind:['submissionHandle','innerComponentShowSubmitButton'],\r\n editType:ElementEditorTypes.Toggle,\r\n label:\"Show submit button in inner component form\",\r\n },\r\n {\r\n id:\"c9103faa-eeba-4cf3-b3d0-f25fd05ccb3c\",\r\n name:SpecialElementKeys.Default,\r\n deepBind:['systemDefault'],\r\n editType:ElementEditorTypes.Toggle,\r\n label:\"Is system default\",\r\n }\r\n] \r\n \r\n export const SubmissionDialogConfig = [\r\n {\r\n titleIcon:'send',\r\n id:'44023662-7343-4c93-999d-fc03c54438c3',\r\n title:'Form Submission Endpoint',\r\n description:`Choose where to send your form data after submission. Connect to your database, data pipeline, or\r\n third-party service.`,\r\n primaryKey:'submissionAPI',\r\n elements : SubmissionAPIOtherConfig \r\n },\r\n {\r\n titleIcon:'rule',\r\n title:'Data Validation Rules',\r\n description:`Define validation rules to ensure data quality before form submission.\r\n <br>\r\n <strong>Note:</strong> All validation endpoints must return a boolean value -\r\n <code>true</code> indicates valid data that will be submitted.`,\r\n primaryKey:'canSubmitAPI',\r\n id:'acac6045-5b13-4c8d-a7ee-da194ebbad41',\r\n }\r\n ]","import { Component, computed, inject } from '@angular/core';\r\n\r\nimport { CommonModule } from '@angular/common';\r\nimport { MatModulesModule } from '../../../../shared/modules/mat-modules.module';\r\n\r\nimport { SubmissionAPIListItemConfig, SubmissionDialogConfig } from './config';\r\nimport { MAT_DIALOG_DATA, MatDialog, } from '@angular/material/dialog';\r\nimport { FormsStoreService } from '../../../forms/store/forms-store.service';\r\nimport { testAgainstItem } from '../../../t-dynamic-data-edit/functions/testData';\r\n\r\nimport { TDynamicDataEditComponent } from '../../../t-dynamic-data-edit/t-dynamic-data-edit.component';\r\n\r\nimport { assignDeepPropertyToObject } from '../../../../../lib/shared';\r\nimport { NGX_T_FORMS_CONFIG_TOKEN } from '../../../../../lib/injection-tokens';\r\n\r\nimport { toSignal } from '@angular/core/rxjs-interop';\r\nimport { APIDataFetchingConfigurationInterface, ElementEditorInnerSectionElementInterface, validateApiDataFetchingConfiguration } from 'ngx-t-forms-types';\r\nimport { map } from 'rxjs';\r\n\r\n\r\n@Component({\r\n selector: 'app-form-submissions-config',\r\n standalone: true,\r\n imports: [CommonModule, MatModulesModule, TDynamicDataEditComponent],\r\n\r\n templateUrl: './form-submissions-config.component.html',\r\n styleUrl: './form-submissions-config.component.scss',\r\n\r\n})\r\nexport class FormSubmissionsConfigComponent {\r\n NGX_T_FORMS_CONFIG = inject(NGX_T_FORMS_CONFIG_TOKEN)\r\n\r\n panels = computed(() => {\r\n return SubmissionDialogConfig.map((panel) => {\r\n const error = this.getPanelError(panel)()\r\n return ({\r\n ...panel,\r\n error\r\n })\r\n });\r\n })\r\n\r\n private getPanelError(panel: typeof SubmissionDialogConfig[0]) {\r\n\r\n return computed(() => {\r\n const form = this.form()\r\n if (panel.primaryKey === 'submissionAPI') {\r\n return (form?.submissionHandle?.submissionAPI || []).length === 0 ? 'At least one submission API is required' : undefined\r\n }\r\n return undefined;\r\n })\r\n\r\n }\r\n\r\n\r\n _data = inject(MAT_DIALOG_DATA)\r\n store: FormsStoreService = this._data.store\r\n form = toSignal(\r\n this.store.selectors.selectFormInEdit$.pipe(map((form) => form?.form)),\r\n {\r\n initialValue: undefined\r\n }\r\n )\r\n formInputs = toSignal(\r\n this.store.formBuilderSelectors.getAllFormInputs$,\r\n {\r\n initialValue: []\r\n }\r\n )\r\n getFormApis(key: any) {\r\n return computed(() => {\r\n const form = this.form()\r\n return form?.submissionHandle?.[key as ('submissionAPI' | 'canSubmitAPI')] || []\r\n })\r\n }\r\n // private dialogRef: MatDialogRef<FormSubmissionsConfigComponent>,\r\n constructor(\r\n ) {\r\n\r\n\r\n\r\n\r\n }\r\n dataPlaceholder: undefined | {\r\n data: APIDataFetchingConfigurationInterface\r\n }\r\n keyInEdit: 'submissionAPI' | 'canSubmitAPI' | undefined = undefined\r\n get submissionApiListItemConfigElements(): ElementEditorInnerSectionElementInterface[] {\r\n return SubmissionAPIListItemConfig(this.NGX_T_FORMS_CONFIG).filter((el) => {\r\n if (!el.additionalTest) {\r\n return true\r\n }\r\n return el.additionalTest.every((test) => {\r\n return testAgainstItem(test, this.dataPlaceholder || { data: {} })\r\n })\r\n })\r\n }\r\n\r\n\r\n\r\n formValidationError = toSignal(this.store.formBuilderSelectors.formErrors$, {\r\n initialValue: []\r\n })\r\n readonly computedErrors = (element: ElementEditorInnerSectionElementInterface) => {\r\n return computed(() => {\r\n const allErrors = this.formValidationError();\r\n const errors = allErrors?.filter(error => error.key === element.deepBind?.join('.')) || []\r\n\r\n return [...[], ...errors];\r\n })\r\n }\r\n validationErrors(element: ElementEditorInnerSectionElementInterface): {\r\n key: string;\r\n message: string;\r\n }[] {\r\n const val = validateApiDataFetchingConfiguration((this.dataPlaceholder || {})).map((error) => ({\r\n message: error.message,\r\n key: 'data.' + error.key\r\n }))\r\n\r\n const errors = val?.filter(error => error.key === element.deepBind?.join('.')) || []\r\n return errors\r\n }\r\n elementBlur(\r\n event: any,\r\n element: ElementEditorInnerSectionElementInterface\r\n ) {\r\n console.info(\"[Admin Panel Input Element Editor] element blur\", event)\r\n }\r\n inputChange(event: any, element: ElementEditorInnerSectionElementInterface) {\r\n console.info(\"[Admin Panel Input Element Editor] input changed\", event)\r\n\r\n this.elementConfigurationChanged(event, element)\r\n // this.elementConfigurationChanged(event?.target?.value, element)\r\n }\r\n passFormChanges(event: any, element: ElementEditorInnerSectionElementInterface) {\r\n console.error(\"[Admin Panel Input Element Editor] pass form changes\", event)\r\n this.formInputChange(event, element)\r\n }\r\n formInputChange = (value: any, ele: ElementEditorInnerSectionElementInterface) => this.store.actionsFormBuilder.formInputChange(ele, value)\r\n elementConfigurationChanged(\r\n value: any,\r\n ele: ElementEditorInnerSectionElementInterface\r\n ) {\r\n\r\n\r\n const applyTo = { ...this.dataPlaceholder }\r\n if (ele.clearOnChange) {\r\n ele.clearOnChange.forEach((clear: string[]) => {\r\n assignDeepPropertyToObject(\r\n applyTo,\r\n clear,\r\n undefined\r\n );\r\n })\r\n\r\n }\r\n if (ele.deepBind) {\r\n assignDeepPropertyToObject(\r\n applyTo,\r\n ele.deepBind,\r\n value\r\n );\r\n }\r\n this.dataPlaceholder = {\r\n ...applyTo\r\n } as any\r\n\r\n // const formClass= this.formBuilder$$.value\r\n // const form = formClass?.form\r\n // if (form) {\r\n // formClass?.refreshController(form)\r\n // }\r\n\r\n // this.formBuilder$$.value?.syncInputController();\r\n // this.appStatesService.TFormBuilderService$.next(formClass)\r\n\r\n\r\n }\r\n addNewSubmitApi(submision: string) {\r\n this.dataPlaceholder = {\r\n data: {} as any\r\n }\r\n this.keyInEdit = submision as any\r\n\r\n\r\n\r\n }\r\n saveSubmitApi() {\r\n\r\n this.store.actionsFormBuilder.saveSubmitApi(this.dataPlaceholder, this.keyInEdit)\r\n this.dataPlaceholder = undefined\r\n this.keyInEdit = undefined\r\n }\r\n removeSubmissionApi(api: APIDataFetchingConfigurationInterface, key: string) {\r\n this.store.actionsFormBuilder.removeSubmissionApi(api, key as any)\r\n this.dataPlaceholder = undefined\r\n this.keyInEdit = undefined\r\n\r\n }\r\n cancelSubmitApi() {\r\n this.dataPlaceholder = undefined\r\n this.keyInEdit = undefined\r\n\r\n }\r\n matDialog = inject(MatDialog)\r\n close() {\r\n //close mat dialog\r\n this.matDialog.closeAll()\r\n\r\n }\r\n\r\n}\r\n","<div class=\"submissions-dialog\">\r\n <!-- Header: icon + title + subtitle per design rule §4 -->\r\n <header class=\"dialog-header\">\r\n <div class=\"header-icon\" aria-hidden=\"true\">\r\n <mat-icon>settings</mat-icon>\r\n </div>\r\n <div class=\"header-text\">\r\n <h1 class=\"header-title\">Form Submission Configuration</h1>\r\n <p class=\"header-subtitle\">Configure how your form is submitted and validated before submission.</p>\r\n </div>\r\n <button mat-icon-button mat-dialog-close [attr.aria-label]=\"'Close dialog'\" class=\"header-close\">\r\n <mat-icon>close</mat-icon>\r\n </button>\r\n </header>\r\n\r\n <section class=\"dialog-content\">\r\n <mat-accordion multi>\r\n @for (panel of panels(); track panel.id) {\r\n <mat-expansion-panel [expanded]=\"true\" class=\"config-panel\">\r\n <mat-expansion-panel-header class=\"panel-header\">\r\n <mat-panel-title class=\"panel-title\">\r\n <mat-icon class=\"panel-icon\" [color]=\"'primary'\">{{ panel.titleIcon }}</mat-icon>\r\n {{ panel.title }}\r\n </mat-panel-title>\r\n </mat-expansion-panel-header>\r\n\r\n <div class=\"panel-content\">\r\n <p class=\"panel-description\" [innerHTML]=\"panel.description\"></p>\r\n\r\n <!-- Dynamic form elements (submission message, toggles, etc.) -->\r\n @if ((panel?.elements || []).length > 0) {\r\n <div class=\"edit-section\">\r\n @for (edit of (panel?.elements || []); track edit.id) {\r\n <div class=\"edit-field\">\r\n <lib-t-dynamic-data-edit\r\n [editorConfig]=\"edit\"\r\n [data]=\"form()\"\r\n [validationErrors]=\"(computedErrors(edit)()) || []\"\r\n (valueChange)=\"passFormChanges($event, edit)\"\r\n [formInputs]=\"(formInputs()) || []\"\r\n (blur)=\"elementBlur($event, edit)\">\r\n </lib-t-dynamic-data-edit>\r\n </div>\r\n }\r\n </div>\r\n }\r\n\r\n <!-- API endpoints list -->\r\n <div class=\"api-section\">\r\n <h3 class=\"api-section-header\" [class.api-section-header--error]=\"panel.error\">\r\n {{ panel.primaryKey === 'submissionAPI' ? 'Submission Endpoints' : 'Validation Endpoints' }}\r\n </h3>\r\n\r\n @if ((getFormApis(panel.primaryKey)()).length === 0) {\r\n <div class=\"empty-state\" [class.empty-state--error]=\"panel.error\">\r\n <mat-icon class=\"empty-state-icon\">add_link</mat-icon>\r\n <p>\r\n {{ panel.primaryKey === 'submissionAPI'\r\n ? 'No submission endpoints configured. Add one to send form data.'\r\n : 'No validation endpoints configured. Add one to validate data before submission.' }}\r\n </p>\r\n </div>\r\n }\r\n\r\n @for (api of getFormApis(panel.primaryKey)(); track api._id) {\r\n <div class=\"api-list-item\">\r\n <span class=\"api-method-tag\" [class.api-method-tag--post]=\"api.httpMethod === 'POST'\">\r\n {{ api.httpMethod }}\r\n </span>\r\n <div class=\"api-details\">\r\n <span class=\"api-name\">{{ api.name }}</span>\r\n <span class=\"api-endpoint\" [matTooltip]=\"api.httpEndPoint\" matTooltipPosition=\"above\">\r\n {{ api.httpEndPoint }}\r\n </span>\r\n </div>\r\n <div class=\"api-item-actions\">\r\n <button\r\n [cdkCopyToClipboard]=\"api.httpEndPoint || ''\"\r\n mat-icon-button\r\n matTooltip=\"Copy URL\"\r\n [attr.aria-label]=\"'Copy endpoint URL'\"\r\n class=\"api-copy-btn\"\r\n (click)=\"$event.stopPropagation()\">\r\n <mat-icon>content_copy</mat-icon>\r\n </button>\r\n <button\r\n (click)=\"removeSubmissionApi(api,panel.primaryKey)\"\r\n mat-icon-button\r\n matTooltip=\"Remove endpoint\"\r\n [attr.aria-label]=\"'Remove endpoint ' + api.name\"\r\n class=\"api-remove-btn\">\r\n <mat-icon>delete</mat-icon>\r\n </button>\r\n </div>\r\n </div>\r\n <mat-divider></mat-divider>\r\n }\r\n\r\n @if (keyInEdit !== panel.primaryKey) {\r\n <div class=\"api-actions\">\r\n <button\r\n (click)=\"addNewSubmitApi(panel.primaryKey)\"\r\n mat-stroked-button\r\n color=\"primary\"\r\n class=\"add-endpoint-btn\">\r\n <mat-icon>add</mat-icon>\r\n Add new endpoint\r\n </button>\r\n </div>\r\n }\r\n\r\n @if (panel.error) {\r\n <mat-error class=\"panel-error\">{{ panel.error }}</mat-error>\r\n }\r\n </div>\r\n\r\n <!-- Inline edit form for new/editing endpoint -->\r\n @if (keyInEdit === panel.primaryKey) {\r\n <div class=\"edit-section edit-section--inline\">\r\n @for (element of submissionApiListItemConfigElements; track element.id) {\r\n <div class=\"edit-field\">\r\n <lib-t-dynamic-data-edit\r\n [editorConfig]=\"element\"\r\n [data]=\"dataPlaceholder\"\r\n [validationErrors]=\"validationErrors(element)\"\r\n (valueChange)=\"inputChange($event, element)\"\r\n [formInputs]=\"(formInputs()) || []\"\r\n (blur)=\"elementBlur($event, element)\">\r\n </lib-t-dynamic-data-edit>\r\n </div>\r\n }\r\n <mat-divider></mat-divider>\r\n <div class=\"inline-edit-actions\">\r\n <button mat-flat-button (click)=\"saveSubmitApi()\" color=\"primary\">Save</button>\r\n <button mat-button (click)=\"cancelSubmitApi()\" color=\"warn\">Cancel</button>\r\n </div>\r\n </div>\r\n }\r\n </div>\r\n </mat-expansion-panel>\r\n }\r\n </mat-accordion>\r\n </section>\r\n</div>\r\n","import { CommonModule } from '@angular/common';\r\nimport { Component, inject } from '@angular/core';\r\nimport { MatModulesModule } from '../../../../shared/modules/mat-modules.module';\r\nimport { FormJsonViewComponent } from '../form-json-view/form-json-view.component';\r\nimport { combineLatest, map, take, } from 'rxjs';\r\nimport { CdkDrag } from '@angular/cdk/drag-drop';\r\nimport { MatDialog } from '@angular/material/dialog';\r\nimport { SelectFormTemplateComponent } from '../select-form-template/select-form-template.component';\r\nimport { FormSubmissionsConfigComponent } from '../form-submissions-config/form-submissions-config.component'\r\nimport { FormsStoreService } from '../../../forms/store/forms-store.service';\r\n\r\n\r\nimport { FormInterface } from 'ngx-t-forms-types';\r\nimport { MatSnackBar } from '@angular/material/snack-bar';\r\n\r\n@Component({\r\n selector: 'app-forms-builder-menu',\r\n standalone: true,\r\n imports: [CommonModule, MatModulesModule, FormJsonViewComponent, CdkDrag],\r\n templateUrl: './forms-builder-menu.component.html',\r\n styleUrl: './forms-builder-menu.component.scss',\r\n})\r\nexport class FormsBuilderMenuComponent {\r\n form$ = this.store.selectors.selectFormInEdit$.pipe(map((form) => form?.form))\r\n\r\n\r\n showJson = false;\r\n \r\n snackBar = inject(MatSnackBar);\r\n constructor(private store: FormsStoreService) { }\r\n\r\n\r\n\r\n\r\n formCopy$ = this.form$.pipe(\r\n map((\r\n form\r\n ) => JSON.stringify(form || {}))\r\n )\r\n readonly dialog = inject(MatDialog);\r\n openDialog() {\r\n const dialogRef = this.dialog.open(SelectFormTemplateComponent);\r\n\r\n dialogRef.afterClosed().pipe(take(1)).subscribe(form => {\r\n if (form) {\r\n this.applyFormTemplate(form)\r\n }\r\n });\r\n }\r\n \r\n applyFormTemplate = (form: FormInterface) => this.store.actionsFormBuilder.applyFormTemplate(form)\r\n openFormSubmissionDialog() {\r\n\r\n const dialogRef = this.dialog.open(FormSubmissionsConfigComponent\r\n , {\r\n data: {\r\n store: this.store\r\n }\r\n })\r\n\r\n dialogRef.afterClosed().pipe(take(1)).subscribe(data => {\r\n if (data) {\r\n console.log(data)\r\n\r\n }\r\n });\r\n }\r\n errors$ = this.store.formBuilderSelectors.formErrors$\r\n\r\n configHasErrors$ = this.errors$.pipe(\r\n map(errors => errors?.some(error => error.key.split('.')?.[0] === 'submissionHandle'))\r\n )\r\n items$ = this.configHasErrors$.pipe(\r\n map(configHasErrors =>{\r\n \r\n return [\r\n {\r\n label: \"Templates\",\r\n icon: \"content_copy\",\r\n hint: \"Select a form to apply as a template\",\r\n action: () => this.openDialog()\r\n },\r\n {\r\n label: \"Submission Config\",\r\n hint: \"Setup form submission api and other configurations\",\r\n icon: \"api\",\r\n errors: configHasErrors,\r\n action: () => this.openFormSubmissionDialog()\r\n },\r\n ]\r\n })\r\n )\r\n vm$ = combineLatest(\r\n [\r\n this.items$,\r\n this.form$,\r\n this.formCopy$,\r\n this\r\n.configHasErrors$\r\n ]\r\n ).pipe(\r\n map(([items, form, formCopy,configHasErrors]) => ({ items, form, formCopy,configHasErrors }))\r\n )\r\n\r\n}\r\n","\r\n<ng-container *ngIf=\"( vm$|async) as vm\">\r\n\r\n <button [matBadge]=\"'!'\" matBadgeColor=\"warn\" [matTooltip]=\"vm.configHasErrors === false?'\r\n Click to view form configuration \r\n ':'Click to fix or add missing form configurations'\" [matBadgeHidden]=\"!!(vm.configHasErrors) === false\"\r\n [matMenuTriggerFor]=\"configMenu\" mat-flat-button>\r\n Config\r\n <mat-icon>\r\n settings_suggest\r\n </mat-icon>\r\n </button>\r\n \r\n <mat-menu #configMenu=\"matMenu\">\r\n \r\n \r\n <div mat-subheader style=\"padding-left: 16px;\">Form edit options</div>\r\n @for(item of vm.items;track item){\r\n <button [matTooltip]=\"item.hint\" matTooltipPosition=\"right\" style=\"padding-top:4px;padding-bottom:4px\"\r\n (click)=\"item?.action()\" mat-menu-item>\r\n <mat-icon>{{item.icon}}</mat-icon>\r\n \r\n <span>\r\n @if (item.errors) {\r\n <div style=\"line-height: normal;\"ks>\r\n {{item.label}}\r\n </div>\r\n <div style=\"line-height: normal;\">\r\n \r\n <mat-error>\r\n <small>\r\n Invalid configurations\r\n </small>\r\n \r\n </mat-error>\r\n </div>\r\n }@else {\r\n \r\n {{item.label}}\r\n \r\n }\r\n \r\n \r\n </span>\r\n \r\n </button>\r\n <mat-divider></mat-divider>\r\n }\r\n <button [matTooltip]=\"showJson?'Hide json':'Show json'\" (click)=\"showJson= !showJson\" mat-menu-item>\r\n <mat-icon>data_object</mat-icon>\r\n \r\n <span>\r\n <div>\r\n Show json\r\n </div>\r\n \r\n \r\n \r\n </span>\r\n </button>\r\n \r\n </mat-menu>\r\n \r\n <div *ngIf=\"showJson\" class=\"DragBoundary\">\r\n @defer (on viewport) {\r\n <mat-card cdkDragBoundary=\"DragBoundary\" cdkDrag class=\"menuCard\">\r\n <mat-toolbar\r\n style=\"background: var(--mat-sidenav-content-background-color);color:var(--mdc-filled-button-label-text-color, var(--mat-app-on-primary))\">\r\n <span>\r\n Json Form Value\r\n </span>\r\n <span class=\"spacer\"></span>\r\n \r\n <mat-icon cdkDragHandle style=\"\r\n margin:8px;\r\n color:var(--mat-divider-color, var(--mat-app-outline))\">\r\n drag_indicator\r\n </mat-icon>\r\n </mat-toolbar>\r\n <mat-divider style=\"margin-bottom: 12px;\"></mat-divider>\r\n \r\n \r\n <mat-card-content style=\" overflow-y: auto;\r\n max-height: calc(100vh - 150px);\">\r\n <app-form-json-view></app-form-json-view>\r\n </mat-card-content>\r\n <mat-card-actions>\r\n <span class=\"spacer\"></span>\r\n <button (click)=\"showJson = false\" mat-flat-button>\r\n Close\r\n </button>\r\n <button color=\"primary\" [cdkCopyToClipboard]=\"(vm.formCopy)||''\" matTooltip=\"Copy json to clipboard\"\r\n mat-flat-button>\r\n Copy\r\n </button>\r\n \r\n </mat-card-actions>\r\n \r\n \r\n </mat-card>\r\n }@placeholder {\r\n <div style=\"padding: 50px;display:flex;justify-content:center;align-items:center; text-align: center;\">\r\n <mat-spinner diameter=\"20\" />\r\n </div>\r\n \r\n }\r\n </div>\r\n</ng-container>\r\n"],"names":["i1.FormsStoreService","i3","i4","i5","i6","i8","i9","i10","i11","i12","i13","i14","i15"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;MAkBa,2BAA2B,CAAA;AAMxC,IAAA,WAAA,CAAoB,KAAuB,EAAA;QAAvB,IAAK,CAAA,KAAA,GAAL,KAAK;QAJzB,IAAY,CAAA,YAAA,GAAG,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC,YAAY;QAChD,IAAmB,CAAA,mBAAA,GAAG,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC,mBAAmB;AACrD,QAAA,IAAA,CAAA,SAAS,GAAG,MAAM,EAAC,YAA0C,EAAC;AAC9D,QAAA,IAAA,CAAA,IAAI,GAAG,MAAM,CAAM,eAAe,CAAC;AAW3C,QAAA,IAAA,CAAA,MAAM,GAAG,MAAM,CAAC,SAAS,CAAC;AAC1B,QAAA,IAAA,CAAA,QAAQ,GAAG,MAAM,CAAC,WAAW,CAAC;;IAR7B,QAAQ,GAAA;AACN,QAAA,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,UAAU,EAAE;;IAEnC,KAAK,GAAA;AACH,QAAA,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE;;AAKxB,IAAA,UAAU,CAAC,IAAkB,EAAA;QAC3B,IAAI,CAAC,KAAK,CAAC,kBAAkB,CAAC,iBAAiB,CAAC,IAAI,CAAC;AACrD,QAAA,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,IAAI,CAAC;;AAE1B,IAAA,aAAa,CAAC,SAAgB,EAAA;AAC5B,QAAA,OAAO,SAAS,CAAC,SAAS,CAAC;;+GAvBlB,2BAA2B,EAAA,IAAA,EAAA,CAAA,EAAA,KAAA,EAAAA,iBAAA,EAAA,CAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,SAAA,EAAA,CAAA,CAAA;AAA3B,IAAA,SAAA,IAAA,CAAA,IAAA,GAAA,EAAA,CAAA,oBAAA,CAAA,EAAA,UAAA,EAAA,QAAA,EAAA,OAAA,EAAA,SAAA,EAAA,IAAA,EAAA,2BAA2B,oFClBxC,stGA6EA,EAAA,MAAA,EAAA,CAAA,inHAAA,CAAA,EAAA,YAAA,EAAA,CAAA,EAAA,IAAA,EAAA,UAAA,EAAA,IAAA,ED/DY,gBAAgB,EAAE,EAAA,EAAA,IAAA,EAAA,WAAA,EAAA,IAAA,EAAA,EAAA,CAAA,SAAA,EAAA,QAAA,EAAA,6GAAA,EAAA,QAAA,EAAA,CAAA,WAAA,CAAA,EAAA,EAAA,EAAA,IAAA,EAAA,WAAA,EAAA,IAAA,EAAAC,IAAA,CAAA,OAAA,EAAA,QAAA,EAAA,UAAA,EAAA,MAAA,EAAA,CAAA,OAAA,EAAA,QAAA,EAAA,SAAA,EAAA,SAAA,EAAA,UAAA,CAAA,EAAA,QAAA,EAAA,CAAA,SAAA,CAAA,EAAA,EAAA,EAAA,IAAA,EAAA,WAAA,EAAA,IAAA,EAAAC,EAAA,CAAA,kBAAA,EAAA,QAAA,EAAA,mCAAA,EAAA,MAAA,EAAA,CAAA,OAAA,EAAA,MAAA,EAAA,OAAA,EAAA,UAAA,EAAA,aAAA,CAAA,EAAA,QAAA,EAAA,CAAA,oBAAA,CAAA,EAAA,EAAA,EAAA,IAAA,EAAA,WAAA,EAAA,IAAA,EAAAC,EAAA,CAAA,UAAA,EAAA,QAAA,EAAA,cAAA,EAAA,MAAA,EAAA,CAAA,oBAAA,EAAA,4BAAA,EAAA,oBAAA,EAAA,qBAAA,EAAA,qBAAA,EAAA,yBAAA,EAAA,YAAA,EAAA,iBAAA,CAAA,EAAA,QAAA,EAAA,CAAA,YAAA,CAAA,EAAA,EAAA,EAAA,IAAA,EAAA,UAAA,EAAA,IAAA,EAAA,YAAY,gJAAE,WAAW,EAAA,IAAA,EAAA,SAAA,EAAA,CAAA,EAAA,CAAA,CAAA;;4FAI1C,2BAA2B,EAAA,UAAA,EAAA,CAAA;kBAPvC,SAAS;+BACE,0BAA0B,EAAA,UAAA,EACxB,IAAI,EACP,OAAA,EAAA,CAAC,gBAAgB,EAAE,YAAY,EAAE,WAAW,CAAC,EAAA,QAAA,EAAA,stGAAA,EAAA,MAAA,EAAA,CAAA,inHAAA,CAAA,EAAA;;;AEPlD,SAAW,2BAA2B,CAAE,WAA4B,EAAA;IACxE,OAAO;AAEH,QAAA;AACE,YAAA,EAAE,EAAE,sCAAsC;YAC1C,IAAI,EAAE,kBAAkB,CAAC,iBAAiB;YAC1C,QAAQ,EAAE,kBAAkB,CAAC,OAAO;AACpC,YAAA,QAAQ,EAAE,IAAI;AACd,YAAA,uBAAuB,EAAE;AACvB,gBAAA,aAAa,EAAE,WAAW,CAAC,wBAAwB,CAAW;AAC9D,gBAAA,aAAa,EAAE,WAAW,CAAC,wBAAwB;AACpD,aAAA;AACD,YAAA,IAAI,EAAE,CAA4J,0JAAA,CAAA;YAClK,QAAQ,EAAE,CAAC,MAAM,CAAC;AAClB,YAAA,KAAK,EAAE,4BAA4B;AACpC,SAAA;AAED,QAAA;YACE,IAAI,EAAE,kBAAkB,CAAC,OAAO;AAChC,YAAA,QAAQ,EAAE;gBAER,MAAM;gBACN,eAAe;gBACf,sBAAsB;AACvB,aAAA;AACD,YAAA,cAAc,EAAE;AACd,gBAAA;AACE,oBAAA,QAAQ,EAAE,QAAQ;AAClB,oBAAA,QAAQ,EAAE;wBAER,MAAM;wBACN,eAAe;wBACf,sBAAsB;AACvB,qBAAA;AACiC;AAErC,aAAA;AACD,YAAA,IAAI,EAAE,CAAyG,uGAAA,CAAA;YAC/G,QAAQ,EAAE,kBAAkB,CAAC,cAAc;AAC3C,YAAA,KAAK,EAAE,wBAAwB;AAC/B,YAAA,EAAE,EAAE;AACL,SAAA;AACD,QAAA;YACE,IAAI,EAAE,kBAAkB,CAAC,OAAO;AAChC,YAAA,QAAQ,EAAE,CAAC,MAAM,EAAC,iBAAiB,CAAC;YACpC,QAAQ,EAAE,kBAAkB,CAAC,KAAK;AAClC,YAAA,KAAK,EAAE,mBAAmB;AAC1B,YAAA,IAAI,EAAE,iHAAiH;AACvH,YAAA,EAAE,EAAE;AACP,SAAA;KACA;AAAA;AACE,MAAM,wBAAwB,GAAiD;AAClF,IAAA;AACI,QAAA,EAAE,EAAC,sCAAsC;QACzC,IAAI,EAAC,kBAAkB,CAAC,OAAO;AAC/B,QAAA,QAAQ,EAAC,CAAC,kBAAkB,EAAC,mBAAmB,CAAC;QACjD,QAAQ,EAAC,kBAAkB,CAAC,KAAK;AACjC,QAAA,KAAK,EAAC,oBAAoB;AAC1B,QAAA,IAAI,EAAC,6DAA6D;AACrE,KAAA;AACD,IAAA;AACI,QAAA,EAAE,EAAC,sCAAsC;QACzC,IAAI,EAAC,kBAAkB,CAAC,OAAO;AAC/B,QAAA,QAAQ,EAAC,CAAC,kBAAkB,EAAC,gCAAgC,CAAC;QAC9D,QAAQ,EAAC,kBAAkB,CAAC,MAAM;AAClC,QAAA,KAAK,EAAC,4CAA4C;AACrD,KAAA;AACD,IAAA;AACE,QAAA,EAAE,EAAC,sCAAsC;QACzC,IAAI,EAAC,kBAAkB,CAAC,OAAO;QAC/B,QAAQ,EAAC,CAAC,eAAe,CAAC;QAC1B,QAAQ,EAAC,kBAAkB,CAAC,MAAM;AAClC,QAAA,KAAK,EAAC,mBAAmB;AAC5B;CACF;AAEO,MAAM,sBAAsB,GAAG;AACnC,IAAA;AACI,QAAA,SAAS,EAAC,MAAM;AAChB,QAAA,EAAE,EAAC,sCAAsC;AACzC,QAAA,KAAK,EAAC,0BAA0B;AAChC,QAAA,WAAW,EAAC,CAAA;AACa,gCAAA,CAAA;AACzB,QAAA,UAAU,EAAC,eAAe;AAC1B,QAAA,QAAQ,EAAG;AACd,KAAA;AACD,IAAA;AACI,QAAA,SAAS,EAAC,MAAM;AAChB,QAAA,KAAK,EAAC,uBAAuB;AAC7B,QAAA,WAAW,EAAC,CAAA;;;AAGuD,0EAAA,CAAA;AACnE,QAAA,UAAU,EAAC,cAAc;AACzB,QAAA,EAAE,EAAC,sCAAsC;AAC5C;CACH;;MC1EW,8BAA8B,CAAA;AAajC,IAAA,aAAa,CAAC,KAAuC,EAAA;QAE3D,OAAO,QAAQ,CAAC,MAAK;AACnB,YAAA,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,EAAE;AACxB,YAAA,IAAI,KAAK,CAAC,UAAU,KAAK,eAAe,EAAE;gBACxC,OAAO,CAAC,IAAI,EAAE,gBAAgB,EAAE,aAAa,IAAI,EAAE,EAAE,MAAM,KAAK,CAAC,GAAG,yCAAyC,GAAG,SAAS;;AAE3H,YAAA,OAAO,SAAS;AAClB,SAAC,CAAC;;AAmBJ,IAAA,WAAW,CAAC,GAAQ,EAAA;QAClB,OAAO,QAAQ,CAAC,MAAK;AACnB,YAAA,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,EAAE;YACxB,OAAO,IAAI,EAAE,gBAAgB,GAAG,GAAyC,CAAC,IAAI,EAAE;AAClF,SAAC,CAAC;;;AAGJ,IAAA,WAAA,GAAA;AA9CA,QAAA,IAAA,CAAA,kBAAkB,GAAG,MAAM,CAAC,wBAAwB,CAAC;AAErD,QAAA,IAAA,CAAA,MAAM,GAAG,QAAQ,CAAC,MAAK;AACrB,YAAA,OAAO,sBAAsB,CAAC,GAAG,CAAC,CAAC,KAAK,KAAI;gBAC1C,MAAM,KAAK,GAAG,IAAI,CAAC,aAAa,CAAC,KAAK,CAAC,EAAE;AACzC,gBAAA,QAAQ;AACN,oBAAA,GAAG,KAAK;oBACR;AACD,iBAAA;AACH,aAAC,CAAC;AACJ,SAAC,CAAC;AAeF,QAAA,IAAA,CAAA,KAAK,GAAG,MAAM,CAAC,eAAe,CAAC;AAC/B,QAAA,IAAA,CAAA,KAAK,GAAsB,IAAI,CAAC,KAAK,CAAC,KAAK;QAC3C,IAAI,CAAA,IAAA,GAAG,QAAQ,CACb,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC,iBAAiB,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,KAAK,IAAI,EAAE,IAAI,CAAC,CAAC,EACtE;AACE,YAAA,YAAY,EAAE;AACf,SAAA,CACF;QACD,IAAU,CAAA,UAAA,GAAG,QAAQ,CACnB,IAAI,CAAC,KAAK,CAAC,oBAAoB,CAAC,iBAAiB,EACjD;AACE,YAAA,YAAY,EAAE;AACf,SAAA,CACF;QAkBD,IAAS,CAAA,SAAA,GAAiD,SAAS;QAcnE,IAAmB,CAAA,mBAAA,GAAG,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,oBAAoB,CAAC,WAAW,EAAE;AAC1E,YAAA,YAAY,EAAE;AACf,SAAA,CAAC;AACO,QAAA,IAAA,CAAA,cAAc,GAAG,CAAC,OAAkD,KAAI;YAC/E,OAAO,QAAQ,CAAC,MAAK;AACnB,gBAAA,MAAM,SAAS,GAAG,IAAI,CAAC,mBAAmB,EAAE;gBAC5C,MAAM,MAAM,GAAG,SAAS,EAAE,MAAM,CAAC,KAAK,IAAI,KAAK,CAAC,GAAG,KAAK,OAAO,CAAC,QAAQ,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE;AAE1F,gBAAA,OAAO,CAAC,GAAG,EAAE,EAAE,GAAG,MAAM,CAAC;AAC3B,aAAC,CAAC;AACJ,SAAC;QA6BD,IAAe,CAAA,eAAA,GAAG,CAAC,KAAU,EAAE,GAA8C,KAAK,IAAI,CAAC,KAAK,CAAC,kBAAkB,CAAC,eAAe,CAAC,GAAG,EAAE,KAAK,CAAC;AAkE3I,QAAA,IAAA,CAAA,SAAS,GAAG,MAAM,CAAC,SAAS,CAAC;;AAtH7B,IAAA,IAAI,mCAAmC,GAAA;AACrC,QAAA,OAAO,2BAA2B,CAAC,IAAI,CAAC,kBAAkB,CAAC,CAAC,MAAM,CAAC,CAAC,EAAE,KAAI;AACxE,YAAA,IAAI,CAAC,EAAE,CAAC,cAAc,EAAE;AACtB,gBAAA,OAAO,IAAI;;YAEb,OAAO,EAAE,CAAC,cAAc,CAAC,KAAK,CAAC,CAAC,IAAI,KAAI;AACtC,gBAAA,OAAO,eAAe,CAAC,IAAI,EAAE,IAAI,CAAC,eAAe,IAAI,EAAE,IAAI,EAAE,EAAE,EAAE,CAAC;AACpE,aAAC,CAAC;AACJ,SAAC,CAAC;;AAgBJ,IAAA,gBAAgB,CAAC,OAAkD,EAAA;QAIjE,MAAM,GAAG,GAAG,oCAAoC,EAAE,IAAI,CAAC,eAAe,IAAI,EAAE,EAAE,CAAC,GAAG,CAAC,CAAC,KAAK,MAAM;YAC7F,OAAO,EAAE,KAAK,CAAC,OAAO;AACtB,YAAA,GAAG,EAAE,OAAO,GAAG,KAAK,CAAC;AACtB,SAAA,CAAC,CAAC;QAEH,MAAM,MAAM,GAAG,GAAG,EAAE,MAAM,CAAC,KAAK,IAAI,KAAK,CAAC,GAAG,KAAK,OAAO,CAAC,QAAQ,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE;AACpF,QAAA,OAAO,MAAM;;IAEf,WAAW,CACT,KAAU,EACV,OAAkD,EAAA;AAElD,QAAA,OAAO,CAAC,IAAI,CAAC,iDAAiD,EAAE,KAAK,CAAC;;IAExE,WAAW,CAAC,KAAU,EAAE,OAAkD,EAAA;AACxE,QAAA,OAAO,CAAC,IAAI,CAAC,kDAAkD,EAAE,KAAK,CAAC;AAEvE,QAAA,IAAI,CAAC,2BAA2B,CAAC,KAAK,EAAE,OAAO,CAAC;;;IAGlD,eAAe,CAAC,KAAU,EAAE,OAAkD,EAAA;AAC5E,QAAA,OAAO,CAAC,KAAK,CAAC,sDAAsD,EAAE,KAAK,CAAC;AAC5E,QAAA,IAAI,CAAC,eAAe,CAAC,KAAK,EAAE,OAAO,CAAC;;IAGtC,2BAA2B,CACzB,KAAU,EACV,GAA8C,EAAA;QAI9C,MAAM,OAAO,GAAG,EAAE,GAAG,IAAI,CAAC,eAAe,EAAE;AAC3C,QAAA,IAAI,GAAG,CAAC,aAAa,EAAE;YACrB,GAAG,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC,KAAe,KAAI;AAC5C,gBAAA,0BAA0B,CACxB,OAAO,EACP,KAAK,EACL,SAAS,CACV;AACH,aAAC,CAAC;;AAGJ,QAAA,IAAI,GAAG,CAAC,QAAQ,EAAE;YAChB,0BAA0B,CACxB,OAAO,EACP,GAAG,CAAC,QAAQ,EACZ,KAAK,CACN;;QAEH,IAAI,CAAC,eAAe,GAAG;AACrB,YAAA,GAAG;SACG;;;;;;;;;AAaV,IAAA,eAAe,CAAC,SAAiB,EAAA;QAC/B,IAAI,CAAC,eAAe,GAAG;AACrB,YAAA,IAAI,EAAE;SACP;AACD,QAAA,IAAI,CAAC,SAAS,GAAG,SAAgB;;IAKnC,aAAa,GAAA;AAEX,QAAA,IAAI,CAAC,KAAK,CAAC,kBAAkB,CAAC,aAAa,CAAC,IAAI,CAAC,eAAe,EAAE,IAAI,CAAC,SAAS,CAAC;AACjF,QAAA,IAAI,CAAC,eAAe,GAAG,SAAS;AAChC,QAAA,IAAI,CAAC,SAAS,GAAG,SAAS;;IAE5B,mBAAmB,CAAC,GAA0C,EAAE,GAAW,EAAA;QACzE,IAAI,CAAC,KAAK,CAAC,kBAAkB,CAAC,mBAAmB,CAAC,GAAG,EAAE,GAAU,CAAC;AAClE,QAAA,IAAI,CAAC,eAAe,GAAG,SAAS;AAChC,QAAA,IAAI,CAAC,SAAS,GAAG,SAAS;;IAG5B,eAAe,GAAA;AACb,QAAA,IAAI,CAAC,eAAe,GAAG,SAAS;AAChC,QAAA,IAAI,CAAC,SAAS,GAAG,SAAS;;IAI5B,KAAK,GAAA;;AAEH,QAAA,IAAI,CAAC,SAAS,CAAC,QAAQ,EAAE;;+GAnLhB,8BAA8B,EAAA,IAAA,EAAA,EAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,SAAA,EAAA,CAAA,CAAA;AAA9B,IAAA,SAAA,IAAA,CAAA,IAAA,GAAA,EAAA,CAAA,oBAAA,CAAA,EAAA,UAAA,EAAA,QAAA,EAAA,OAAA,EAAA,SAAA,EAAA,IAAA,EAAA,8BAA8B,uFC7B3C,wtNAgJA,EAAA,MAAA,EAAA,CAAA,ghJAAA,CAAA,EAAA,YAAA,EAAA,CAAA,EAAA,IAAA,EAAA,UAAA,EAAA,IAAA,EDzHY,YAAY,EAAE,EAAA,EAAA,IAAA,EAAA,UAAA,EAAA,IAAA,EAAA,gBAAgB,65DAAE,yBAAyB,EAAA,QAAA,EAAA,yBAAA,EAAA,MAAA,EAAA,CAAA,cAAA,EAAA,YAAA,EAAA,MAAA,EAAA,kBAAA,CAAA,EAAA,OAAA,EAAA,CAAA,aAAA,EAAA,MAAA,CAAA,EAAA,CAAA,EAAA,CAAA,CAAA;;4FAMxD,8BAA8B,EAAA,UAAA,EAAA,CAAA;kBAT1C,SAAS;+BACE,6BAA6B,EAAA,UAAA,EAC3B,IAAI,EACP,OAAA,EAAA,CAAC,YAAY,EAAE,gBAAgB,EAAE,yBAAyB,CAAC,EAAA,QAAA,EAAA,wtNAAA,EAAA,MAAA,EAAA,CAAA,ghJAAA,CAAA,EAAA;;;MEDzD,yBAAyB,CAAA;AAOpC,IAAA,WAAA,CAAoB,KAAwB,EAAA;QAAxB,IAAK,CAAA,KAAA,GAAL,KAAK;QANzB,IAAK,CAAA,KAAA,GAAG,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC,iBAAiB,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,KAAK,IAAI,EAAE,IAAI,CAAC,CAAC;QAG9E,IAAQ,CAAA,QAAA,GAAG,KAAK;AAEjB,QAAA,IAAA,CAAA,QAAQ,GAAG,MAAM,CAAC,WAAW,CAAC;QAM7B,IAAS,CAAA,SAAA,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CACzB,GAAG,CAAC,CACF,IAAI,KACD,IAAI,CAAC,SAAS,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC,CACjC;AACQ,QAAA,IAAA,CAAA,MAAM,GAAG,MAAM,CAAC,SAAS,CAAC;AAWnC,QAAA,IAAA,CAAA,iBAAiB,GAAG,CAAC,IAAmB,KAAK,IAAI,CAAC,KAAK,CAAC,kBAAkB,CAAC,iBAAiB,CAAC,IAAI,CAAC;QAiBlG,IAAO,CAAA,OAAA,GAAG,IAAI,CAAC,KAAK,CAAC,oBAAoB,CAAC,WAAW;AAErD,QAAA,IAAA,CAAA,gBAAgB,GAAI,IAAI,CAAC,OAAO,CAAC,IAAI,CACnC,GAAG,CAAC,MAAM,IAAI,MAAM,EAAE,IAAI,CAAC,KAAK,IAAI,KAAK,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,KAAK,kBAAkB,CAAC,CAAC,CACvF;QACD,IAAM,CAAA,MAAA,GAAG,IAAI,CAAC,gBAAgB,CAAC,IAAI,CACjC,GAAG,CAAC,eAAe,IAAG;YAEpB,OAAO;AACL,gBAAA;AACE,oBAAA,KAAK,EAAE,WAAW;AAClB,oBAAA,IAAI,EAAE,cAAc;AACpB,oBAAA,IAAI,EAAE,sCAAsC;AAC5C,oBAAA,MAAM,EAAE,MAAM,IAAI,CAAC,UAAU;AAC9B,iBAAA;AACD,gBAAA;AACE,oBAAA,KAAK,EAAE,mBAAmB;AAC1B,oBAAA,IAAI,EAAE,oDAAoD;AAC1D,oBAAA,IAAI,EAAE,KAAK;AACX,oBAAA,MAAM,EAAE,eAAe;AACvB,oBAAA,MAAM,EAAE,MAAM,IAAI,CAAC,wBAAwB;AAC5C,iBAAA;aACF;SACF,CAAC,CACH;QACD,IAAG,CAAA,GAAA,GAAG,aAAa,CACjB;AACE,YAAA,IAAI,CAAC,MAAM;AACX,YAAA,IAAI,CAAC,KAAK;AACV,YAAA,IAAI,CAAC,SAAS;YACd;iBACL;AACI,SAAA,CACF,CAAC,IAAI,CACJ,GAAG,CAAC,CAAC,CAAC,KAAK,EAAE,IAAI,EAAE,QAAQ,EAAC,eAAe,CAAC,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,QAAQ,EAAC,eAAe,EAAE,CAAC,CAAC,CAC9F;;IA9DD,UAAU,GAAA;QACR,MAAM,SAAS,GAAG,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,2BAA2B,CAAC;AAE/D,QAAA,SAAS,CAAC,WAAW,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,IAAI,IAAG;YACrD,IAAI,IAAI,EAAE;AACR,gBAAA,IAAI,CAAC,iBAAiB,CAAC,IAAI,CAAC;;AAEhC,SAAC,CAAC;;IAIJ,wBAAwB,GAAA;QAEtB,MAAM,SAAS,GAAG,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,8BAA8B,EAC7D;AACA,YAAA,IAAI,EAAE;gBACJ,KAAK,EAAE,IAAI,CAAC;AACb;AACF,SAAA,CAAC;AAEJ,QAAA,SAAS,CAAC,WAAW,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,IAAI,IAAG;YACrD,IAAI,IAAI,EAAE;AACR,gBAAA,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC;;AAGrB,SAAC,CAAC;;+GA3CO,yBAAyB,EAAA,IAAA,EAAA,CAAA,EAAA,KAAA,EAAAH,iBAAA,EAAA,CAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,SAAA,EAAA,CAAA,CAAA;AAAzB,IAAA,SAAA,IAAA,CAAA,IAAA,GAAA,EAAA,CAAA,oBAAA,CAAA,EAAA,UAAA,EAAA,QAAA,EAAA,OAAA,EAAA,SAAA,EAAA,IAAA,EAAA,yBAAyB,ECtBtC,YAAA,EAAA,IAAA,EAAA,QAAA,EAAA,wBAAA,EAAA,QAAA,EAAA,EAAA,EAAA,QAAA,EAAA,4wHA4GA,ED1FY,MAAA,EAAA,CAAA,yJAAA,CAAA,EAAA,YAAA,EAAA,CAAA,EAAA,IAAA,EAAA,UAAA,EAAA,IAAA,EAAA,YAAY,2LAAE,gBAAgB,EAAA,EAAA,EAAA,IAAA,EAAA,WAAA,EAAA,IAAA,EAAAC,IAAA,CAAA,QAAA,EAAA,QAAA,EAAA,YAAA,EAAA,MAAA,EAAA,CAAA,eAAA,EAAA,iBAAA,EAAA,kBAAA,EAAA,kBAAA,EAAA,UAAA,EAAA,qBAAA,EAAA,cAAA,EAAA,gBAAA,CAAA,EAAA,EAAA,EAAA,IAAA,EAAA,WAAA,EAAA,IAAA,EAAAC,EAAA,CAAA,SAAA,EAAA,QAAA,EAAA,6GAAA,EAAA,QAAA,EAAA,CAAA,WAAA,CAAA,EAAA,EAAA,EAAA,IAAA,EAAA,WAAA,EAAA,IAAA,EAAAC,EAAA,CAAA,UAAA,EAAA,QAAA,EAAA,aAAA,EAAA,MAAA,EAAA,CAAA,UAAA,EAAA,OAAA,CAAA,EAAA,EAAA,EAAA,IAAA,EAAA,WAAA,EAAA,IAAA,EAAAC,IAAA,CAAA,OAAA,EAAA,QAAA,EAAA,UAAA,EAAA,MAAA,EAAA,CAAA,OAAA,EAAA,QAAA,EAAA,SAAA,EAAA,SAAA,EAAA,UAAA,CAAA,EAAA,QAAA,EAAA,CAAA,SAAA,CAAA,EAAA,EAAA,EAAA,IAAA,EAAA,WAAA,EAAA,IAAA,EAAA,EAAA,CAAA,4BAAA,EAAA,QAAA,EAAA,iCAAA,EAAA,EAAA,EAAA,IAAA,EAAA,WAAA,EAAA,IAAA,EAAAC,IAAA,CAAA,OAAA,EAAA,QAAA,EAAA,UAAA,EAAA,MAAA,EAAA,CAAA,eAAA,EAAA,YAAA,EAAA,iBAAA,EAAA,kBAAA,EAAA,WAAA,EAAA,WAAA,EAAA,gBAAA,EAAA,aAAA,EAAA,OAAA,EAAA,WAAA,CAAA,EAAA,OAAA,EAAA,CAAA,QAAA,EAAA,OAAA,CAAA,EAAA,QAAA,EAAA,CAAA,SAAA,CAAA,EAAA,EAAA,EAAA,IAAA,EAAA,WAAA,EAAA,IAAA,EAAAA,IAAA,CAAA,WAAA,EAAA,QAAA,EAAA,iBAAA,EAAA,MAAA,EAAA,CAAA,MAAA,EAAA,UAAA,EAAA,eAAA,CAAA,EAAA,QAAA,EAAA,CAAA,aAAA,CAAA,EAAA,EAAA,EAAA,IAAA,EAAA,WAAA,EAAA,IAAA,EAAAA,IAAA,CAAA,cAAA,EAAA,QAAA,EAAA,6CAAA,EAAA,MAAA,EAAA,CAAA,sBAAA,EAAA,mBAAA,EAAA,oBAAA,EAAA,4BAAA,CAAA,EAAA,OAAA,EAAA,CAAA,YAAA,EAAA,YAAA,EAAA,YAAA,EAAA,aAAA,CAAA,EAAA,QAAA,EAAA,CAAA,gBAAA,CAAA,EAAA,EAAA,EAAA,IAAA,EAAA,WAAA,EAAA,IAAA,EAAAC,EAAA,CAAA,kBAAA,EAAA,QAAA,EAAA,mCAAA,EAAA,MAAA,EAAA,CAAA,OAAA,EAAA,MAAA,EAAA,OAAA,EAAA,UAAA,EAAA,aAAA,CAAA,EAAA,QAAA,EAAA,CAAA,oBAAA,CAAA,EAAA,EAAA,EAAA,IAAA,EAAA,WAAA,EAAA,IAAA,EAAAC,IAAA,CAAA,QAAA,EAAA,QAAA,EAAA,uBAAA,EAAA,MAAA,EAAA,CAAA,IAAA,CAAA,EAAA,EAAA,EAAA,IAAA,EAAA,WAAA,EAAA,IAAA,EAAAC,EAAA,CAAA,UAAA,EAAA,QAAA,EAAA,cAAA,EAAA,MAAA,EAAA,CAAA,oBAAA,EAAA,4BAAA,EAAA,oBAAA,EAAA,qBAAA,EAAA,qBAAA,EAAA,yBAAA,EAAA,YAAA,EAAA,iBAAA,CAAA,EAAA,QAAA,EAAA,CAAA,YAAA,CAAA,EAAA,CAAA,EAAA,sBAAA,EAAA,CAAA,MAAA,CAAAC,EAAA,CAAA,kBAAA,EAAA,OAAA,wBAAA,CAAA,CAAA,IAAA,CAAA,CAAA,IAAA,CAAA,CAAA,OAAA,CAAA,EAAAC,IAAA,CAAA,aAAA,EAAAR,EAAA,CAAA,SAAA,EAAAS,IAAA,CAAA,OAAA,EAAAA,IAAA,CAAA,cAAA,EAAAA,IAAA,CAAA,cAAA,EAAAR,EAAA,CAAA,UAAA,EAAAC,IAAA,CAAA,OAAA,EAAAQ,GAAA,CAAA,UAAA,EAAAJ,EAAA,CAAA,UAAA,EAAA,OAAA,qDAAA,CAAA,CAAA,IAAA,CAAA,CAAA,IAAA,CAAA,CAAA,qBAAA,CAAA,CAAA,CAAA,EAAA,CAAA,CAAA;;iGAI7B,yBAAyB,EAAA,mBAAA,EAAA,MAAA,CAAA,OAAA,wBAAA,CAAA,CAAA,IAAA,CAAA,CAAA,IAAA,CAAA,CAAA,OAAA,CAAA,EAAA,OAAA,qDAAA,CAAA,CAAA,IAAA,CAAA,CAAA,IAAA,CAAA,CAAA,qBAAA,CAAA,CAAA,EAAA,eAAA,EAAA,CAAA,OAAA,EAAA,qBAAA,MAAA,EAAA,UAAA,EAAA,CAAA;sBAPrC,SAAS;mCACE,wBAAwB,EAAA,UAAA,EACtB,IAAI,EAAA,OAAA,EACP,CAAC,YAAY,EAAE,gBAAgB,EAAE,qBAAqB,EAAE,OAAO,CAAC,EAAA,QAAA,EAAA,4wHAAA,EAAA,MAAA,EAAA,CAAA,yJAAA,CAAA,EAAA;;;;;"}