@cqa-lib/cqa-ui 1.1.556 → 1.1.557-delta.10

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.
@@ -7156,6 +7156,15 @@ class TableTemplateComponent {
7156
7156
  this.viewMode = 'list';
7157
7157
  this.viewModeLabels = { list: 'List', modular: 'Modular' };
7158
7158
  this.viewModeChange = new EventEmitter();
7159
+ // Stable reference fed to <cqa-segment-control [segments]>. A getter here
7160
+ // allocates a new array each CD tick, which the segment control then sees as
7161
+ // a changed Input — its updateIndicator runs inside a queueMicrotask, the
7162
+ // zone re-ticks via onMicrotaskEmpty, and the loop pegs CPU at 100% with
7163
+ // HTTP requests starved. Rebuilt only when viewModeLabels actually changes.
7164
+ this.viewModeSegments = [
7165
+ { label: 'List', value: 'list', icon: 'view_list' },
7166
+ { label: 'Modular', value: 'modular', icon: 'folder' },
7167
+ ];
7159
7168
  // Data input
7160
7169
  this.data = [];
7161
7170
  // Empty state inputs
@@ -7261,12 +7270,6 @@ class TableTemplateComponent {
7261
7270
  // Export modal state
7262
7271
  this.isExportModalOpen = false;
7263
7272
  }
7264
- get viewModeSegments() {
7265
- return [
7266
- { label: this.viewModeLabels.list, value: 'list', icon: 'view_list' },
7267
- { label: this.viewModeLabels.modular, value: 'modular', icon: 'folder' },
7268
- ];
7269
- }
7270
7273
  onViewModeChange(value) {
7271
7274
  const next = value === 'modular' ? 'modular' : 'list';
7272
7275
  if (next !== this.viewMode) {
@@ -7319,6 +7322,12 @@ class TableTemplateComponent {
7319
7322
  if (changes['selectedItems'] || changes['data']) {
7320
7323
  this.restoreSelectionState();
7321
7324
  }
7325
+ if (changes['viewModeLabels']) {
7326
+ this.viewModeSegments = [
7327
+ { label: this.viewModeLabels.list, value: 'list', icon: 'view_list' },
7328
+ { label: this.viewModeLabels.modular, value: 'modular', icon: 'folder' },
7329
+ ];
7330
+ }
7322
7331
  }
7323
7332
  initializeComponent() {
7324
7333
  if (this.isEmptyState) {
@@ -7940,6 +7949,7 @@ const DEFAULT_MODULAR_LABELS = {
7940
7949
  folderColumnLabel: 'Folder',
7941
7950
  unorganizedRowLabel: 'Unorganized',
7942
7951
  noFoldersFound: 'No Folders Found',
7952
+ noFoldersEmptyDescription: 'Click the + icon above to create your first folder.',
7943
7953
  folderTooltipTotalSingular: '1 total test case including subfolders',
7944
7954
  folderTooltipTotalPlural: '{n} total test cases including subfolders',
7945
7955
  };
@@ -9728,10 +9738,10 @@ FolderSidebarComponent.RENAME_LIMIT_FLASH_MS = 600;
9728
9738
  * position is within this many px of the end. */
9729
9739
  FolderSidebarComponent.SCROLL_LOAD_THRESHOLD_PX = 64;
9730
9740
  FolderSidebarComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.4.0", ngImport: i0, type: FolderSidebarComponent, deps: [{ token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Component });
9731
- FolderSidebarComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version: "13.4.0", type: FolderSidebarComponent, selector: "cqa-folder-sidebar", inputs: { folders: "folders", selectedFolderId: "selectedFolderId", expandedFolderIds: "expandedFolderIds", unorganizedCount: "unorganizedCount", allowCreate: "allowCreate", allowRename: "allowRename", allowDelete: "allowDelete", allowMove: "allowMove", allowDuplicate: "allowDuplicate", allowDrop: "allowDrop", showCounts: "showCounts", collapsed: "collapsed", foldersAccordionExpanded: "foldersAccordionExpanded", labels: "labels", serverSideSearch: "serverSideSearch", rootTotal: "rootTotal", folderSearchLoading: "folderSearchLoading", rootFoldersLoading: "rootFoldersLoading", savingFolderIds: "savingFolderIds", searchValue: "searchValue" }, outputs: { folderSelected: "folderSelected", folderExpansionToggled: "folderExpansionToggled", folderChildrenRequested: "folderChildrenRequested", folderLoadMoreRequested: "folderLoadMoreRequested", searchChange: "searchChange", rootLoadMoreRequested: "rootLoadMoreRequested", folderCreated: "folderCreated", folderCreateRequested: "folderCreateRequested", folderRenamed: "folderRenamed", folderDeleted: "folderDeleted", folderMoveRequested: "folderMoveRequested", folderDuplicateRequested: "folderDuplicateRequested", testsDropped: "testsDropped", folderDropped: "folderDropped", collapsedChange: "collapsedChange", foldersAccordionExpandedChange: "foldersAccordionExpandedChange" }, host: { listeners: { "document:click": "onDocumentClick()", "document:keydown.escape": "onEscape()", "document:dragend": "onDocumentDragEnd()" }, classAttribute: "cqa-ui-root" }, ngImport: i0, template: "<!-- Reusable indeterminate loading spinner. Render via\n <ng-container *ngTemplateOutlet=\"loadingSpinner; context: { size: 14 }\"></ng-container>. -->\n<ng-template #loadingSpinner let-size=\"size\">\n <svg [attr.width]=\"size || 16\" [attr.height]=\"size || 16\" viewBox=\"0 0 50 50\" aria-label=\"loading\" xmlns=\"http://www.w3.org/2000/svg\">\n <circle cx=\"25\" cy=\"25\" r=\"20\" stroke=\"#E5E7EB\" stroke-width=\"6\" fill=\"none\"></circle>\n <path d=\"M45 25a20 20 0 0 0-20-20\" stroke=\"#4F46E5\" stroke-width=\"6\" fill=\"none\" stroke-linecap=\"round\">\n <animateTransform attributeName=\"transform\" type=\"rotate\" from=\"0 25 25\" to=\"360 25 25\" dur=\"0.8s\" repeatCount=\"indefinite\"></animateTransform>\n </path>\n </svg>\n</ng-template>\n\n<!-- Reusable folder icon. Render via <ng-container *ngTemplateOutlet=\"folderIcon; context: { color: node.color }\"></ng-container>. -->\n<ng-template #folderIcon let-color=\"color\">\n <span class=\"cqa-inline-flex cqa-items-center cqa-justify-center cqa-w-4 cqa-h-4 cqa-flex-shrink-0\">\n <svg width=\"13\" height=\"12\" viewBox=\"0 0 13 12\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\">\n <path d=\"M12.1916 9.85824C12.1916 10.1677 12.0687 10.4644 11.8499 10.6832C11.6311 10.902 11.3343 11.0249 11.0249 11.0249H1.69157C1.38215 11.0249 1.0854 10.902 0.866611 10.6832C0.647819 10.4644 0.524902 10.1677 0.524902 9.85824V1.69157C0.524902 1.38215 0.647819 1.0854 0.866611 0.866611C1.0854 0.647819 1.38215 0.524902 1.69157 0.524902H4.60824L5.7749 2.2749H11.0249C11.3343 2.2749 11.6311 2.39782 11.8499 2.61661C12.0687 2.8354 12.1916 3.13215 12.1916 3.44157V9.85824Z\" [attr.stroke]=\"color || '#99999E'\" stroke-width=\"1.05\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/>\n </svg>\n </span>\n</ng-template>\n\n<aside\n class=\"cqa-flex cqa-flex-col cqa-bg-white cqa-h-full cqa-min-h-0\"\n [class.cqa-border]=\"!collapsed\"\n [class.cqa-border-neutral-200]=\"!collapsed\"\n [class.cqa-rounded-lg]=\"!collapsed\"\n [style.width.px]=\"collapsed ? 22 : 240\"\n style=\"overflow: hidden; transition: width 260ms cubic-bezier(0.4, 0, 0.2, 1);\"\n>\n <!-- Collapsed rail: the entire sidebar becomes a single click target so the\n slim strip reads as an obvious \"expand me\" handle. -->\n <button\n *ngIf=\"collapsed\"\n type=\"button\"\n style=\"width: 22px;\"\n class=\"cqa-group cqa-flex cqa-flex-col cqa-items-center cqa-flex-shrink-0 cqa-h-full cqa-py-2 cqa-gap-3 cqa-bg-neutral-50 cqa-border cqa-border-neutral-200 cqa-text-neutral-500 hover:cqa-bg-indigo-50 hover:cqa-border-indigo-200 hover:cqa-text-indigo-600 focus:cqa-outline-none focus:cqa-ring-2 focus:cqa-ring-indigo-200 cqa-cursor-pointer\"\n (click)=\"togglePanel()\"\n [attr.aria-label]=\"'Expand folders panel'\"\n [title]=\"'Expand folders panel'\"\n >\n <mat-icon style=\"font-size:16px;width:16px;height:16px\">chevron_right</mat-icon>\n <span class=\"cqa-inline-flex cqa-items-center cqa-justify-center\" aria-hidden=\"true\">\n <svg width=\"13\" height=\"12\" viewBox=\"0 0 13 12\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n <path d=\"M12.1916 9.85824C12.1916 10.1677 12.0687 10.4644 11.8499 10.6832C11.6311 10.902 11.3343 11.0249 11.0249 11.0249H1.69157C1.38215 11.0249 1.0854 10.902 0.866611 10.6832C0.647819 10.4644 0.524902 10.1677 0.524902 9.85824V1.69157C0.524902 1.38215 0.647819 1.0854 0.866611 0.866611C1.0854 0.647819 1.38215 0.524902 1.69157 0.524902H4.60824L5.7749 2.2749H11.0249C11.3343 2.2749 11.6311 2.39782 11.8499 2.61661C12.0687 2.8354 12.1916 3.13215 12.1916 3.44157V9.85824Z\" stroke=\"currentColor\" stroke-width=\"1.05\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/>\n </svg>\n </span>\n </button>\n\n <div *ngIf=\"!collapsed\" class=\"cqa-flex cqa-flex-col cqa-h-full cqa-min-h-0 cqa-pb-2\" style=\"width: 240px;\">\n <!-- Header (expanded) -->\n <div\n class=\"cqa-flex cqa-items-center cqa-justify-between cqa-py-1 cqa-px-3\"\n style=\"border-bottom: 1px solid #E2E2E3; margin-bottom: 8px;\"\n >\n <span class=\"cqa-text-sm cqa-font-semibold cqa-text-neutral-900 cqa-py-0.5\">{{ labels.folders }}</span>\n <div class=\"cqa-flex cqa-items-center cqa-gap-1\">\n <button\n type=\"button\"\n class=\"cqa-p-1 cqa-rounded hover:cqa-bg-neutral-100 cqa-text-neutral-500\"\n (click)=\"togglePanel()\"\n [attr.aria-label]=\"'Collapse folders panel'\"\n [title]=\"'Collapse folders panel'\"\n >\n <mat-icon style=\"font-size:18px;width:18px;height:18px\">keyboard_double_arrow_left</mat-icon>\n </button>\n <button\n type=\"button\"\n class=\"cqa-p-1 cqa-rounded hover:cqa-bg-neutral-100\"\n [class.cqa-text-neutral-500]=\"!searchVisible\"\n [class.cqa-text-indigo-600]=\"searchVisible\"\n [class.cqa-bg-neutral-100]=\"searchVisible\"\n (click)=\"toggleSearchVisible()\"\n [attr.aria-label]=\"searchVisible ? 'Hide folder search' : 'Search folders'\"\n [attr.aria-pressed]=\"searchVisible\"\n [title]=\"searchVisible ? 'Hide folder search' : 'Search folders'\"\n >\n <mat-icon style=\"font-size:18px;width:18px;height:18px\">search</mat-icon>\n </button>\n <button\n *ngIf=\"allowCreate\"\n type=\"button\"\n class=\"cqa-p-1 cqa-rounded hover:cqa-bg-neutral-100 cqa-text-neutral-500\"\n (click)=\"requestCreate(null)\"\n [attr.aria-label]=\"labels.newFolder\"\n [title]=\"labels.newFolder\"\n >\n <mat-icon style=\"font-size:18px;width:18px;height:18px\">add</mat-icon>\n </button>\n </div>\n </div>\n\n <ng-container>\n <!-- Search (toggled via the search icon in the header) -->\n <div\n class=\"cqa-grid\"\n style=\"transition: grid-template-rows 300ms cubic-bezier(0.4, 0, 0.2, 1);\"\n [style.grid-template-rows]=\"searchVisible ? '1fr' : '0fr'\"\n >\n <div class=\"cqa-min-h-0\" style=\"overflow: hidden;\">\n <div class=\"cqa-px-3 cqa-pb-2\">\n <div class=\"cqa-relative\">\n <cqa-search-bar\n size=\"sm\"\n [fullWidth]=\"true\"\n [value]=\"searchValue\"\n [autoFocus]=\"searchVisible\"\n [placeholder]=\"labels.searchFoldersPlaceholder\"\n [showClear]=\"true\"\n (valueChange)=\"onSearchInput($event)\"\n (cleared)=\"onSearchClear()\"\n ></cqa-search-bar>\n <span\n *ngIf=\"folderSearchLoading\"\n class=\"cqa-absolute cqa-right-8 cqa-inset-y-0 cqa-flex cqa-items-center\"\n aria-live=\"polite\"\n aria-label=\"Searching folders\"\n >\n <ng-container *ngTemplateOutlet=\"loadingSpinner; context: { size: 14 }\"></ng-container>\n </span>\n </div>\n </div>\n </div>\n </div>\n\n <!-- Organized section header \u2014 visually distinct from folder rows so it reads\n as a view/section toggle rather than a folder item. -->\n <button\n type=\"button\"\n class=\"cqa-flex cqa-items-center cqa-justify-between cqa-w-full cqa-px-3 cqa-py-1 cqa-text-left cqa-text-[#6D6D74] hover:cqa-text-[#3F43EE] focus:cqa-outline-none focus:cqa-text-[#3F43EE] cqa-flex-shrink-0 cqa-uppercase\"\n (click)=\"toggleFoldersAccordion()\"\n [attr.aria-expanded]=\"foldersAccordionExpanded\"\n [attr.aria-label]=\"foldersAccordionExpanded ? 'Collapse organized folders' : 'Expand organized folders'\"\n >\n <span class=\"cqa-text-[11px] cqa-font-semibold cqa-tracking-wider\">{{ labels.organized }}</span>\n <mat-icon\n style=\"font-size:16px;width:16px;height:16px;transition: transform 200ms ease;\"\n [style.transform]=\"foldersAccordionExpanded ? 'rotate(0deg)' : 'rotate(-90deg)'\"\n >expand_more</mat-icon>\n </button>\n\n <!-- Tree (folders only \u2014 Unorganized sits directly below; tree caps at 60vh and scrolls when overflowing).\n Scroll-based lazy-loading at the root level: nearing the bottom emits\n `rootLoadMoreRequested`, which the host responds to by fetching the\n next page of roots. -->\n <div\n class=\"cqa-grid cqa-min-h-0\"\n style=\"transition: grid-template-rows 300ms cubic-bezier(0.4, 0, 0.2, 1);\"\n [style.grid-template-rows]=\"foldersAccordionExpanded ? '1fr' : '0fr'\"\n >\n <div class=\"cqa-min-h-0\" style=\"overflow: hidden;\">\n <div role=\"tree\" class=\"cqa-min-h-0 cqa-max-h-[40vh] cqa-overflow-y-auto cqa-py-1 cqa-scrollbar-thin cqa-scrollbar-track-transparent cqa-scrollbar-thumb-[#E5E7EB] cqa-scrollbar-thumb-rounded-full cqa-scrollbar-thumb-hover:cqa-bg-[#D1D5DB]\"\n (scroll)=\"onTreeScroll($event)\">\n <!-- Initial-load spinner: only renders when we have no folders yet AND\n no search active (search has its own inline spinner). -->\n <div\n *ngIf=\"rootFoldersLoading && folders.length === 0 && !searchValue?.trim()\"\n class=\"cqa-flex cqa-items-center cqa-justify-center cqa-py-6\"\n aria-live=\"polite\"\n >\n <ng-container *ngTemplateOutlet=\"loadingSpinner; context: { size: 20 }\"></ng-container>\n </div>\n <div\n *ngIf=\"searchValue?.trim() && rows.length === 0 && !folderSearchLoading\"\n class=\"cqa-px-3 cqa-py-2 cqa-text-sm cqa-text-neutral-500 cqa-text-center\"\n >\n {{ labels.noFoldersFound }}\n </div>\n <ng-container *ngFor=\"let row of rows; let i = index; trackBy: trackByRow\">\n <!-- Synthetic loading row (lazy fetch in flight) -->\n <div\n *ngIf=\"row.kind === 'loading'\"\n [style.paddingLeft.px]=\"rowIndent(row.depth)\"\n class=\"cqa-flex cqa-items-center cqa-gap-2 cqa-py-1.5 cqa-text-sm cqa-text-neutral-500\"\n aria-live=\"polite\"\n >\n <ng-container *ngTemplateOutlet=\"loadingSpinner; context: { size: 14 }\"></ng-container>\n <span>Loading\u2026</span>\n </div>\n <!-- Synthetic load-more row (more children available on backend) -->\n <button\n *ngIf=\"row.kind === 'load-more'\"\n type=\"button\"\n [style.paddingLeft.px]=\"rowIndent(row.depth)\"\n class=\"cqa-flex cqa-items-center cqa-w-full cqa-py-1.5 cqa-text-sm cqa-text-indigo-600 hover:cqa-bg-[#D8D9FC] cqa-text-left\"\n (click)=\"onLoadMore(row.node); $event.stopPropagation()\"\n >\n <span>Load more ({{ (row.node.totalChildren ?? 0) - (row.node.children?.length ?? 0) }})</span>\n </button>\n <!-- Real folder row -->\n <div\n *ngIf=\"row.kind === 'folder'\"\n role=\"treeitem\"\n tabindex=\"0\"\n [attr.aria-expanded]=\"row.hasChildren ? isExpanded(row.node.id) : null\"\n [attr.aria-selected]=\"isSelected(row.node.id)\"\n [attr.data-folder-row-id]=\"row.node.id\"\n [cqaFolderDrop]=\"row.node.id\"\n [dropEnabled]=\"allowDrop\"\n (testsDropped)=\"rowDropped(row.node.id, $event)\"\n (folderDropped)=\"onFolderRowDropped(row.node.id, $event)\"\n [cqaFolderDrag]=\"row.node.id\"\n [dragEnabled]=\"allowDrop\"\n (dragstart)=\"onFolderDragStart(row.node)\"\n (dragend)=\"onFolderDragEnd()\"\n (dragover)=\"onFolderRowDragOver(row)\"\n (dragleave)=\"onFolderRowDragLeave(row)\"\n (click)=\"onSelect(row.node)\"\n (contextmenu)=\"openContextMenu(row.node, $event)\"\n (keydown)=\"onRowKeydown($event, row, i)\"\n class=\"cqa-group cqa-flex cqa-items-center cqa-gap-1 cqa-pr-3 cqa-py-1.5 cqa-cursor-pointer hover:cqa-bg-[#D8D9FC] focus:cqa-outline-none focus:cqa-bg-[#D8D9FC] cqa-relative\"\n [style.background-color]=\"(isSelected(row.node.id) || contextMenuFolderId === row.node.id) ? '#D8D9FC' : null\"\n [style.paddingLeft.px]=\"rowIndent(row.depth)\"\n [attr.title]=\"folderRowTooltip(row.node)\"\n >\n <span\n *ngIf=\"isSelected(row.node.id)\"\n class=\"cqa-absolute cqa-left-0 cqa-top-0 cqa-bottom-0 cqa-w-[3px] cqa-bg-indigo-600\"\n ></span>\n <button\n type=\"button\"\n class=\"cqa-p-0\"\n [style.color]=\"isSelected(row.node.id) ? '#3F43EE' : '#4C4C51'\"\n [style.visibility]=\"row.hasChildren ? 'visible' : 'hidden'\"\n (click)=\"onToggle(row.node, $event)\"\n [attr.aria-label]=\"isExpanded(row.node.id) ? 'Collapse' : 'Expand'\"\n >\n <mat-icon style=\"font-size:16px;width:16px;height:16px\">\n {{ isExpanded(row.node.id) ? 'expand_more' : 'chevron_right' }}\n </mat-icon>\n </button>\n <ng-container *ngTemplateOutlet=\"folderIcon; context: { color: row.node.color }\"></ng-container>\n\n <ng-container *ngIf=\"renamingId === row.node.id; else nameTpl\">\n <input\n type=\"text\"\n size=\"1\"\n [attr.data-folder-rename-input]=\"row.node.id\"\n [attr.maxlength]=\"renameMaxLength\"\n class=\"cqa-flex-1 cqa-min-w-0 cqa-w-full cqa-px-2 cqa-py-0.5 cqa-rounded cqa-border cqa-bg-white cqa-text-sm cqa-shadow-sm focus:cqa-outline-none focus:cqa-ring-1\"\n [ngClass]=\"(isRenameDraftValid && !renameLimitFlash)\n ? 'cqa-border-neutral-300 focus:cqa-border-indigo-400 focus:cqa-ring-indigo-200'\n : 'cqa-border-[#EF4444] focus:cqa-border-[#EF4444] focus:cqa-ring-[#FCA5A5]'\"\n [attr.aria-invalid]=\"!isRenameDraftValid || renameLimitFlash\"\n [(ngModel)]=\"renameDraft\"\n (keydown)=\"onRenameKey($event, row.node)\"\n (keypress)=\"$event.stopPropagation()\"\n (keyup)=\"$event.stopPropagation()\"\n (blur)=\"commitRename(row.node)\"\n (click)=\"$event.stopPropagation()\"\n />\n </ng-container>\n <ng-template #nameTpl>\n <span\n class=\"cqa-flex-1 cqa-text-sm cqa-truncate\"\n [style.color]=\"isSelected(row.node.id) ? '#3F43EE' : '#4C4C51'\"\n (dblclick)=\"beginRename(row.node, $event)\"\n >{{ row.node.name }}</span>\n </ng-template>\n\n <!-- Saving indicator: replaces the count while the host's rename/save\n API call is in flight, so the user gets immediate feedback. -->\n <span\n *ngIf=\"isFolderSaving(row.node.id)\"\n class=\"cqa-flex cqa-items-center cqa-ml-1 cqa-flex-shrink-0\"\n aria-live=\"polite\"\n [attr.aria-label]=\"'Saving ' + row.node.name\"\n >\n <ng-container *ngTemplateOutlet=\"loadingSpinner; context: { size: 12 }\"></ng-container>\n </span>\n\n <!-- Count shown at rest; hidden on row hover when delete is allowed -->\n <span\n *ngIf=\"!isFolderSaving(row.node.id) && showCounts && row.node.count != null\"\n class=\"cqa-text-xs cqa-tabular-nums cqa-ml-1\"\n [style.color]=\"isSelected(row.node.id) ? '#3F43EE' : '#4C4C51'\"\n [class.group-hover:cqa-hidden]=\"allowDelete\"\n >{{ row.node.count }}</span>\n\n <button\n *ngIf=\"hasAnyContextAction\"\n type=\"button\"\n class=\"cqa-p-0.5 cqa-rounded hover:cqa-bg-neutral-200\"\n [style.color]=\"isSelected(row.node.id) || contextMenuFolderId === row.node.id ? '#3F43EE' : '#4C4C51'\"\n [class.cqa-hidden]=\"contextMenuFolderId !== row.node.id\"\n [class.group-hover:cqa-inline-flex]=\"true\"\n [class.cqa-inline-flex]=\"contextMenuFolderId === row.node.id\"\n (click)=\"openContextMenu(row.node, $event)\"\n [attr.aria-label]=\"'Open actions for ' + row.node.name\"\n [attr.aria-haspopup]=\"'menu'\"\n [attr.aria-expanded]=\"contextMenuFolderId === row.node.id\"\n >\n <mat-icon style=\"font-size:16px;width:16px;height:16px\">more_horiz</mat-icon>\n </button>\n </div>\n </ng-container>\n <!-- Root-level pagination is now driven by scroll (see `(scroll)` on the\n tree container above). Per-folder children retain the explicit\n \"Load more\" button rendered inside the rows loop above, since\n subtrees aren't independently scrollable. -->\n <!-- Pagination spinner \u2014 visible only when fetching an additional page\n of roots (folders.length > 0 distinguishes from initial-load case). -->\n <div\n *ngIf=\"rootFoldersLoading && folders.length > 0 && !searchValue?.trim()\"\n class=\"cqa-flex cqa-items-center cqa-justify-center cqa-py-2\"\n aria-live=\"polite\"\n >\n <ng-container *ngTemplateOutlet=\"loadingSpinner; context: { size: 14 }\"></ng-container>\n </div>\n </div>\n </div>\n </div>\n\n <!-- Divider between folder tree and pinned Unorganized tab -->\n <div\n aria-hidden=\"true\"\n class=\"cqa-mx-3 cqa-flex-shrink-0\"\n style=\"height: 1px; background-color: #E5E7EB; margin-top: 6px; margin-bottom: 6px;\"\n ></div>\n\n <!-- Unorganized \u2014 pinned below the scrollable folder tree so it stays visible regardless of folder count -->\n <div\n role=\"treeitem\"\n tabindex=\"0\"\n [attr.aria-selected]=\"isSelected(null)\"\n class=\"cqa-flex cqa-items-center cqa-gap-2 cqa-px-3 cqa-py-1.5 cqa-cursor-pointer hover:cqa-bg-[#D8D9FC] focus:cqa-outline-none focus:cqa-bg-[#D8D9FC] cqa-relative cqa-flex-shrink-0\"\n [style.background-color]=\"isSelected(null) ? '#D8D9FC' : null\"\n [cqaFolderDrop]=\"null\"\n [dropEnabled]=\"allowDrop\"\n (testsDropped)=\"rowDropped(null, $event)\"\n (folderDropped)=\"onFolderRowDropped(null, $event)\"\n (click)=\"onSelectUnorganized()\"\n >\n <span\n *ngIf=\"isSelected(null)\"\n class=\"cqa-absolute cqa-left-0 cqa-top-0 cqa-bottom-0 cqa-w-[3px] cqa-bg-indigo-600\"\n ></span>\n <mat-icon [style.color]=\"isSelected(null) ? '#3F43EE' : '#4C4C51'\" style=\"font-size:16px;width:16px;height:16px\">inbox</mat-icon>\n <span class=\"cqa-flex-1 cqa-text-sm\" [style.color]=\"isSelected(null) ? '#3F43EE' : '#4C4C51'\">{{ labels.unorganized }}</span>\n <span *ngIf=\"showCounts\" class=\"cqa-text-xs cqa-tabular-nums\" [style.color]=\"isSelected(null) ? '#3F43EE' : '#4C4C51'\">{{ unorganizedCount }}</span>\n </div>\n\n <!-- Folder context menu (right-click / ellipsis). A 0\u00D70 fixed-position\n anchor at the click coords drives a cdkConnectedOverlay; CDK's\n flexible-connected positioning auto-flips up/left when the menu\n would overflow the viewport (last-folder-near-bottom case). -->\n <div #ctxAnchor cdkOverlayOrigin\n class=\"cqa-fixed cqa-w-0 cqa-h-0 cqa-pointer-events-none\"\n [style.left.px]=\"contextMenuPosition.x\"\n [style.top.px]=\"contextMenuPosition.y\"></div>\n\n <ng-template\n cdkConnectedOverlay\n [cdkConnectedOverlayOrigin]=\"ctxAnchor\"\n [cdkConnectedOverlayOpen]=\"contextMenuFolderId !== null\"\n [cdkConnectedOverlayPositions]=\"contextMenuPositions\"\n [cdkConnectedOverlayHasBackdrop]=\"false\"\n [cdkConnectedOverlayFlexibleDimensions]=\"false\"\n [cdkConnectedOverlayPush]=\"true\"\n (overlayOutsideClick)=\"onOverlayOutsideClick()\"\n (detach)=\"closeContextMenu()\"\n >\n <!-- The lib's tailwind config sets important: '.cqa-ui-root', which\n emits descendant selectors like `.cqa-ui-root .cqa-bg-white`.\n CDK Overlay teleports this template into the global overlay\n container (outside the host's cqa-ui-root), so we wrap the menu in\n an ancestor cqa-ui-root div to re-establish the utility scope. -->\n <div class=\"cqa-ui-root\">\n <div\n role=\"menu\"\n class=\"cqa-min-w-[180px] cqa-bg-white cqa-border cqa-border-neutral-200 cqa-rounded-md cqa-shadow-lg cqa-py-1\"\n (click)=\"$event.stopPropagation()\"\n (contextmenu)=\"$event.preventDefault(); $event.stopPropagation()\"\n >\n <ng-container *ngIf=\"folderById(contextMenuFolderId) as menuNode\">\n <button\n *ngIf=\"allowCreate\"\n type=\"button\"\n role=\"menuitem\"\n class=\"cqa-flex cqa-items-center cqa-gap-2 cqa-w-full cqa-px-3 cqa-py-1.5 cqa-text-sm cqa-text-neutral-800 hover:cqa-bg-indigo-50 cqa-text-left\"\n (click)=\"onContextCreateSubfolder(menuNode)\"\n >\n <mat-icon style=\"font-size:16px;width:16px;height:16px\">create_new_folder</mat-icon>\n <span>{{ labels.folderMenuCreateSubfolder }}</span>\n </button>\n <button\n *ngIf=\"allowRename\"\n type=\"button\"\n role=\"menuitem\"\n class=\"cqa-flex cqa-items-center cqa-gap-2 cqa-w-full cqa-px-3 cqa-py-1.5 cqa-text-sm cqa-text-neutral-800 hover:cqa-bg-indigo-50 cqa-text-left\"\n (click)=\"onContextRename(menuNode)\"\n >\n <mat-icon style=\"font-size:16px;width:16px;height:16px\">edit</mat-icon>\n <span>{{ labels.folderMenuRename }}</span>\n </button>\n <button\n *ngIf=\"allowMove\"\n type=\"button\"\n role=\"menuitem\"\n class=\"cqa-flex cqa-items-center cqa-gap-2 cqa-w-full cqa-px-3 cqa-py-1.5 cqa-text-sm cqa-text-neutral-800 hover:cqa-bg-indigo-50 cqa-text-left\"\n (click)=\"onContextMove(menuNode)\"\n >\n <mat-icon style=\"font-size:16px;width:16px;height:16px\">drive_file_move</mat-icon>\n <span>{{ labels.folderMenuMove }}</span>\n </button>\n <button\n *ngIf=\"allowDuplicate\"\n type=\"button\"\n role=\"menuitem\"\n class=\"cqa-flex cqa-items-center cqa-gap-2 cqa-w-full cqa-px-3 cqa-py-1.5 cqa-text-sm cqa-text-neutral-800 hover:cqa-bg-indigo-50 cqa-text-left\"\n (click)=\"onContextDuplicate(menuNode)\"\n >\n <mat-icon style=\"font-size:16px;width:16px;height:16px\">content_copy</mat-icon>\n <span>{{ labels.folderMenuDuplicate }}</span>\n </button>\n <div *ngIf=\"allowDelete && (allowCreate || allowRename || allowMove || allowDuplicate)\" class=\"cqa-h-px cqa-my-1\" style=\"background-color: #E5E7EB;\"></div>\n <button\n *ngIf=\"allowDelete\"\n type=\"button\"\n role=\"menuitem\"\n class=\"cqa-flex cqa-items-center cqa-gap-2 cqa-w-full cqa-px-3 cqa-py-1.5 cqa-text-sm cqa-text-red-600 hover:cqa-bg-red-50 cqa-text-left\"\n (click)=\"onContextDelete(menuNode)\"\n >\n <mat-icon style=\"font-size:16px;width:16px;height:16px\">delete_outline</mat-icon>\n <span>{{ labels.folderMenuDelete }}</span>\n </button>\n </ng-container>\n </div>\n </div>\n </ng-template>\n\n </ng-container>\n </div>\n</aside>\n", components: [{ type: i1.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { type: SearchBarComponent, selector: "cqa-search-bar", inputs: ["placeholder", "value", "disabled", "showClear", "ariaLabel", "autoFocus", "size", "fullWidth"], outputs: ["valueChange", "search", "cleared"] }], directives: [{ type: i2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { type: i2.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet"] }, { type: i2.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { type: FolderDropDirective, selector: "[cqaFolderDrop]", inputs: ["cqaFolderDrop", "dropEnabled"], outputs: ["testsDropped", "folderDropped"] }, { type: FolderDragDirective, selector: "[cqaFolderDrag]", inputs: ["cqaFolderDrag", "dragEnabled"] }, { type: i1$1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { type: i1$1.MaxLengthValidator, selector: "[maxlength][formControlName],[maxlength][formControl],[maxlength][ngModel]", inputs: ["maxlength"] }, { type: i2.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { type: i1$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { type: i1$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { type: i1$6.CdkOverlayOrigin, selector: "[cdk-overlay-origin], [overlay-origin], [cdkOverlayOrigin]", exportAs: ["cdkOverlayOrigin"] }, { type: i1$6.CdkConnectedOverlay, selector: "[cdk-connected-overlay], [connected-overlay], [cdkConnectedOverlay]", inputs: ["cdkConnectedOverlayOrigin", "cdkConnectedOverlayPositions", "cdkConnectedOverlayPositionStrategy", "cdkConnectedOverlayOffsetX", "cdkConnectedOverlayOffsetY", "cdkConnectedOverlayWidth", "cdkConnectedOverlayHeight", "cdkConnectedOverlayMinWidth", "cdkConnectedOverlayMinHeight", "cdkConnectedOverlayBackdropClass", "cdkConnectedOverlayPanelClass", "cdkConnectedOverlayViewportMargin", "cdkConnectedOverlayScrollStrategy", "cdkConnectedOverlayOpen", "cdkConnectedOverlayDisableClose", "cdkConnectedOverlayTransformOriginOn", "cdkConnectedOverlayHasBackdrop", "cdkConnectedOverlayLockPosition", "cdkConnectedOverlayFlexibleDimensions", "cdkConnectedOverlayGrowAfterOpen", "cdkConnectedOverlayPush"], outputs: ["backdropClick", "positionChange", "attach", "detach", "overlayKeydown", "overlayOutsideClick"], exportAs: ["cdkConnectedOverlay"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
9741
+ FolderSidebarComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version: "13.4.0", type: FolderSidebarComponent, selector: "cqa-folder-sidebar", inputs: { folders: "folders", selectedFolderId: "selectedFolderId", expandedFolderIds: "expandedFolderIds", unorganizedCount: "unorganizedCount", allowCreate: "allowCreate", allowRename: "allowRename", allowDelete: "allowDelete", allowMove: "allowMove", allowDuplicate: "allowDuplicate", allowDrop: "allowDrop", showCounts: "showCounts", collapsed: "collapsed", foldersAccordionExpanded: "foldersAccordionExpanded", labels: "labels", serverSideSearch: "serverSideSearch", rootTotal: "rootTotal", folderSearchLoading: "folderSearchLoading", rootFoldersLoading: "rootFoldersLoading", savingFolderIds: "savingFolderIds", searchValue: "searchValue" }, outputs: { folderSelected: "folderSelected", folderExpansionToggled: "folderExpansionToggled", folderChildrenRequested: "folderChildrenRequested", folderLoadMoreRequested: "folderLoadMoreRequested", searchChange: "searchChange", rootLoadMoreRequested: "rootLoadMoreRequested", folderCreated: "folderCreated", folderCreateRequested: "folderCreateRequested", folderRenamed: "folderRenamed", folderDeleted: "folderDeleted", folderMoveRequested: "folderMoveRequested", folderDuplicateRequested: "folderDuplicateRequested", testsDropped: "testsDropped", folderDropped: "folderDropped", collapsedChange: "collapsedChange", foldersAccordionExpandedChange: "foldersAccordionExpandedChange" }, host: { listeners: { "document:click": "onDocumentClick()", "document:keydown.escape": "onEscape()", "document:dragend": "onDocumentDragEnd()" }, classAttribute: "cqa-ui-root" }, ngImport: i0, template: "<!-- Reusable indeterminate loading spinner. Render via\n <ng-container *ngTemplateOutlet=\"loadingSpinner; context: { size: 14 }\"></ng-container>. -->\n<ng-template #loadingSpinner let-size=\"size\">\n <svg [attr.width]=\"size || 16\" [attr.height]=\"size || 16\" viewBox=\"0 0 50 50\" aria-label=\"loading\" xmlns=\"http://www.w3.org/2000/svg\">\n <circle cx=\"25\" cy=\"25\" r=\"20\" stroke=\"#E5E7EB\" stroke-width=\"6\" fill=\"none\"></circle>\n <path d=\"M45 25a20 20 0 0 0-20-20\" stroke=\"#4F46E5\" stroke-width=\"6\" fill=\"none\" stroke-linecap=\"round\">\n <animateTransform attributeName=\"transform\" type=\"rotate\" from=\"0 25 25\" to=\"360 25 25\" dur=\"0.8s\" repeatCount=\"indefinite\"></animateTransform>\n </path>\n </svg>\n</ng-template>\n\n<!-- Reusable folder icon. Render via <ng-container *ngTemplateOutlet=\"folderIcon; context: { color: node.color }\"></ng-container>. -->\n<ng-template #folderIcon let-color=\"color\">\n <span class=\"cqa-inline-flex cqa-items-center cqa-justify-center cqa-w-4 cqa-h-4 cqa-flex-shrink-0\">\n <svg width=\"13\" height=\"12\" viewBox=\"0 0 13 12\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\">\n <path d=\"M12.1916 9.85824C12.1916 10.1677 12.0687 10.4644 11.8499 10.6832C11.6311 10.902 11.3343 11.0249 11.0249 11.0249H1.69157C1.38215 11.0249 1.0854 10.902 0.866611 10.6832C0.647819 10.4644 0.524902 10.1677 0.524902 9.85824V1.69157C0.524902 1.38215 0.647819 1.0854 0.866611 0.866611C1.0854 0.647819 1.38215 0.524902 1.69157 0.524902H4.60824L5.7749 2.2749H11.0249C11.3343 2.2749 11.6311 2.39782 11.8499 2.61661C12.0687 2.8354 12.1916 3.13215 12.1916 3.44157V9.85824Z\" [attr.stroke]=\"color || '#99999E'\" stroke-width=\"1.05\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/>\n </svg>\n </span>\n</ng-template>\n\n<aside\n class=\"cqa-flex cqa-flex-col cqa-bg-white cqa-h-full cqa-min-h-0\"\n [class.cqa-border]=\"!collapsed\"\n [class.cqa-border-neutral-200]=\"!collapsed\"\n [class.cqa-rounded-lg]=\"!collapsed\"\n [style.width.px]=\"collapsed ? 22 : 240\"\n style=\"overflow: hidden; transition: width 260ms cubic-bezier(0.4, 0, 0.2, 1);\"\n>\n <!-- Collapsed rail: the entire sidebar becomes a single click target so the\n slim strip reads as an obvious \"expand me\" handle. -->\n <button\n *ngIf=\"collapsed\"\n type=\"button\"\n style=\"width: 22px;\"\n class=\"cqa-group cqa-flex cqa-flex-col cqa-items-center cqa-flex-shrink-0 cqa-h-full cqa-py-2 cqa-gap-3 cqa-bg-neutral-50 cqa-border cqa-border-neutral-200 cqa-text-neutral-500 hover:cqa-bg-indigo-50 hover:cqa-border-indigo-200 hover:cqa-text-indigo-600 focus:cqa-outline-none focus:cqa-ring-2 focus:cqa-ring-indigo-200 cqa-cursor-pointer\"\n (click)=\"togglePanel()\"\n [attr.aria-label]=\"'Expand folders panel'\"\n [title]=\"'Expand folders panel'\"\n >\n <mat-icon style=\"font-size:16px;width:16px;height:16px\">chevron_right</mat-icon>\n <span class=\"cqa-inline-flex cqa-items-center cqa-justify-center\" aria-hidden=\"true\">\n <svg width=\"13\" height=\"12\" viewBox=\"0 0 13 12\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n <path d=\"M12.1916 9.85824C12.1916 10.1677 12.0687 10.4644 11.8499 10.6832C11.6311 10.902 11.3343 11.0249 11.0249 11.0249H1.69157C1.38215 11.0249 1.0854 10.902 0.866611 10.6832C0.647819 10.4644 0.524902 10.1677 0.524902 9.85824V1.69157C0.524902 1.38215 0.647819 1.0854 0.866611 0.866611C1.0854 0.647819 1.38215 0.524902 1.69157 0.524902H4.60824L5.7749 2.2749H11.0249C11.3343 2.2749 11.6311 2.39782 11.8499 2.61661C12.0687 2.8354 12.1916 3.13215 12.1916 3.44157V9.85824Z\" stroke=\"currentColor\" stroke-width=\"1.05\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/>\n </svg>\n </span>\n </button>\n\n <div *ngIf=\"!collapsed\" class=\"cqa-flex cqa-flex-col cqa-h-full cqa-min-h-0 cqa-pb-2\" style=\"width: 240px;\">\n <!-- Header (expanded) -->\n <div\n class=\"cqa-flex cqa-items-center cqa-justify-between cqa-py-1 cqa-px-3\"\n style=\"border-bottom: 1px solid #E2E2E3; margin-bottom: 8px;\"\n >\n <span class=\"cqa-text-sm cqa-font-semibold cqa-text-neutral-900 cqa-py-0.5\">{{ labels.folders }}</span>\n <div class=\"cqa-flex cqa-items-center cqa-gap-1\">\n <button\n type=\"button\"\n class=\"cqa-p-1 cqa-rounded hover:cqa-bg-neutral-100 cqa-text-neutral-500\"\n (click)=\"togglePanel()\"\n [attr.aria-label]=\"'Collapse folders panel'\"\n [title]=\"'Collapse folders panel'\"\n >\n <mat-icon style=\"font-size:18px;width:18px;height:18px\">keyboard_double_arrow_left</mat-icon>\n </button>\n <button\n type=\"button\"\n class=\"cqa-p-1 cqa-rounded hover:cqa-bg-neutral-100\"\n [class.cqa-text-neutral-500]=\"!searchVisible\"\n [class.cqa-text-indigo-600]=\"searchVisible\"\n [class.cqa-bg-neutral-100]=\"searchVisible\"\n (click)=\"toggleSearchVisible()\"\n [attr.aria-label]=\"searchVisible ? 'Hide folder search' : 'Search folders'\"\n [attr.aria-pressed]=\"searchVisible\"\n [title]=\"searchVisible ? 'Hide folder search' : 'Search folders'\"\n >\n <mat-icon style=\"font-size:18px;width:18px;height:18px\">search</mat-icon>\n </button>\n <button\n *ngIf=\"allowCreate\"\n type=\"button\"\n class=\"cqa-p-1 cqa-rounded hover:cqa-bg-neutral-100 cqa-text-neutral-500\"\n (click)=\"requestCreate(null)\"\n [attr.aria-label]=\"labels.newFolder\"\n [title]=\"labels.newFolder\"\n >\n <mat-icon style=\"font-size:18px;width:18px;height:18px\">add</mat-icon>\n </button>\n </div>\n </div>\n\n <ng-container>\n <!-- Search (toggled via the search icon in the header) -->\n <div\n class=\"cqa-grid\"\n style=\"transition: grid-template-rows 300ms cubic-bezier(0.4, 0, 0.2, 1);\"\n [style.grid-template-rows]=\"searchVisible ? '1fr' : '0fr'\"\n >\n <div class=\"cqa-min-h-0\" style=\"overflow: hidden;\">\n <div class=\"cqa-px-3 cqa-pb-2\">\n <div class=\"cqa-relative\">\n <cqa-search-bar\n size=\"sm\"\n [fullWidth]=\"true\"\n [value]=\"searchValue\"\n [autoFocus]=\"searchVisible\"\n [placeholder]=\"labels.searchFoldersPlaceholder\"\n [showClear]=\"true\"\n (valueChange)=\"onSearchInput($event)\"\n (cleared)=\"onSearchClear()\"\n ></cqa-search-bar>\n <span\n *ngIf=\"folderSearchLoading\"\n class=\"cqa-absolute cqa-right-8 cqa-inset-y-0 cqa-flex cqa-items-center\"\n aria-live=\"polite\"\n aria-label=\"Searching folders\"\n >\n <ng-container *ngTemplateOutlet=\"loadingSpinner; context: { size: 14 }\"></ng-container>\n </span>\n </div>\n </div>\n </div>\n </div>\n\n <!-- Organized section header \u2014 visually distinct from folder rows so it reads\n as a view/section toggle rather than a folder item. -->\n <button\n type=\"button\"\n class=\"cqa-flex cqa-items-center cqa-justify-between cqa-w-full cqa-px-3 cqa-py-1 cqa-text-left cqa-text-[#6D6D74] hover:cqa-text-[#3F43EE] focus:cqa-outline-none focus:cqa-text-[#3F43EE] cqa-flex-shrink-0 cqa-uppercase\"\n (click)=\"toggleFoldersAccordion()\"\n [attr.aria-expanded]=\"foldersAccordionExpanded\"\n [attr.aria-label]=\"foldersAccordionExpanded ? 'Collapse organized folders' : 'Expand organized folders'\"\n >\n <span class=\"cqa-text-[11px] cqa-font-semibold cqa-tracking-wider\">{{ labels.organized }}</span>\n <mat-icon\n style=\"font-size:16px;width:16px;height:16px;transition: transform 200ms ease;\"\n [style.transform]=\"foldersAccordionExpanded ? 'rotate(0deg)' : 'rotate(-90deg)'\"\n >expand_more</mat-icon>\n </button>\n\n <!-- Tree (folders only \u2014 Unorganized sits directly below; tree caps at 60vh and scrolls when overflowing).\n Scroll-based lazy-loading at the root level: nearing the bottom emits\n `rootLoadMoreRequested`, which the host responds to by fetching the\n next page of roots. -->\n <div\n class=\"cqa-grid cqa-min-h-0\"\n style=\"transition: grid-template-rows 300ms cubic-bezier(0.4, 0, 0.2, 1);\"\n [style.grid-template-rows]=\"foldersAccordionExpanded ? '1fr' : '0fr'\"\n >\n <div class=\"cqa-min-h-0\" style=\"overflow: hidden;\">\n <div role=\"tree\" class=\"cqa-min-h-0 cqa-max-h-[40vh] cqa-overflow-y-auto cqa-py-1 cqa-scrollbar-thin cqa-scrollbar-track-transparent cqa-scrollbar-thumb-[#E5E7EB] cqa-scrollbar-thumb-rounded-full cqa-scrollbar-thumb-hover:cqa-bg-[#D1D5DB]\"\n (scroll)=\"onTreeScroll($event)\">\n <!-- Initial-load spinner: only renders when we have no folders yet AND\n no search active (search has its own inline spinner). -->\n <div\n *ngIf=\"rootFoldersLoading && folders.length === 0 && !searchValue?.trim()\"\n class=\"cqa-flex cqa-items-center cqa-justify-center cqa-py-6\"\n aria-live=\"polite\"\n >\n <ng-container *ngTemplateOutlet=\"loadingSpinner; context: { size: 20 }\"></ng-container>\n </div>\n <div\n *ngIf=\"searchValue?.trim() && rows.length === 0 && !folderSearchLoading\"\n class=\"cqa-px-3 cqa-py-2 cqa-text-sm cqa-text-neutral-500 cqa-text-center\"\n >\n {{ labels.noFoldersFound }}\n </div>\n <div\n *ngIf=\"!rootFoldersLoading && folders.length === 0 && !searchValue?.trim()\"\n class=\"cqa-flex cqa-flex-col cqa-items-center cqa-justify-center cqa-text-center cqa-px-4 cqa-py-6 cqa-gap-2\"\n >\n <span class=\"cqa-inline-flex cqa-items-center cqa-justify-center cqa-w-10 cqa-h-10 cqa-rounded-full cqa-bg-neutral-100 cqa-text-neutral-400\">\n <mat-icon style=\"font-size:22px;width:22px;height:22px\">folder_open</mat-icon>\n </span>\n <span class=\"cqa-text-sm cqa-font-medium cqa-text-neutral-700\">{{ labels.noFoldersFound }}</span>\n <span *ngIf=\"allowCreate\" class=\"cqa-text-xs cqa-text-neutral-500 cqa-leading-snug\">\n {{ labels.noFoldersEmptyDescription }}\n </span>\n </div>\n <ng-container *ngFor=\"let row of rows; let i = index; trackBy: trackByRow\">\n <!-- Synthetic loading row (lazy fetch in flight) -->\n <div\n *ngIf=\"row.kind === 'loading'\"\n [style.paddingLeft.px]=\"rowIndent(row.depth)\"\n class=\"cqa-flex cqa-items-center cqa-gap-2 cqa-py-1.5 cqa-text-sm cqa-text-neutral-500\"\n aria-live=\"polite\"\n >\n <ng-container *ngTemplateOutlet=\"loadingSpinner; context: { size: 14 }\"></ng-container>\n <span>Loading\u2026</span>\n </div>\n <!-- Synthetic load-more row (more children available on backend) -->\n <button\n *ngIf=\"row.kind === 'load-more'\"\n type=\"button\"\n [style.paddingLeft.px]=\"rowIndent(row.depth)\"\n class=\"cqa-flex cqa-items-center cqa-w-full cqa-py-1.5 cqa-text-sm cqa-text-indigo-600 hover:cqa-bg-[#D8D9FC] cqa-text-left\"\n (click)=\"onLoadMore(row.node); $event.stopPropagation()\"\n >\n <span>Load more ({{ (row.node.totalChildren ?? 0) - (row.node.children?.length ?? 0) }})</span>\n </button>\n <!-- Real folder row -->\n <div\n *ngIf=\"row.kind === 'folder'\"\n role=\"treeitem\"\n tabindex=\"0\"\n [attr.aria-expanded]=\"row.hasChildren ? isExpanded(row.node.id) : null\"\n [attr.aria-selected]=\"isSelected(row.node.id)\"\n [attr.data-folder-row-id]=\"row.node.id\"\n [cqaFolderDrop]=\"row.node.id\"\n [dropEnabled]=\"allowDrop\"\n (testsDropped)=\"rowDropped(row.node.id, $event)\"\n (folderDropped)=\"onFolderRowDropped(row.node.id, $event)\"\n [cqaFolderDrag]=\"row.node.id\"\n [dragEnabled]=\"allowDrop\"\n (dragstart)=\"onFolderDragStart(row.node)\"\n (dragend)=\"onFolderDragEnd()\"\n (dragover)=\"onFolderRowDragOver(row)\"\n (dragleave)=\"onFolderRowDragLeave(row)\"\n (click)=\"onSelect(row.node)\"\n (contextmenu)=\"openContextMenu(row.node, $event)\"\n (keydown)=\"onRowKeydown($event, row, i)\"\n class=\"cqa-group cqa-flex cqa-items-center cqa-gap-1 cqa-pr-3 cqa-py-1.5 cqa-cursor-pointer hover:cqa-bg-[#D8D9FC] focus:cqa-outline-none focus:cqa-bg-[#D8D9FC] cqa-relative\"\n [style.background-color]=\"renamingId === row.node.id ? '#FFFFFF' : ((isSelected(row.node.id) || contextMenuFolderId === row.node.id) ? '#D8D9FC' : null)\"\n [style.paddingLeft.px]=\"rowIndent(row.depth)\"\n [attr.title]=\"folderRowTooltip(row.node)\"\n >\n <span\n *ngIf=\"isSelected(row.node.id)\"\n class=\"cqa-absolute cqa-left-0 cqa-top-0 cqa-bottom-0 cqa-w-[3px] cqa-bg-indigo-600\"\n ></span>\n <button\n type=\"button\"\n class=\"cqa-p-0\"\n [style.color]=\"isSelected(row.node.id) ? '#3F43EE' : '#4C4C51'\"\n [style.visibility]=\"row.hasChildren ? 'visible' : 'hidden'\"\n (click)=\"onToggle(row.node, $event)\"\n [attr.aria-label]=\"isExpanded(row.node.id) ? 'Collapse' : 'Expand'\"\n >\n <mat-icon style=\"font-size:16px;width:16px;height:16px\">\n {{ isExpanded(row.node.id) ? 'expand_more' : 'chevron_right' }}\n </mat-icon>\n </button>\n <ng-container *ngTemplateOutlet=\"folderIcon; context: { color: row.node.color }\"></ng-container>\n\n <ng-container *ngIf=\"renamingId === row.node.id; else nameTpl\">\n <input\n type=\"text\"\n size=\"1\"\n [attr.data-folder-rename-input]=\"row.node.id\"\n [attr.maxlength]=\"renameMaxLength\"\n class=\"cqa-flex-1 cqa-min-w-0 cqa-w-full cqa-px-2 cqa-py-0.5 cqa-rounded cqa-border cqa-bg-white cqa-text-sm cqa-shadow-sm focus:cqa-outline-none focus:cqa-ring-1\"\n [ngClass]=\"(isRenameDraftValid && !renameLimitFlash)\n ? 'cqa-border-neutral-300 focus:cqa-border-indigo-400 focus:cqa-ring-indigo-200'\n : 'cqa-border-[#EF4444] focus:cqa-border-[#EF4444] focus:cqa-ring-[#FCA5A5]'\"\n [attr.aria-invalid]=\"!isRenameDraftValid || renameLimitFlash\"\n [(ngModel)]=\"renameDraft\"\n (keydown)=\"onRenameKey($event, row.node)\"\n (keypress)=\"$event.stopPropagation()\"\n (keyup)=\"$event.stopPropagation()\"\n (blur)=\"commitRename(row.node)\"\n (click)=\"$event.stopPropagation()\"\n />\n </ng-container>\n <ng-template #nameTpl>\n <span\n class=\"cqa-flex-1 cqa-text-sm cqa-truncate\"\n [style.color]=\"isSelected(row.node.id) ? '#3F43EE' : '#4C4C51'\"\n (dblclick)=\"beginRename(row.node, $event)\"\n >{{ row.node.name }}</span>\n </ng-template>\n\n <!-- Saving indicator: replaces the count while the host's rename/save\n API call is in flight, so the user gets immediate feedback. -->\n <span\n *ngIf=\"isFolderSaving(row.node.id)\"\n class=\"cqa-flex cqa-items-center cqa-ml-1 cqa-flex-shrink-0\"\n aria-live=\"polite\"\n [attr.aria-label]=\"'Saving ' + row.node.name\"\n >\n <ng-container *ngTemplateOutlet=\"loadingSpinner; context: { size: 12 }\"></ng-container>\n </span>\n\n <!-- Count shown at rest; hidden on row hover when delete is allowed -->\n <span\n *ngIf=\"!isFolderSaving(row.node.id) && showCounts && row.node.count != null\"\n class=\"cqa-text-xs cqa-tabular-nums cqa-ml-1\"\n [style.color]=\"isSelected(row.node.id) ? '#3F43EE' : '#4C4C51'\"\n [class.group-hover:cqa-hidden]=\"allowDelete\"\n >{{ row.node.count }}</span>\n\n <button\n *ngIf=\"hasAnyContextAction\"\n type=\"button\"\n class=\"cqa-p-0.5 cqa-rounded hover:cqa-bg-neutral-200\"\n [style.color]=\"isSelected(row.node.id) || contextMenuFolderId === row.node.id ? '#3F43EE' : '#4C4C51'\"\n [class.cqa-hidden]=\"contextMenuFolderId !== row.node.id\"\n [class.group-hover:cqa-inline-flex]=\"true\"\n [class.cqa-inline-flex]=\"contextMenuFolderId === row.node.id\"\n (click)=\"openContextMenu(row.node, $event)\"\n [attr.aria-label]=\"'Open actions for ' + row.node.name\"\n [attr.aria-haspopup]=\"'menu'\"\n [attr.aria-expanded]=\"contextMenuFolderId === row.node.id\"\n >\n <mat-icon style=\"font-size:16px;width:16px;height:16px\">more_horiz</mat-icon>\n </button>\n </div>\n </ng-container>\n <!-- Root-level pagination is now driven by scroll (see `(scroll)` on the\n tree container above). Per-folder children retain the explicit\n \"Load more\" button rendered inside the rows loop above, since\n subtrees aren't independently scrollable. -->\n <!-- Pagination spinner \u2014 visible only when fetching an additional page\n of roots (folders.length > 0 distinguishes from initial-load case). -->\n <div\n *ngIf=\"rootFoldersLoading && folders.length > 0 && !searchValue?.trim()\"\n class=\"cqa-flex cqa-items-center cqa-justify-center cqa-py-2\"\n aria-live=\"polite\"\n >\n <ng-container *ngTemplateOutlet=\"loadingSpinner; context: { size: 14 }\"></ng-container>\n </div>\n </div>\n </div>\n </div>\n\n <!-- Divider between folder tree and pinned Unorganized tab -->\n <div\n aria-hidden=\"true\"\n class=\"cqa-mx-3 cqa-flex-shrink-0\"\n style=\"height: 1px; background-color: #E5E7EB; margin-top: 6px; margin-bottom: 6px;\"\n ></div>\n\n <!-- Unorganized \u2014 pinned below the scrollable folder tree so it stays visible regardless of folder count -->\n <div\n role=\"treeitem\"\n tabindex=\"0\"\n [attr.aria-selected]=\"isSelected(null)\"\n class=\"cqa-flex cqa-items-center cqa-gap-2 cqa-px-3 cqa-py-1.5 cqa-cursor-pointer hover:cqa-bg-[#D8D9FC] focus:cqa-outline-none focus:cqa-bg-[#D8D9FC] cqa-relative cqa-flex-shrink-0\"\n [style.background-color]=\"isSelected(null) ? '#D8D9FC' : null\"\n [cqaFolderDrop]=\"null\"\n [dropEnabled]=\"allowDrop\"\n (testsDropped)=\"rowDropped(null, $event)\"\n (folderDropped)=\"onFolderRowDropped(null, $event)\"\n (click)=\"onSelectUnorganized()\"\n >\n <span\n *ngIf=\"isSelected(null)\"\n class=\"cqa-absolute cqa-left-0 cqa-top-0 cqa-bottom-0 cqa-w-[3px] cqa-bg-indigo-600\"\n ></span>\n <mat-icon [style.color]=\"isSelected(null) ? '#3F43EE' : '#4C4C51'\" style=\"font-size:16px;width:16px;height:16px\">inbox</mat-icon>\n <span class=\"cqa-flex-1 cqa-text-sm\" [style.color]=\"isSelected(null) ? '#3F43EE' : '#4C4C51'\">{{ labels.unorganized }}</span>\n <span *ngIf=\"showCounts\" class=\"cqa-text-xs cqa-tabular-nums\" [style.color]=\"isSelected(null) ? '#3F43EE' : '#4C4C51'\">{{ unorganizedCount }}</span>\n </div>\n\n <!-- Folder context menu (right-click / ellipsis). A 0\u00D70 fixed-position\n anchor at the click coords drives a cdkConnectedOverlay; CDK's\n flexible-connected positioning auto-flips up/left when the menu\n would overflow the viewport (last-folder-near-bottom case). -->\n <div #ctxAnchor cdkOverlayOrigin\n class=\"cqa-fixed cqa-w-0 cqa-h-0 cqa-pointer-events-none\"\n [style.left.px]=\"contextMenuPosition.x\"\n [style.top.px]=\"contextMenuPosition.y\"></div>\n\n <ng-template\n cdkConnectedOverlay\n [cdkConnectedOverlayOrigin]=\"ctxAnchor\"\n [cdkConnectedOverlayOpen]=\"contextMenuFolderId !== null\"\n [cdkConnectedOverlayPositions]=\"contextMenuPositions\"\n [cdkConnectedOverlayHasBackdrop]=\"false\"\n [cdkConnectedOverlayFlexibleDimensions]=\"false\"\n [cdkConnectedOverlayPush]=\"true\"\n (overlayOutsideClick)=\"onOverlayOutsideClick()\"\n (detach)=\"closeContextMenu()\"\n >\n <!-- The lib's tailwind config sets important: '.cqa-ui-root', which\n emits descendant selectors like `.cqa-ui-root .cqa-bg-white`.\n CDK Overlay teleports this template into the global overlay\n container (outside the host's cqa-ui-root), so we wrap the menu in\n an ancestor cqa-ui-root div to re-establish the utility scope. -->\n <div class=\"cqa-ui-root\">\n <div\n role=\"menu\"\n class=\"cqa-min-w-[180px] cqa-bg-white cqa-border cqa-border-neutral-200 cqa-rounded-md cqa-shadow-lg cqa-py-1\"\n (click)=\"$event.stopPropagation()\"\n (contextmenu)=\"$event.preventDefault(); $event.stopPropagation()\"\n >\n <ng-container *ngIf=\"folderById(contextMenuFolderId) as menuNode\">\n <button\n *ngIf=\"allowCreate\"\n type=\"button\"\n role=\"menuitem\"\n class=\"cqa-flex cqa-items-center cqa-gap-2 cqa-w-full cqa-px-3 cqa-py-1.5 cqa-text-sm cqa-text-neutral-800 hover:cqa-bg-indigo-50 cqa-text-left\"\n (click)=\"onContextCreateSubfolder(menuNode)\"\n >\n <mat-icon style=\"font-size:16px;width:16px;height:16px\">create_new_folder</mat-icon>\n <span>{{ labels.folderMenuCreateSubfolder }}</span>\n </button>\n <button\n *ngIf=\"allowRename\"\n type=\"button\"\n role=\"menuitem\"\n class=\"cqa-flex cqa-items-center cqa-gap-2 cqa-w-full cqa-px-3 cqa-py-1.5 cqa-text-sm cqa-text-neutral-800 hover:cqa-bg-indigo-50 cqa-text-left\"\n (click)=\"onContextRename(menuNode)\"\n >\n <mat-icon style=\"font-size:16px;width:16px;height:16px\">edit</mat-icon>\n <span>{{ labels.folderMenuRename }}</span>\n </button>\n <button\n *ngIf=\"allowMove\"\n type=\"button\"\n role=\"menuitem\"\n class=\"cqa-flex cqa-items-center cqa-gap-2 cqa-w-full cqa-px-3 cqa-py-1.5 cqa-text-sm cqa-text-neutral-800 hover:cqa-bg-indigo-50 cqa-text-left\"\n (click)=\"onContextMove(menuNode)\"\n >\n <mat-icon style=\"font-size:16px;width:16px;height:16px\">drive_file_move</mat-icon>\n <span>{{ labels.folderMenuMove }}</span>\n </button>\n <button\n *ngIf=\"allowDuplicate\"\n type=\"button\"\n role=\"menuitem\"\n class=\"cqa-flex cqa-items-center cqa-gap-2 cqa-w-full cqa-px-3 cqa-py-1.5 cqa-text-sm cqa-text-neutral-800 hover:cqa-bg-indigo-50 cqa-text-left\"\n (click)=\"onContextDuplicate(menuNode)\"\n >\n <mat-icon style=\"font-size:16px;width:16px;height:16px\">content_copy</mat-icon>\n <span>{{ labels.folderMenuDuplicate }}</span>\n </button>\n <div *ngIf=\"allowDelete && (allowCreate || allowRename || allowMove || allowDuplicate)\" class=\"cqa-h-px cqa-my-1\" style=\"background-color: #E5E7EB;\"></div>\n <button\n *ngIf=\"allowDelete\"\n type=\"button\"\n role=\"menuitem\"\n class=\"cqa-flex cqa-items-center cqa-gap-2 cqa-w-full cqa-px-3 cqa-py-1.5 cqa-text-sm cqa-text-red-600 hover:cqa-bg-red-50 cqa-text-left\"\n (click)=\"onContextDelete(menuNode)\"\n >\n <mat-icon style=\"font-size:16px;width:16px;height:16px\">delete_outline</mat-icon>\n <span>{{ labels.folderMenuDelete }}</span>\n </button>\n </ng-container>\n </div>\n </div>\n </ng-template>\n\n </ng-container>\n </div>\n</aside>\n", components: [{ type: i1.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { type: SearchBarComponent, selector: "cqa-search-bar", inputs: ["placeholder", "value", "disabled", "showClear", "ariaLabel", "autoFocus", "size", "fullWidth"], outputs: ["valueChange", "search", "cleared"] }], directives: [{ type: i2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { type: i2.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet"] }, { type: i2.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { type: FolderDropDirective, selector: "[cqaFolderDrop]", inputs: ["cqaFolderDrop", "dropEnabled"], outputs: ["testsDropped", "folderDropped"] }, { type: FolderDragDirective, selector: "[cqaFolderDrag]", inputs: ["cqaFolderDrag", "dragEnabled"] }, { type: i1$1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { type: i1$1.MaxLengthValidator, selector: "[maxlength][formControlName],[maxlength][formControl],[maxlength][ngModel]", inputs: ["maxlength"] }, { type: i2.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { type: i1$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { type: i1$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { type: i1$6.CdkOverlayOrigin, selector: "[cdk-overlay-origin], [overlay-origin], [cdkOverlayOrigin]", exportAs: ["cdkOverlayOrigin"] }, { type: i1$6.CdkConnectedOverlay, selector: "[cdk-connected-overlay], [connected-overlay], [cdkConnectedOverlay]", inputs: ["cdkConnectedOverlayOrigin", "cdkConnectedOverlayPositions", "cdkConnectedOverlayPositionStrategy", "cdkConnectedOverlayOffsetX", "cdkConnectedOverlayOffsetY", "cdkConnectedOverlayWidth", "cdkConnectedOverlayHeight", "cdkConnectedOverlayMinWidth", "cdkConnectedOverlayMinHeight", "cdkConnectedOverlayBackdropClass", "cdkConnectedOverlayPanelClass", "cdkConnectedOverlayViewportMargin", "cdkConnectedOverlayScrollStrategy", "cdkConnectedOverlayOpen", "cdkConnectedOverlayDisableClose", "cdkConnectedOverlayTransformOriginOn", "cdkConnectedOverlayHasBackdrop", "cdkConnectedOverlayLockPosition", "cdkConnectedOverlayFlexibleDimensions", "cdkConnectedOverlayGrowAfterOpen", "cdkConnectedOverlayPush"], outputs: ["backdropClick", "positionChange", "attach", "detach", "overlayKeydown", "overlayOutsideClick"], exportAs: ["cdkConnectedOverlay"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
9732
9742
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.4.0", ngImport: i0, type: FolderSidebarComponent, decorators: [{
9733
9743
  type: Component,
9734
- args: [{ selector: 'cqa-folder-sidebar', host: { class: 'cqa-ui-root' }, changeDetection: ChangeDetectionStrategy.OnPush, template: "<!-- Reusable indeterminate loading spinner. Render via\n <ng-container *ngTemplateOutlet=\"loadingSpinner; context: { size: 14 }\"></ng-container>. -->\n<ng-template #loadingSpinner let-size=\"size\">\n <svg [attr.width]=\"size || 16\" [attr.height]=\"size || 16\" viewBox=\"0 0 50 50\" aria-label=\"loading\" xmlns=\"http://www.w3.org/2000/svg\">\n <circle cx=\"25\" cy=\"25\" r=\"20\" stroke=\"#E5E7EB\" stroke-width=\"6\" fill=\"none\"></circle>\n <path d=\"M45 25a20 20 0 0 0-20-20\" stroke=\"#4F46E5\" stroke-width=\"6\" fill=\"none\" stroke-linecap=\"round\">\n <animateTransform attributeName=\"transform\" type=\"rotate\" from=\"0 25 25\" to=\"360 25 25\" dur=\"0.8s\" repeatCount=\"indefinite\"></animateTransform>\n </path>\n </svg>\n</ng-template>\n\n<!-- Reusable folder icon. Render via <ng-container *ngTemplateOutlet=\"folderIcon; context: { color: node.color }\"></ng-container>. -->\n<ng-template #folderIcon let-color=\"color\">\n <span class=\"cqa-inline-flex cqa-items-center cqa-justify-center cqa-w-4 cqa-h-4 cqa-flex-shrink-0\">\n <svg width=\"13\" height=\"12\" viewBox=\"0 0 13 12\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\">\n <path d=\"M12.1916 9.85824C12.1916 10.1677 12.0687 10.4644 11.8499 10.6832C11.6311 10.902 11.3343 11.0249 11.0249 11.0249H1.69157C1.38215 11.0249 1.0854 10.902 0.866611 10.6832C0.647819 10.4644 0.524902 10.1677 0.524902 9.85824V1.69157C0.524902 1.38215 0.647819 1.0854 0.866611 0.866611C1.0854 0.647819 1.38215 0.524902 1.69157 0.524902H4.60824L5.7749 2.2749H11.0249C11.3343 2.2749 11.6311 2.39782 11.8499 2.61661C12.0687 2.8354 12.1916 3.13215 12.1916 3.44157V9.85824Z\" [attr.stroke]=\"color || '#99999E'\" stroke-width=\"1.05\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/>\n </svg>\n </span>\n</ng-template>\n\n<aside\n class=\"cqa-flex cqa-flex-col cqa-bg-white cqa-h-full cqa-min-h-0\"\n [class.cqa-border]=\"!collapsed\"\n [class.cqa-border-neutral-200]=\"!collapsed\"\n [class.cqa-rounded-lg]=\"!collapsed\"\n [style.width.px]=\"collapsed ? 22 : 240\"\n style=\"overflow: hidden; transition: width 260ms cubic-bezier(0.4, 0, 0.2, 1);\"\n>\n <!-- Collapsed rail: the entire sidebar becomes a single click target so the\n slim strip reads as an obvious \"expand me\" handle. -->\n <button\n *ngIf=\"collapsed\"\n type=\"button\"\n style=\"width: 22px;\"\n class=\"cqa-group cqa-flex cqa-flex-col cqa-items-center cqa-flex-shrink-0 cqa-h-full cqa-py-2 cqa-gap-3 cqa-bg-neutral-50 cqa-border cqa-border-neutral-200 cqa-text-neutral-500 hover:cqa-bg-indigo-50 hover:cqa-border-indigo-200 hover:cqa-text-indigo-600 focus:cqa-outline-none focus:cqa-ring-2 focus:cqa-ring-indigo-200 cqa-cursor-pointer\"\n (click)=\"togglePanel()\"\n [attr.aria-label]=\"'Expand folders panel'\"\n [title]=\"'Expand folders panel'\"\n >\n <mat-icon style=\"font-size:16px;width:16px;height:16px\">chevron_right</mat-icon>\n <span class=\"cqa-inline-flex cqa-items-center cqa-justify-center\" aria-hidden=\"true\">\n <svg width=\"13\" height=\"12\" viewBox=\"0 0 13 12\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n <path d=\"M12.1916 9.85824C12.1916 10.1677 12.0687 10.4644 11.8499 10.6832C11.6311 10.902 11.3343 11.0249 11.0249 11.0249H1.69157C1.38215 11.0249 1.0854 10.902 0.866611 10.6832C0.647819 10.4644 0.524902 10.1677 0.524902 9.85824V1.69157C0.524902 1.38215 0.647819 1.0854 0.866611 0.866611C1.0854 0.647819 1.38215 0.524902 1.69157 0.524902H4.60824L5.7749 2.2749H11.0249C11.3343 2.2749 11.6311 2.39782 11.8499 2.61661C12.0687 2.8354 12.1916 3.13215 12.1916 3.44157V9.85824Z\" stroke=\"currentColor\" stroke-width=\"1.05\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/>\n </svg>\n </span>\n </button>\n\n <div *ngIf=\"!collapsed\" class=\"cqa-flex cqa-flex-col cqa-h-full cqa-min-h-0 cqa-pb-2\" style=\"width: 240px;\">\n <!-- Header (expanded) -->\n <div\n class=\"cqa-flex cqa-items-center cqa-justify-between cqa-py-1 cqa-px-3\"\n style=\"border-bottom: 1px solid #E2E2E3; margin-bottom: 8px;\"\n >\n <span class=\"cqa-text-sm cqa-font-semibold cqa-text-neutral-900 cqa-py-0.5\">{{ labels.folders }}</span>\n <div class=\"cqa-flex cqa-items-center cqa-gap-1\">\n <button\n type=\"button\"\n class=\"cqa-p-1 cqa-rounded hover:cqa-bg-neutral-100 cqa-text-neutral-500\"\n (click)=\"togglePanel()\"\n [attr.aria-label]=\"'Collapse folders panel'\"\n [title]=\"'Collapse folders panel'\"\n >\n <mat-icon style=\"font-size:18px;width:18px;height:18px\">keyboard_double_arrow_left</mat-icon>\n </button>\n <button\n type=\"button\"\n class=\"cqa-p-1 cqa-rounded hover:cqa-bg-neutral-100\"\n [class.cqa-text-neutral-500]=\"!searchVisible\"\n [class.cqa-text-indigo-600]=\"searchVisible\"\n [class.cqa-bg-neutral-100]=\"searchVisible\"\n (click)=\"toggleSearchVisible()\"\n [attr.aria-label]=\"searchVisible ? 'Hide folder search' : 'Search folders'\"\n [attr.aria-pressed]=\"searchVisible\"\n [title]=\"searchVisible ? 'Hide folder search' : 'Search folders'\"\n >\n <mat-icon style=\"font-size:18px;width:18px;height:18px\">search</mat-icon>\n </button>\n <button\n *ngIf=\"allowCreate\"\n type=\"button\"\n class=\"cqa-p-1 cqa-rounded hover:cqa-bg-neutral-100 cqa-text-neutral-500\"\n (click)=\"requestCreate(null)\"\n [attr.aria-label]=\"labels.newFolder\"\n [title]=\"labels.newFolder\"\n >\n <mat-icon style=\"font-size:18px;width:18px;height:18px\">add</mat-icon>\n </button>\n </div>\n </div>\n\n <ng-container>\n <!-- Search (toggled via the search icon in the header) -->\n <div\n class=\"cqa-grid\"\n style=\"transition: grid-template-rows 300ms cubic-bezier(0.4, 0, 0.2, 1);\"\n [style.grid-template-rows]=\"searchVisible ? '1fr' : '0fr'\"\n >\n <div class=\"cqa-min-h-0\" style=\"overflow: hidden;\">\n <div class=\"cqa-px-3 cqa-pb-2\">\n <div class=\"cqa-relative\">\n <cqa-search-bar\n size=\"sm\"\n [fullWidth]=\"true\"\n [value]=\"searchValue\"\n [autoFocus]=\"searchVisible\"\n [placeholder]=\"labels.searchFoldersPlaceholder\"\n [showClear]=\"true\"\n (valueChange)=\"onSearchInput($event)\"\n (cleared)=\"onSearchClear()\"\n ></cqa-search-bar>\n <span\n *ngIf=\"folderSearchLoading\"\n class=\"cqa-absolute cqa-right-8 cqa-inset-y-0 cqa-flex cqa-items-center\"\n aria-live=\"polite\"\n aria-label=\"Searching folders\"\n >\n <ng-container *ngTemplateOutlet=\"loadingSpinner; context: { size: 14 }\"></ng-container>\n </span>\n </div>\n </div>\n </div>\n </div>\n\n <!-- Organized section header \u2014 visually distinct from folder rows so it reads\n as a view/section toggle rather than a folder item. -->\n <button\n type=\"button\"\n class=\"cqa-flex cqa-items-center cqa-justify-between cqa-w-full cqa-px-3 cqa-py-1 cqa-text-left cqa-text-[#6D6D74] hover:cqa-text-[#3F43EE] focus:cqa-outline-none focus:cqa-text-[#3F43EE] cqa-flex-shrink-0 cqa-uppercase\"\n (click)=\"toggleFoldersAccordion()\"\n [attr.aria-expanded]=\"foldersAccordionExpanded\"\n [attr.aria-label]=\"foldersAccordionExpanded ? 'Collapse organized folders' : 'Expand organized folders'\"\n >\n <span class=\"cqa-text-[11px] cqa-font-semibold cqa-tracking-wider\">{{ labels.organized }}</span>\n <mat-icon\n style=\"font-size:16px;width:16px;height:16px;transition: transform 200ms ease;\"\n [style.transform]=\"foldersAccordionExpanded ? 'rotate(0deg)' : 'rotate(-90deg)'\"\n >expand_more</mat-icon>\n </button>\n\n <!-- Tree (folders only \u2014 Unorganized sits directly below; tree caps at 60vh and scrolls when overflowing).\n Scroll-based lazy-loading at the root level: nearing the bottom emits\n `rootLoadMoreRequested`, which the host responds to by fetching the\n next page of roots. -->\n <div\n class=\"cqa-grid cqa-min-h-0\"\n style=\"transition: grid-template-rows 300ms cubic-bezier(0.4, 0, 0.2, 1);\"\n [style.grid-template-rows]=\"foldersAccordionExpanded ? '1fr' : '0fr'\"\n >\n <div class=\"cqa-min-h-0\" style=\"overflow: hidden;\">\n <div role=\"tree\" class=\"cqa-min-h-0 cqa-max-h-[40vh] cqa-overflow-y-auto cqa-py-1 cqa-scrollbar-thin cqa-scrollbar-track-transparent cqa-scrollbar-thumb-[#E5E7EB] cqa-scrollbar-thumb-rounded-full cqa-scrollbar-thumb-hover:cqa-bg-[#D1D5DB]\"\n (scroll)=\"onTreeScroll($event)\">\n <!-- Initial-load spinner: only renders when we have no folders yet AND\n no search active (search has its own inline spinner). -->\n <div\n *ngIf=\"rootFoldersLoading && folders.length === 0 && !searchValue?.trim()\"\n class=\"cqa-flex cqa-items-center cqa-justify-center cqa-py-6\"\n aria-live=\"polite\"\n >\n <ng-container *ngTemplateOutlet=\"loadingSpinner; context: { size: 20 }\"></ng-container>\n </div>\n <div\n *ngIf=\"searchValue?.trim() && rows.length === 0 && !folderSearchLoading\"\n class=\"cqa-px-3 cqa-py-2 cqa-text-sm cqa-text-neutral-500 cqa-text-center\"\n >\n {{ labels.noFoldersFound }}\n </div>\n <ng-container *ngFor=\"let row of rows; let i = index; trackBy: trackByRow\">\n <!-- Synthetic loading row (lazy fetch in flight) -->\n <div\n *ngIf=\"row.kind === 'loading'\"\n [style.paddingLeft.px]=\"rowIndent(row.depth)\"\n class=\"cqa-flex cqa-items-center cqa-gap-2 cqa-py-1.5 cqa-text-sm cqa-text-neutral-500\"\n aria-live=\"polite\"\n >\n <ng-container *ngTemplateOutlet=\"loadingSpinner; context: { size: 14 }\"></ng-container>\n <span>Loading\u2026</span>\n </div>\n <!-- Synthetic load-more row (more children available on backend) -->\n <button\n *ngIf=\"row.kind === 'load-more'\"\n type=\"button\"\n [style.paddingLeft.px]=\"rowIndent(row.depth)\"\n class=\"cqa-flex cqa-items-center cqa-w-full cqa-py-1.5 cqa-text-sm cqa-text-indigo-600 hover:cqa-bg-[#D8D9FC] cqa-text-left\"\n (click)=\"onLoadMore(row.node); $event.stopPropagation()\"\n >\n <span>Load more ({{ (row.node.totalChildren ?? 0) - (row.node.children?.length ?? 0) }})</span>\n </button>\n <!-- Real folder row -->\n <div\n *ngIf=\"row.kind === 'folder'\"\n role=\"treeitem\"\n tabindex=\"0\"\n [attr.aria-expanded]=\"row.hasChildren ? isExpanded(row.node.id) : null\"\n [attr.aria-selected]=\"isSelected(row.node.id)\"\n [attr.data-folder-row-id]=\"row.node.id\"\n [cqaFolderDrop]=\"row.node.id\"\n [dropEnabled]=\"allowDrop\"\n (testsDropped)=\"rowDropped(row.node.id, $event)\"\n (folderDropped)=\"onFolderRowDropped(row.node.id, $event)\"\n [cqaFolderDrag]=\"row.node.id\"\n [dragEnabled]=\"allowDrop\"\n (dragstart)=\"onFolderDragStart(row.node)\"\n (dragend)=\"onFolderDragEnd()\"\n (dragover)=\"onFolderRowDragOver(row)\"\n (dragleave)=\"onFolderRowDragLeave(row)\"\n (click)=\"onSelect(row.node)\"\n (contextmenu)=\"openContextMenu(row.node, $event)\"\n (keydown)=\"onRowKeydown($event, row, i)\"\n class=\"cqa-group cqa-flex cqa-items-center cqa-gap-1 cqa-pr-3 cqa-py-1.5 cqa-cursor-pointer hover:cqa-bg-[#D8D9FC] focus:cqa-outline-none focus:cqa-bg-[#D8D9FC] cqa-relative\"\n [style.background-color]=\"(isSelected(row.node.id) || contextMenuFolderId === row.node.id) ? '#D8D9FC' : null\"\n [style.paddingLeft.px]=\"rowIndent(row.depth)\"\n [attr.title]=\"folderRowTooltip(row.node)\"\n >\n <span\n *ngIf=\"isSelected(row.node.id)\"\n class=\"cqa-absolute cqa-left-0 cqa-top-0 cqa-bottom-0 cqa-w-[3px] cqa-bg-indigo-600\"\n ></span>\n <button\n type=\"button\"\n class=\"cqa-p-0\"\n [style.color]=\"isSelected(row.node.id) ? '#3F43EE' : '#4C4C51'\"\n [style.visibility]=\"row.hasChildren ? 'visible' : 'hidden'\"\n (click)=\"onToggle(row.node, $event)\"\n [attr.aria-label]=\"isExpanded(row.node.id) ? 'Collapse' : 'Expand'\"\n >\n <mat-icon style=\"font-size:16px;width:16px;height:16px\">\n {{ isExpanded(row.node.id) ? 'expand_more' : 'chevron_right' }}\n </mat-icon>\n </button>\n <ng-container *ngTemplateOutlet=\"folderIcon; context: { color: row.node.color }\"></ng-container>\n\n <ng-container *ngIf=\"renamingId === row.node.id; else nameTpl\">\n <input\n type=\"text\"\n size=\"1\"\n [attr.data-folder-rename-input]=\"row.node.id\"\n [attr.maxlength]=\"renameMaxLength\"\n class=\"cqa-flex-1 cqa-min-w-0 cqa-w-full cqa-px-2 cqa-py-0.5 cqa-rounded cqa-border cqa-bg-white cqa-text-sm cqa-shadow-sm focus:cqa-outline-none focus:cqa-ring-1\"\n [ngClass]=\"(isRenameDraftValid && !renameLimitFlash)\n ? 'cqa-border-neutral-300 focus:cqa-border-indigo-400 focus:cqa-ring-indigo-200'\n : 'cqa-border-[#EF4444] focus:cqa-border-[#EF4444] focus:cqa-ring-[#FCA5A5]'\"\n [attr.aria-invalid]=\"!isRenameDraftValid || renameLimitFlash\"\n [(ngModel)]=\"renameDraft\"\n (keydown)=\"onRenameKey($event, row.node)\"\n (keypress)=\"$event.stopPropagation()\"\n (keyup)=\"$event.stopPropagation()\"\n (blur)=\"commitRename(row.node)\"\n (click)=\"$event.stopPropagation()\"\n />\n </ng-container>\n <ng-template #nameTpl>\n <span\n class=\"cqa-flex-1 cqa-text-sm cqa-truncate\"\n [style.color]=\"isSelected(row.node.id) ? '#3F43EE' : '#4C4C51'\"\n (dblclick)=\"beginRename(row.node, $event)\"\n >{{ row.node.name }}</span>\n </ng-template>\n\n <!-- Saving indicator: replaces the count while the host's rename/save\n API call is in flight, so the user gets immediate feedback. -->\n <span\n *ngIf=\"isFolderSaving(row.node.id)\"\n class=\"cqa-flex cqa-items-center cqa-ml-1 cqa-flex-shrink-0\"\n aria-live=\"polite\"\n [attr.aria-label]=\"'Saving ' + row.node.name\"\n >\n <ng-container *ngTemplateOutlet=\"loadingSpinner; context: { size: 12 }\"></ng-container>\n </span>\n\n <!-- Count shown at rest; hidden on row hover when delete is allowed -->\n <span\n *ngIf=\"!isFolderSaving(row.node.id) && showCounts && row.node.count != null\"\n class=\"cqa-text-xs cqa-tabular-nums cqa-ml-1\"\n [style.color]=\"isSelected(row.node.id) ? '#3F43EE' : '#4C4C51'\"\n [class.group-hover:cqa-hidden]=\"allowDelete\"\n >{{ row.node.count }}</span>\n\n <button\n *ngIf=\"hasAnyContextAction\"\n type=\"button\"\n class=\"cqa-p-0.5 cqa-rounded hover:cqa-bg-neutral-200\"\n [style.color]=\"isSelected(row.node.id) || contextMenuFolderId === row.node.id ? '#3F43EE' : '#4C4C51'\"\n [class.cqa-hidden]=\"contextMenuFolderId !== row.node.id\"\n [class.group-hover:cqa-inline-flex]=\"true\"\n [class.cqa-inline-flex]=\"contextMenuFolderId === row.node.id\"\n (click)=\"openContextMenu(row.node, $event)\"\n [attr.aria-label]=\"'Open actions for ' + row.node.name\"\n [attr.aria-haspopup]=\"'menu'\"\n [attr.aria-expanded]=\"contextMenuFolderId === row.node.id\"\n >\n <mat-icon style=\"font-size:16px;width:16px;height:16px\">more_horiz</mat-icon>\n </button>\n </div>\n </ng-container>\n <!-- Root-level pagination is now driven by scroll (see `(scroll)` on the\n tree container above). Per-folder children retain the explicit\n \"Load more\" button rendered inside the rows loop above, since\n subtrees aren't independently scrollable. -->\n <!-- Pagination spinner \u2014 visible only when fetching an additional page\n of roots (folders.length > 0 distinguishes from initial-load case). -->\n <div\n *ngIf=\"rootFoldersLoading && folders.length > 0 && !searchValue?.trim()\"\n class=\"cqa-flex cqa-items-center cqa-justify-center cqa-py-2\"\n aria-live=\"polite\"\n >\n <ng-container *ngTemplateOutlet=\"loadingSpinner; context: { size: 14 }\"></ng-container>\n </div>\n </div>\n </div>\n </div>\n\n <!-- Divider between folder tree and pinned Unorganized tab -->\n <div\n aria-hidden=\"true\"\n class=\"cqa-mx-3 cqa-flex-shrink-0\"\n style=\"height: 1px; background-color: #E5E7EB; margin-top: 6px; margin-bottom: 6px;\"\n ></div>\n\n <!-- Unorganized \u2014 pinned below the scrollable folder tree so it stays visible regardless of folder count -->\n <div\n role=\"treeitem\"\n tabindex=\"0\"\n [attr.aria-selected]=\"isSelected(null)\"\n class=\"cqa-flex cqa-items-center cqa-gap-2 cqa-px-3 cqa-py-1.5 cqa-cursor-pointer hover:cqa-bg-[#D8D9FC] focus:cqa-outline-none focus:cqa-bg-[#D8D9FC] cqa-relative cqa-flex-shrink-0\"\n [style.background-color]=\"isSelected(null) ? '#D8D9FC' : null\"\n [cqaFolderDrop]=\"null\"\n [dropEnabled]=\"allowDrop\"\n (testsDropped)=\"rowDropped(null, $event)\"\n (folderDropped)=\"onFolderRowDropped(null, $event)\"\n (click)=\"onSelectUnorganized()\"\n >\n <span\n *ngIf=\"isSelected(null)\"\n class=\"cqa-absolute cqa-left-0 cqa-top-0 cqa-bottom-0 cqa-w-[3px] cqa-bg-indigo-600\"\n ></span>\n <mat-icon [style.color]=\"isSelected(null) ? '#3F43EE' : '#4C4C51'\" style=\"font-size:16px;width:16px;height:16px\">inbox</mat-icon>\n <span class=\"cqa-flex-1 cqa-text-sm\" [style.color]=\"isSelected(null) ? '#3F43EE' : '#4C4C51'\">{{ labels.unorganized }}</span>\n <span *ngIf=\"showCounts\" class=\"cqa-text-xs cqa-tabular-nums\" [style.color]=\"isSelected(null) ? '#3F43EE' : '#4C4C51'\">{{ unorganizedCount }}</span>\n </div>\n\n <!-- Folder context menu (right-click / ellipsis). A 0\u00D70 fixed-position\n anchor at the click coords drives a cdkConnectedOverlay; CDK's\n flexible-connected positioning auto-flips up/left when the menu\n would overflow the viewport (last-folder-near-bottom case). -->\n <div #ctxAnchor cdkOverlayOrigin\n class=\"cqa-fixed cqa-w-0 cqa-h-0 cqa-pointer-events-none\"\n [style.left.px]=\"contextMenuPosition.x\"\n [style.top.px]=\"contextMenuPosition.y\"></div>\n\n <ng-template\n cdkConnectedOverlay\n [cdkConnectedOverlayOrigin]=\"ctxAnchor\"\n [cdkConnectedOverlayOpen]=\"contextMenuFolderId !== null\"\n [cdkConnectedOverlayPositions]=\"contextMenuPositions\"\n [cdkConnectedOverlayHasBackdrop]=\"false\"\n [cdkConnectedOverlayFlexibleDimensions]=\"false\"\n [cdkConnectedOverlayPush]=\"true\"\n (overlayOutsideClick)=\"onOverlayOutsideClick()\"\n (detach)=\"closeContextMenu()\"\n >\n <!-- The lib's tailwind config sets important: '.cqa-ui-root', which\n emits descendant selectors like `.cqa-ui-root .cqa-bg-white`.\n CDK Overlay teleports this template into the global overlay\n container (outside the host's cqa-ui-root), so we wrap the menu in\n an ancestor cqa-ui-root div to re-establish the utility scope. -->\n <div class=\"cqa-ui-root\">\n <div\n role=\"menu\"\n class=\"cqa-min-w-[180px] cqa-bg-white cqa-border cqa-border-neutral-200 cqa-rounded-md cqa-shadow-lg cqa-py-1\"\n (click)=\"$event.stopPropagation()\"\n (contextmenu)=\"$event.preventDefault(); $event.stopPropagation()\"\n >\n <ng-container *ngIf=\"folderById(contextMenuFolderId) as menuNode\">\n <button\n *ngIf=\"allowCreate\"\n type=\"button\"\n role=\"menuitem\"\n class=\"cqa-flex cqa-items-center cqa-gap-2 cqa-w-full cqa-px-3 cqa-py-1.5 cqa-text-sm cqa-text-neutral-800 hover:cqa-bg-indigo-50 cqa-text-left\"\n (click)=\"onContextCreateSubfolder(menuNode)\"\n >\n <mat-icon style=\"font-size:16px;width:16px;height:16px\">create_new_folder</mat-icon>\n <span>{{ labels.folderMenuCreateSubfolder }}</span>\n </button>\n <button\n *ngIf=\"allowRename\"\n type=\"button\"\n role=\"menuitem\"\n class=\"cqa-flex cqa-items-center cqa-gap-2 cqa-w-full cqa-px-3 cqa-py-1.5 cqa-text-sm cqa-text-neutral-800 hover:cqa-bg-indigo-50 cqa-text-left\"\n (click)=\"onContextRename(menuNode)\"\n >\n <mat-icon style=\"font-size:16px;width:16px;height:16px\">edit</mat-icon>\n <span>{{ labels.folderMenuRename }}</span>\n </button>\n <button\n *ngIf=\"allowMove\"\n type=\"button\"\n role=\"menuitem\"\n class=\"cqa-flex cqa-items-center cqa-gap-2 cqa-w-full cqa-px-3 cqa-py-1.5 cqa-text-sm cqa-text-neutral-800 hover:cqa-bg-indigo-50 cqa-text-left\"\n (click)=\"onContextMove(menuNode)\"\n >\n <mat-icon style=\"font-size:16px;width:16px;height:16px\">drive_file_move</mat-icon>\n <span>{{ labels.folderMenuMove }}</span>\n </button>\n <button\n *ngIf=\"allowDuplicate\"\n type=\"button\"\n role=\"menuitem\"\n class=\"cqa-flex cqa-items-center cqa-gap-2 cqa-w-full cqa-px-3 cqa-py-1.5 cqa-text-sm cqa-text-neutral-800 hover:cqa-bg-indigo-50 cqa-text-left\"\n (click)=\"onContextDuplicate(menuNode)\"\n >\n <mat-icon style=\"font-size:16px;width:16px;height:16px\">content_copy</mat-icon>\n <span>{{ labels.folderMenuDuplicate }}</span>\n </button>\n <div *ngIf=\"allowDelete && (allowCreate || allowRename || allowMove || allowDuplicate)\" class=\"cqa-h-px cqa-my-1\" style=\"background-color: #E5E7EB;\"></div>\n <button\n *ngIf=\"allowDelete\"\n type=\"button\"\n role=\"menuitem\"\n class=\"cqa-flex cqa-items-center cqa-gap-2 cqa-w-full cqa-px-3 cqa-py-1.5 cqa-text-sm cqa-text-red-600 hover:cqa-bg-red-50 cqa-text-left\"\n (click)=\"onContextDelete(menuNode)\"\n >\n <mat-icon style=\"font-size:16px;width:16px;height:16px\">delete_outline</mat-icon>\n <span>{{ labels.folderMenuDelete }}</span>\n </button>\n </ng-container>\n </div>\n </div>\n </ng-template>\n\n </ng-container>\n </div>\n</aside>\n", styles: [] }]
9744
+ args: [{ selector: 'cqa-folder-sidebar', host: { class: 'cqa-ui-root' }, changeDetection: ChangeDetectionStrategy.OnPush, template: "<!-- Reusable indeterminate loading spinner. Render via\n <ng-container *ngTemplateOutlet=\"loadingSpinner; context: { size: 14 }\"></ng-container>. -->\n<ng-template #loadingSpinner let-size=\"size\">\n <svg [attr.width]=\"size || 16\" [attr.height]=\"size || 16\" viewBox=\"0 0 50 50\" aria-label=\"loading\" xmlns=\"http://www.w3.org/2000/svg\">\n <circle cx=\"25\" cy=\"25\" r=\"20\" stroke=\"#E5E7EB\" stroke-width=\"6\" fill=\"none\"></circle>\n <path d=\"M45 25a20 20 0 0 0-20-20\" stroke=\"#4F46E5\" stroke-width=\"6\" fill=\"none\" stroke-linecap=\"round\">\n <animateTransform attributeName=\"transform\" type=\"rotate\" from=\"0 25 25\" to=\"360 25 25\" dur=\"0.8s\" repeatCount=\"indefinite\"></animateTransform>\n </path>\n </svg>\n</ng-template>\n\n<!-- Reusable folder icon. Render via <ng-container *ngTemplateOutlet=\"folderIcon; context: { color: node.color }\"></ng-container>. -->\n<ng-template #folderIcon let-color=\"color\">\n <span class=\"cqa-inline-flex cqa-items-center cqa-justify-center cqa-w-4 cqa-h-4 cqa-flex-shrink-0\">\n <svg width=\"13\" height=\"12\" viewBox=\"0 0 13 12\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\">\n <path d=\"M12.1916 9.85824C12.1916 10.1677 12.0687 10.4644 11.8499 10.6832C11.6311 10.902 11.3343 11.0249 11.0249 11.0249H1.69157C1.38215 11.0249 1.0854 10.902 0.866611 10.6832C0.647819 10.4644 0.524902 10.1677 0.524902 9.85824V1.69157C0.524902 1.38215 0.647819 1.0854 0.866611 0.866611C1.0854 0.647819 1.38215 0.524902 1.69157 0.524902H4.60824L5.7749 2.2749H11.0249C11.3343 2.2749 11.6311 2.39782 11.8499 2.61661C12.0687 2.8354 12.1916 3.13215 12.1916 3.44157V9.85824Z\" [attr.stroke]=\"color || '#99999E'\" stroke-width=\"1.05\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/>\n </svg>\n </span>\n</ng-template>\n\n<aside\n class=\"cqa-flex cqa-flex-col cqa-bg-white cqa-h-full cqa-min-h-0\"\n [class.cqa-border]=\"!collapsed\"\n [class.cqa-border-neutral-200]=\"!collapsed\"\n [class.cqa-rounded-lg]=\"!collapsed\"\n [style.width.px]=\"collapsed ? 22 : 240\"\n style=\"overflow: hidden; transition: width 260ms cubic-bezier(0.4, 0, 0.2, 1);\"\n>\n <!-- Collapsed rail: the entire sidebar becomes a single click target so the\n slim strip reads as an obvious \"expand me\" handle. -->\n <button\n *ngIf=\"collapsed\"\n type=\"button\"\n style=\"width: 22px;\"\n class=\"cqa-group cqa-flex cqa-flex-col cqa-items-center cqa-flex-shrink-0 cqa-h-full cqa-py-2 cqa-gap-3 cqa-bg-neutral-50 cqa-border cqa-border-neutral-200 cqa-text-neutral-500 hover:cqa-bg-indigo-50 hover:cqa-border-indigo-200 hover:cqa-text-indigo-600 focus:cqa-outline-none focus:cqa-ring-2 focus:cqa-ring-indigo-200 cqa-cursor-pointer\"\n (click)=\"togglePanel()\"\n [attr.aria-label]=\"'Expand folders panel'\"\n [title]=\"'Expand folders panel'\"\n >\n <mat-icon style=\"font-size:16px;width:16px;height:16px\">chevron_right</mat-icon>\n <span class=\"cqa-inline-flex cqa-items-center cqa-justify-center\" aria-hidden=\"true\">\n <svg width=\"13\" height=\"12\" viewBox=\"0 0 13 12\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n <path d=\"M12.1916 9.85824C12.1916 10.1677 12.0687 10.4644 11.8499 10.6832C11.6311 10.902 11.3343 11.0249 11.0249 11.0249H1.69157C1.38215 11.0249 1.0854 10.902 0.866611 10.6832C0.647819 10.4644 0.524902 10.1677 0.524902 9.85824V1.69157C0.524902 1.38215 0.647819 1.0854 0.866611 0.866611C1.0854 0.647819 1.38215 0.524902 1.69157 0.524902H4.60824L5.7749 2.2749H11.0249C11.3343 2.2749 11.6311 2.39782 11.8499 2.61661C12.0687 2.8354 12.1916 3.13215 12.1916 3.44157V9.85824Z\" stroke=\"currentColor\" stroke-width=\"1.05\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/>\n </svg>\n </span>\n </button>\n\n <div *ngIf=\"!collapsed\" class=\"cqa-flex cqa-flex-col cqa-h-full cqa-min-h-0 cqa-pb-2\" style=\"width: 240px;\">\n <!-- Header (expanded) -->\n <div\n class=\"cqa-flex cqa-items-center cqa-justify-between cqa-py-1 cqa-px-3\"\n style=\"border-bottom: 1px solid #E2E2E3; margin-bottom: 8px;\"\n >\n <span class=\"cqa-text-sm cqa-font-semibold cqa-text-neutral-900 cqa-py-0.5\">{{ labels.folders }}</span>\n <div class=\"cqa-flex cqa-items-center cqa-gap-1\">\n <button\n type=\"button\"\n class=\"cqa-p-1 cqa-rounded hover:cqa-bg-neutral-100 cqa-text-neutral-500\"\n (click)=\"togglePanel()\"\n [attr.aria-label]=\"'Collapse folders panel'\"\n [title]=\"'Collapse folders panel'\"\n >\n <mat-icon style=\"font-size:18px;width:18px;height:18px\">keyboard_double_arrow_left</mat-icon>\n </button>\n <button\n type=\"button\"\n class=\"cqa-p-1 cqa-rounded hover:cqa-bg-neutral-100\"\n [class.cqa-text-neutral-500]=\"!searchVisible\"\n [class.cqa-text-indigo-600]=\"searchVisible\"\n [class.cqa-bg-neutral-100]=\"searchVisible\"\n (click)=\"toggleSearchVisible()\"\n [attr.aria-label]=\"searchVisible ? 'Hide folder search' : 'Search folders'\"\n [attr.aria-pressed]=\"searchVisible\"\n [title]=\"searchVisible ? 'Hide folder search' : 'Search folders'\"\n >\n <mat-icon style=\"font-size:18px;width:18px;height:18px\">search</mat-icon>\n </button>\n <button\n *ngIf=\"allowCreate\"\n type=\"button\"\n class=\"cqa-p-1 cqa-rounded hover:cqa-bg-neutral-100 cqa-text-neutral-500\"\n (click)=\"requestCreate(null)\"\n [attr.aria-label]=\"labels.newFolder\"\n [title]=\"labels.newFolder\"\n >\n <mat-icon style=\"font-size:18px;width:18px;height:18px\">add</mat-icon>\n </button>\n </div>\n </div>\n\n <ng-container>\n <!-- Search (toggled via the search icon in the header) -->\n <div\n class=\"cqa-grid\"\n style=\"transition: grid-template-rows 300ms cubic-bezier(0.4, 0, 0.2, 1);\"\n [style.grid-template-rows]=\"searchVisible ? '1fr' : '0fr'\"\n >\n <div class=\"cqa-min-h-0\" style=\"overflow: hidden;\">\n <div class=\"cqa-px-3 cqa-pb-2\">\n <div class=\"cqa-relative\">\n <cqa-search-bar\n size=\"sm\"\n [fullWidth]=\"true\"\n [value]=\"searchValue\"\n [autoFocus]=\"searchVisible\"\n [placeholder]=\"labels.searchFoldersPlaceholder\"\n [showClear]=\"true\"\n (valueChange)=\"onSearchInput($event)\"\n (cleared)=\"onSearchClear()\"\n ></cqa-search-bar>\n <span\n *ngIf=\"folderSearchLoading\"\n class=\"cqa-absolute cqa-right-8 cqa-inset-y-0 cqa-flex cqa-items-center\"\n aria-live=\"polite\"\n aria-label=\"Searching folders\"\n >\n <ng-container *ngTemplateOutlet=\"loadingSpinner; context: { size: 14 }\"></ng-container>\n </span>\n </div>\n </div>\n </div>\n </div>\n\n <!-- Organized section header \u2014 visually distinct from folder rows so it reads\n as a view/section toggle rather than a folder item. -->\n <button\n type=\"button\"\n class=\"cqa-flex cqa-items-center cqa-justify-between cqa-w-full cqa-px-3 cqa-py-1 cqa-text-left cqa-text-[#6D6D74] hover:cqa-text-[#3F43EE] focus:cqa-outline-none focus:cqa-text-[#3F43EE] cqa-flex-shrink-0 cqa-uppercase\"\n (click)=\"toggleFoldersAccordion()\"\n [attr.aria-expanded]=\"foldersAccordionExpanded\"\n [attr.aria-label]=\"foldersAccordionExpanded ? 'Collapse organized folders' : 'Expand organized folders'\"\n >\n <span class=\"cqa-text-[11px] cqa-font-semibold cqa-tracking-wider\">{{ labels.organized }}</span>\n <mat-icon\n style=\"font-size:16px;width:16px;height:16px;transition: transform 200ms ease;\"\n [style.transform]=\"foldersAccordionExpanded ? 'rotate(0deg)' : 'rotate(-90deg)'\"\n >expand_more</mat-icon>\n </button>\n\n <!-- Tree (folders only \u2014 Unorganized sits directly below; tree caps at 60vh and scrolls when overflowing).\n Scroll-based lazy-loading at the root level: nearing the bottom emits\n `rootLoadMoreRequested`, which the host responds to by fetching the\n next page of roots. -->\n <div\n class=\"cqa-grid cqa-min-h-0\"\n style=\"transition: grid-template-rows 300ms cubic-bezier(0.4, 0, 0.2, 1);\"\n [style.grid-template-rows]=\"foldersAccordionExpanded ? '1fr' : '0fr'\"\n >\n <div class=\"cqa-min-h-0\" style=\"overflow: hidden;\">\n <div role=\"tree\" class=\"cqa-min-h-0 cqa-max-h-[40vh] cqa-overflow-y-auto cqa-py-1 cqa-scrollbar-thin cqa-scrollbar-track-transparent cqa-scrollbar-thumb-[#E5E7EB] cqa-scrollbar-thumb-rounded-full cqa-scrollbar-thumb-hover:cqa-bg-[#D1D5DB]\"\n (scroll)=\"onTreeScroll($event)\">\n <!-- Initial-load spinner: only renders when we have no folders yet AND\n no search active (search has its own inline spinner). -->\n <div\n *ngIf=\"rootFoldersLoading && folders.length === 0 && !searchValue?.trim()\"\n class=\"cqa-flex cqa-items-center cqa-justify-center cqa-py-6\"\n aria-live=\"polite\"\n >\n <ng-container *ngTemplateOutlet=\"loadingSpinner; context: { size: 20 }\"></ng-container>\n </div>\n <div\n *ngIf=\"searchValue?.trim() && rows.length === 0 && !folderSearchLoading\"\n class=\"cqa-px-3 cqa-py-2 cqa-text-sm cqa-text-neutral-500 cqa-text-center\"\n >\n {{ labels.noFoldersFound }}\n </div>\n <div\n *ngIf=\"!rootFoldersLoading && folders.length === 0 && !searchValue?.trim()\"\n class=\"cqa-flex cqa-flex-col cqa-items-center cqa-justify-center cqa-text-center cqa-px-4 cqa-py-6 cqa-gap-2\"\n >\n <span class=\"cqa-inline-flex cqa-items-center cqa-justify-center cqa-w-10 cqa-h-10 cqa-rounded-full cqa-bg-neutral-100 cqa-text-neutral-400\">\n <mat-icon style=\"font-size:22px;width:22px;height:22px\">folder_open</mat-icon>\n </span>\n <span class=\"cqa-text-sm cqa-font-medium cqa-text-neutral-700\">{{ labels.noFoldersFound }}</span>\n <span *ngIf=\"allowCreate\" class=\"cqa-text-xs cqa-text-neutral-500 cqa-leading-snug\">\n {{ labels.noFoldersEmptyDescription }}\n </span>\n </div>\n <ng-container *ngFor=\"let row of rows; let i = index; trackBy: trackByRow\">\n <!-- Synthetic loading row (lazy fetch in flight) -->\n <div\n *ngIf=\"row.kind === 'loading'\"\n [style.paddingLeft.px]=\"rowIndent(row.depth)\"\n class=\"cqa-flex cqa-items-center cqa-gap-2 cqa-py-1.5 cqa-text-sm cqa-text-neutral-500\"\n aria-live=\"polite\"\n >\n <ng-container *ngTemplateOutlet=\"loadingSpinner; context: { size: 14 }\"></ng-container>\n <span>Loading\u2026</span>\n </div>\n <!-- Synthetic load-more row (more children available on backend) -->\n <button\n *ngIf=\"row.kind === 'load-more'\"\n type=\"button\"\n [style.paddingLeft.px]=\"rowIndent(row.depth)\"\n class=\"cqa-flex cqa-items-center cqa-w-full cqa-py-1.5 cqa-text-sm cqa-text-indigo-600 hover:cqa-bg-[#D8D9FC] cqa-text-left\"\n (click)=\"onLoadMore(row.node); $event.stopPropagation()\"\n >\n <span>Load more ({{ (row.node.totalChildren ?? 0) - (row.node.children?.length ?? 0) }})</span>\n </button>\n <!-- Real folder row -->\n <div\n *ngIf=\"row.kind === 'folder'\"\n role=\"treeitem\"\n tabindex=\"0\"\n [attr.aria-expanded]=\"row.hasChildren ? isExpanded(row.node.id) : null\"\n [attr.aria-selected]=\"isSelected(row.node.id)\"\n [attr.data-folder-row-id]=\"row.node.id\"\n [cqaFolderDrop]=\"row.node.id\"\n [dropEnabled]=\"allowDrop\"\n (testsDropped)=\"rowDropped(row.node.id, $event)\"\n (folderDropped)=\"onFolderRowDropped(row.node.id, $event)\"\n [cqaFolderDrag]=\"row.node.id\"\n [dragEnabled]=\"allowDrop\"\n (dragstart)=\"onFolderDragStart(row.node)\"\n (dragend)=\"onFolderDragEnd()\"\n (dragover)=\"onFolderRowDragOver(row)\"\n (dragleave)=\"onFolderRowDragLeave(row)\"\n (click)=\"onSelect(row.node)\"\n (contextmenu)=\"openContextMenu(row.node, $event)\"\n (keydown)=\"onRowKeydown($event, row, i)\"\n class=\"cqa-group cqa-flex cqa-items-center cqa-gap-1 cqa-pr-3 cqa-py-1.5 cqa-cursor-pointer hover:cqa-bg-[#D8D9FC] focus:cqa-outline-none focus:cqa-bg-[#D8D9FC] cqa-relative\"\n [style.background-color]=\"renamingId === row.node.id ? '#FFFFFF' : ((isSelected(row.node.id) || contextMenuFolderId === row.node.id) ? '#D8D9FC' : null)\"\n [style.paddingLeft.px]=\"rowIndent(row.depth)\"\n [attr.title]=\"folderRowTooltip(row.node)\"\n >\n <span\n *ngIf=\"isSelected(row.node.id)\"\n class=\"cqa-absolute cqa-left-0 cqa-top-0 cqa-bottom-0 cqa-w-[3px] cqa-bg-indigo-600\"\n ></span>\n <button\n type=\"button\"\n class=\"cqa-p-0\"\n [style.color]=\"isSelected(row.node.id) ? '#3F43EE' : '#4C4C51'\"\n [style.visibility]=\"row.hasChildren ? 'visible' : 'hidden'\"\n (click)=\"onToggle(row.node, $event)\"\n [attr.aria-label]=\"isExpanded(row.node.id) ? 'Collapse' : 'Expand'\"\n >\n <mat-icon style=\"font-size:16px;width:16px;height:16px\">\n {{ isExpanded(row.node.id) ? 'expand_more' : 'chevron_right' }}\n </mat-icon>\n </button>\n <ng-container *ngTemplateOutlet=\"folderIcon; context: { color: row.node.color }\"></ng-container>\n\n <ng-container *ngIf=\"renamingId === row.node.id; else nameTpl\">\n <input\n type=\"text\"\n size=\"1\"\n [attr.data-folder-rename-input]=\"row.node.id\"\n [attr.maxlength]=\"renameMaxLength\"\n class=\"cqa-flex-1 cqa-min-w-0 cqa-w-full cqa-px-2 cqa-py-0.5 cqa-rounded cqa-border cqa-bg-white cqa-text-sm cqa-shadow-sm focus:cqa-outline-none focus:cqa-ring-1\"\n [ngClass]=\"(isRenameDraftValid && !renameLimitFlash)\n ? 'cqa-border-neutral-300 focus:cqa-border-indigo-400 focus:cqa-ring-indigo-200'\n : 'cqa-border-[#EF4444] focus:cqa-border-[#EF4444] focus:cqa-ring-[#FCA5A5]'\"\n [attr.aria-invalid]=\"!isRenameDraftValid || renameLimitFlash\"\n [(ngModel)]=\"renameDraft\"\n (keydown)=\"onRenameKey($event, row.node)\"\n (keypress)=\"$event.stopPropagation()\"\n (keyup)=\"$event.stopPropagation()\"\n (blur)=\"commitRename(row.node)\"\n (click)=\"$event.stopPropagation()\"\n />\n </ng-container>\n <ng-template #nameTpl>\n <span\n class=\"cqa-flex-1 cqa-text-sm cqa-truncate\"\n [style.color]=\"isSelected(row.node.id) ? '#3F43EE' : '#4C4C51'\"\n (dblclick)=\"beginRename(row.node, $event)\"\n >{{ row.node.name }}</span>\n </ng-template>\n\n <!-- Saving indicator: replaces the count while the host's rename/save\n API call is in flight, so the user gets immediate feedback. -->\n <span\n *ngIf=\"isFolderSaving(row.node.id)\"\n class=\"cqa-flex cqa-items-center cqa-ml-1 cqa-flex-shrink-0\"\n aria-live=\"polite\"\n [attr.aria-label]=\"'Saving ' + row.node.name\"\n >\n <ng-container *ngTemplateOutlet=\"loadingSpinner; context: { size: 12 }\"></ng-container>\n </span>\n\n <!-- Count shown at rest; hidden on row hover when delete is allowed -->\n <span\n *ngIf=\"!isFolderSaving(row.node.id) && showCounts && row.node.count != null\"\n class=\"cqa-text-xs cqa-tabular-nums cqa-ml-1\"\n [style.color]=\"isSelected(row.node.id) ? '#3F43EE' : '#4C4C51'\"\n [class.group-hover:cqa-hidden]=\"allowDelete\"\n >{{ row.node.count }}</span>\n\n <button\n *ngIf=\"hasAnyContextAction\"\n type=\"button\"\n class=\"cqa-p-0.5 cqa-rounded hover:cqa-bg-neutral-200\"\n [style.color]=\"isSelected(row.node.id) || contextMenuFolderId === row.node.id ? '#3F43EE' : '#4C4C51'\"\n [class.cqa-hidden]=\"contextMenuFolderId !== row.node.id\"\n [class.group-hover:cqa-inline-flex]=\"true\"\n [class.cqa-inline-flex]=\"contextMenuFolderId === row.node.id\"\n (click)=\"openContextMenu(row.node, $event)\"\n [attr.aria-label]=\"'Open actions for ' + row.node.name\"\n [attr.aria-haspopup]=\"'menu'\"\n [attr.aria-expanded]=\"contextMenuFolderId === row.node.id\"\n >\n <mat-icon style=\"font-size:16px;width:16px;height:16px\">more_horiz</mat-icon>\n </button>\n </div>\n </ng-container>\n <!-- Root-level pagination is now driven by scroll (see `(scroll)` on the\n tree container above). Per-folder children retain the explicit\n \"Load more\" button rendered inside the rows loop above, since\n subtrees aren't independently scrollable. -->\n <!-- Pagination spinner \u2014 visible only when fetching an additional page\n of roots (folders.length > 0 distinguishes from initial-load case). -->\n <div\n *ngIf=\"rootFoldersLoading && folders.length > 0 && !searchValue?.trim()\"\n class=\"cqa-flex cqa-items-center cqa-justify-center cqa-py-2\"\n aria-live=\"polite\"\n >\n <ng-container *ngTemplateOutlet=\"loadingSpinner; context: { size: 14 }\"></ng-container>\n </div>\n </div>\n </div>\n </div>\n\n <!-- Divider between folder tree and pinned Unorganized tab -->\n <div\n aria-hidden=\"true\"\n class=\"cqa-mx-3 cqa-flex-shrink-0\"\n style=\"height: 1px; background-color: #E5E7EB; margin-top: 6px; margin-bottom: 6px;\"\n ></div>\n\n <!-- Unorganized \u2014 pinned below the scrollable folder tree so it stays visible regardless of folder count -->\n <div\n role=\"treeitem\"\n tabindex=\"0\"\n [attr.aria-selected]=\"isSelected(null)\"\n class=\"cqa-flex cqa-items-center cqa-gap-2 cqa-px-3 cqa-py-1.5 cqa-cursor-pointer hover:cqa-bg-[#D8D9FC] focus:cqa-outline-none focus:cqa-bg-[#D8D9FC] cqa-relative cqa-flex-shrink-0\"\n [style.background-color]=\"isSelected(null) ? '#D8D9FC' : null\"\n [cqaFolderDrop]=\"null\"\n [dropEnabled]=\"allowDrop\"\n (testsDropped)=\"rowDropped(null, $event)\"\n (folderDropped)=\"onFolderRowDropped(null, $event)\"\n (click)=\"onSelectUnorganized()\"\n >\n <span\n *ngIf=\"isSelected(null)\"\n class=\"cqa-absolute cqa-left-0 cqa-top-0 cqa-bottom-0 cqa-w-[3px] cqa-bg-indigo-600\"\n ></span>\n <mat-icon [style.color]=\"isSelected(null) ? '#3F43EE' : '#4C4C51'\" style=\"font-size:16px;width:16px;height:16px\">inbox</mat-icon>\n <span class=\"cqa-flex-1 cqa-text-sm\" [style.color]=\"isSelected(null) ? '#3F43EE' : '#4C4C51'\">{{ labels.unorganized }}</span>\n <span *ngIf=\"showCounts\" class=\"cqa-text-xs cqa-tabular-nums\" [style.color]=\"isSelected(null) ? '#3F43EE' : '#4C4C51'\">{{ unorganizedCount }}</span>\n </div>\n\n <!-- Folder context menu (right-click / ellipsis). A 0\u00D70 fixed-position\n anchor at the click coords drives a cdkConnectedOverlay; CDK's\n flexible-connected positioning auto-flips up/left when the menu\n would overflow the viewport (last-folder-near-bottom case). -->\n <div #ctxAnchor cdkOverlayOrigin\n class=\"cqa-fixed cqa-w-0 cqa-h-0 cqa-pointer-events-none\"\n [style.left.px]=\"contextMenuPosition.x\"\n [style.top.px]=\"contextMenuPosition.y\"></div>\n\n <ng-template\n cdkConnectedOverlay\n [cdkConnectedOverlayOrigin]=\"ctxAnchor\"\n [cdkConnectedOverlayOpen]=\"contextMenuFolderId !== null\"\n [cdkConnectedOverlayPositions]=\"contextMenuPositions\"\n [cdkConnectedOverlayHasBackdrop]=\"false\"\n [cdkConnectedOverlayFlexibleDimensions]=\"false\"\n [cdkConnectedOverlayPush]=\"true\"\n (overlayOutsideClick)=\"onOverlayOutsideClick()\"\n (detach)=\"closeContextMenu()\"\n >\n <!-- The lib's tailwind config sets important: '.cqa-ui-root', which\n emits descendant selectors like `.cqa-ui-root .cqa-bg-white`.\n CDK Overlay teleports this template into the global overlay\n container (outside the host's cqa-ui-root), so we wrap the menu in\n an ancestor cqa-ui-root div to re-establish the utility scope. -->\n <div class=\"cqa-ui-root\">\n <div\n role=\"menu\"\n class=\"cqa-min-w-[180px] cqa-bg-white cqa-border cqa-border-neutral-200 cqa-rounded-md cqa-shadow-lg cqa-py-1\"\n (click)=\"$event.stopPropagation()\"\n (contextmenu)=\"$event.preventDefault(); $event.stopPropagation()\"\n >\n <ng-container *ngIf=\"folderById(contextMenuFolderId) as menuNode\">\n <button\n *ngIf=\"allowCreate\"\n type=\"button\"\n role=\"menuitem\"\n class=\"cqa-flex cqa-items-center cqa-gap-2 cqa-w-full cqa-px-3 cqa-py-1.5 cqa-text-sm cqa-text-neutral-800 hover:cqa-bg-indigo-50 cqa-text-left\"\n (click)=\"onContextCreateSubfolder(menuNode)\"\n >\n <mat-icon style=\"font-size:16px;width:16px;height:16px\">create_new_folder</mat-icon>\n <span>{{ labels.folderMenuCreateSubfolder }}</span>\n </button>\n <button\n *ngIf=\"allowRename\"\n type=\"button\"\n role=\"menuitem\"\n class=\"cqa-flex cqa-items-center cqa-gap-2 cqa-w-full cqa-px-3 cqa-py-1.5 cqa-text-sm cqa-text-neutral-800 hover:cqa-bg-indigo-50 cqa-text-left\"\n (click)=\"onContextRename(menuNode)\"\n >\n <mat-icon style=\"font-size:16px;width:16px;height:16px\">edit</mat-icon>\n <span>{{ labels.folderMenuRename }}</span>\n </button>\n <button\n *ngIf=\"allowMove\"\n type=\"button\"\n role=\"menuitem\"\n class=\"cqa-flex cqa-items-center cqa-gap-2 cqa-w-full cqa-px-3 cqa-py-1.5 cqa-text-sm cqa-text-neutral-800 hover:cqa-bg-indigo-50 cqa-text-left\"\n (click)=\"onContextMove(menuNode)\"\n >\n <mat-icon style=\"font-size:16px;width:16px;height:16px\">drive_file_move</mat-icon>\n <span>{{ labels.folderMenuMove }}</span>\n </button>\n <button\n *ngIf=\"allowDuplicate\"\n type=\"button\"\n role=\"menuitem\"\n class=\"cqa-flex cqa-items-center cqa-gap-2 cqa-w-full cqa-px-3 cqa-py-1.5 cqa-text-sm cqa-text-neutral-800 hover:cqa-bg-indigo-50 cqa-text-left\"\n (click)=\"onContextDuplicate(menuNode)\"\n >\n <mat-icon style=\"font-size:16px;width:16px;height:16px\">content_copy</mat-icon>\n <span>{{ labels.folderMenuDuplicate }}</span>\n </button>\n <div *ngIf=\"allowDelete && (allowCreate || allowRename || allowMove || allowDuplicate)\" class=\"cqa-h-px cqa-my-1\" style=\"background-color: #E5E7EB;\"></div>\n <button\n *ngIf=\"allowDelete\"\n type=\"button\"\n role=\"menuitem\"\n class=\"cqa-flex cqa-items-center cqa-gap-2 cqa-w-full cqa-px-3 cqa-py-1.5 cqa-text-sm cqa-text-red-600 hover:cqa-bg-red-50 cqa-text-left\"\n (click)=\"onContextDelete(menuNode)\"\n >\n <mat-icon style=\"font-size:16px;width:16px;height:16px\">delete_outline</mat-icon>\n <span>{{ labels.folderMenuDelete }}</span>\n </button>\n </ng-container>\n </div>\n </div>\n </ng-template>\n\n </ng-container>\n </div>\n</aside>\n", styles: [] }]
9735
9745
  }], ctorParameters: function () { return [{ type: i0.ChangeDetectorRef }]; }, propDecorators: { folders: [{
9736
9746
  type: Input
9737
9747
  }], selectedFolderId: [{
@@ -9862,6 +9872,15 @@ class ModularTableTemplateComponent {
9862
9872
  this.viewMode = 'list';
9863
9873
  this.viewModeLabels = { list: 'List', modular: 'Modular' };
9864
9874
  this.viewModeChange = new EventEmitter();
9875
+ // Stable reference fed to <cqa-segment-control [segments]>. A getter here
9876
+ // allocates a new array each CD tick, which the segment control then sees as
9877
+ // a changed Input — its updateIndicator runs inside a queueMicrotask, the
9878
+ // zone re-ticks via onMicrotaskEmpty, and the loop pegs CPU at 100% with
9879
+ // HTTP requests starved. Rebuilt only when viewModeLabels actually changes.
9880
+ this.viewModeSegments = [
9881
+ { label: 'List', value: 'list', icon: 'view_list' },
9882
+ { label: 'Modular', value: 'modular', icon: 'folder' },
9883
+ ];
9865
9884
  // Data input
9866
9885
  this.data = [];
9867
9886
  // Empty state inputs
@@ -10158,12 +10177,6 @@ class ModularTableTemplateComponent {
10158
10177
  return ids;
10159
10178
  };
10160
10179
  }
10161
- get viewModeSegments() {
10162
- return [
10163
- { label: this.viewModeLabels.list, value: 'list', icon: 'view_list' },
10164
- { label: this.viewModeLabels.modular, value: 'modular', icon: 'folder' },
10165
- ];
10166
- }
10167
10180
  onViewModeChange(value) {
10168
10181
  const next = value === 'modular' ? 'modular' : 'list';
10169
10182
  if (next !== this.viewMode) {
@@ -10321,6 +10334,12 @@ class ModularTableTemplateComponent {
10321
10334
  if (changes['selectedItems'] || changes['data']) {
10322
10335
  this.restoreSelectionState();
10323
10336
  }
10337
+ if (changes['viewModeLabels']) {
10338
+ this.viewModeSegments = [
10339
+ { label: this.viewModeLabels.list, value: 'list', icon: 'view_list' },
10340
+ { label: this.viewModeLabels.modular, value: 'modular', icon: 'folder' },
10341
+ ];
10342
+ }
10324
10343
  if (changes['reorderSaving'] && !changes['reorderSaving'].firstChange) {
10325
10344
  const nowSaving = !!changes['reorderSaving'].currentValue;
10326
10345
  // Host finished its bulk-update call — exit reorder mode.
@@ -18951,6 +18970,12 @@ class ApiStepComponent extends BaseStepComponent {
18951
18970
  }
18952
18971
  }
18953
18972
  formatJson(obj) {
18973
+ // If the caller already passed a pre-formatted string (e.g. a GraphQL query with
18974
+ // embedded newlines), render it as-is. Running JSON.stringify on it would wrap
18975
+ // the value in quotes and escape every `\n` into a literal `\n`, which is what
18976
+ // shows up as a single-line blob in the Request Body panel.
18977
+ if (typeof obj === 'string')
18978
+ return obj;
18954
18979
  return JSON.stringify(obj, null, 2);
18955
18980
  }
18956
18981
  getPassedAssertions() {
@@ -36371,6 +36396,124 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.4.0", ngImpor
36371
36396
  type: Output
36372
36397
  }] } });
36373
36398
 
36399
+ class GraphQLOperationPickerComponent {
36400
+ constructor() {
36401
+ /** Flat operations list. Picker partitions by `kind` for the section headers. */
36402
+ this.operations = [];
36403
+ /** Display state for the "Introspected" / "Pending" badge in the title row. */
36404
+ this.introspected = false;
36405
+ /** True while the parent is fetching/introspecting a schema. Renders a spinner instead of the empty state. */
36406
+ this.isLoading = false;
36407
+ /** Emitted when the user clicks the back arrow above the title (returns to the main step 1 view). */
36408
+ this.back = new EventEmitter();
36409
+ /** Emitted when the user clicks an operation row. Parent patches graphQLForm and pops the picker. */
36410
+ this.operationSelected = new EventEmitter();
36411
+ /** Emitted when the user clicks Re-introspect — parent re-fetches the schema. */
36412
+ this.reIntrospect = new EventEmitter();
36413
+ /** Emitted when the user clicks Change — parent shows a URL/auth picker (deferred for now). */
36414
+ this.change = new EventEmitter();
36415
+ /** Emitted when the user clicks Preview on a row — parent shows a preview popover (deferred). */
36416
+ this.preview = new EventEmitter();
36417
+ /** Filter input value. Plain substring match across name / description / returnType. */
36418
+ this.filterTerm = '';
36419
+ }
36420
+ /** Operations currently shown after applying `filterTerm`. */
36421
+ get filteredOperations() {
36422
+ const term = this.filterTerm.trim().toLowerCase();
36423
+ if (!term)
36424
+ return this.operations || [];
36425
+ return (this.operations || []).filter((o) => {
36426
+ var _a, _b;
36427
+ return o.name.toLowerCase().includes(term) ||
36428
+ ((_a = o.description) !== null && _a !== void 0 ? _a : '').toLowerCase().includes(term) ||
36429
+ ((_b = o.returnType) !== null && _b !== void 0 ? _b : '').toLowerCase().includes(term);
36430
+ });
36431
+ }
36432
+ get queryOperations() {
36433
+ return this.filteredOperations.filter((o) => o.kind === 'query');
36434
+ }
36435
+ get mutationOperations() {
36436
+ return this.filteredOperations.filter((o) => o.kind === 'mutation');
36437
+ }
36438
+ get subscriptionOperations() {
36439
+ return this.filteredOperations.filter((o) => o.kind === 'subscription');
36440
+ }
36441
+ /** Convenience: total schema-source count line, e.g. "38 types · 4 queries · 3 mutations". */
36442
+ get headerCounts() {
36443
+ const parts = [];
36444
+ const s = this.schemaSource;
36445
+ if (!s)
36446
+ return '';
36447
+ if (s.typeCount != null)
36448
+ parts.push(`${s.typeCount} types`);
36449
+ if (s.queryCount != null)
36450
+ parts.push(`${s.queryCount} ${s.queryCount === 1 ? 'query' : 'queries'}`);
36451
+ if (s.mutationCount != null)
36452
+ parts.push(`${s.mutationCount} ${s.mutationCount === 1 ? 'mutation' : 'mutations'}`);
36453
+ if (s.subscriptionCount != null && s.subscriptionCount > 0) {
36454
+ parts.push(`${s.subscriptionCount} ${s.subscriptionCount === 1 ? 'subscription' : 'subscriptions'}`);
36455
+ }
36456
+ return parts.join(' · ');
36457
+ }
36458
+ onBack() {
36459
+ this.back.emit();
36460
+ }
36461
+ onSelect(op) {
36462
+ this.operationSelected.emit(op);
36463
+ }
36464
+ onPreview(op, event) {
36465
+ // Stop the click from also bubbling to the row's select handler — Preview is a peek,
36466
+ // not a commit.
36467
+ event.stopPropagation();
36468
+ this.preview.emit(op);
36469
+ }
36470
+ onReIntrospect() {
36471
+ this.reIntrospect.emit(this.schemaSource);
36472
+ }
36473
+ onChange() {
36474
+ this.change.emit();
36475
+ }
36476
+ onFilterChange(value) {
36477
+ this.filterTerm = value !== null && value !== void 0 ? value : '';
36478
+ }
36479
+ trackByOperation(_index, op) {
36480
+ return op.id;
36481
+ }
36482
+ /** Tailwind text colour for the return-type pill, keyed by operation kind. */
36483
+ returnTypeColorClass(kind) {
36484
+ switch (kind) {
36485
+ case 'mutation': return 'cqa-text-[#D97706]'; // amber-600
36486
+ case 'subscription': return 'cqa-text-[#7C3AED]'; // violet-600
36487
+ case 'query':
36488
+ default: return 'cqa-text-[#059669]'; // emerald-600
36489
+ }
36490
+ }
36491
+ }
36492
+ GraphQLOperationPickerComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.4.0", ngImport: i0, type: GraphQLOperationPickerComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
36493
+ GraphQLOperationPickerComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version: "13.4.0", type: GraphQLOperationPickerComponent, selector: "cqa-graphql-operation-picker", inputs: { schemaSource: "schemaSource", operations: "operations", introspected: "introspected", isLoading: "isLoading" }, outputs: { back: "back", operationSelected: "operationSelected", reIntrospect: "reIntrospect", change: "change", preview: "preview" }, host: { classAttribute: "cqa-ui-root" }, ngImport: i0, template: "<div class=\"cqa-graphql-operation-picker cqa-flex cqa-flex-col cqa-gap-4 cqa-bg-white cqa-w-full\">\n\n <!-- Title row: back arrow + heading + Introspected badge -->\n <div class=\"cqa-graphql-operation-picker-header cqa-flex cqa-items-center cqa-gap-3\">\n <div class=\"cqa-cursor-pointer cqa-text-[#6B7280] cqa-text-[14px] cqa-font-semibold\" (click)=\"onBack()\">\n <mat-icon class=\"cqa-flex cqa-items-center cqa-justify-center cqa-text-[18px] cqa-text-[#6B7280] cqa-font-semibold\" style=\"display:flex;font-size:18px;font-weight: 600;\">arrow_back</mat-icon>\n </div>\n <h3 class=\"cqa-graphql-operation-picker-title cqa-m-0 cqa-text-[18px] cqa-font-semibold cqa-text-[#111827] cqa-leading-[1.25]\">\n Pick a GraphQL operation\n </h3>\n <span *ngIf=\"introspected\"\n class=\"cqa-graphql-operation-picker-badge cqa-inline-flex cqa-items-center cqa-gap-1 cqa-px-2 cqa-py-[2px] cqa-rounded-full cqa-bg-[#EDE9FE] cqa-text-[#7C3AED] cqa-text-[11px] cqa-font-medium cqa-leading-[1.4]\">\n <span aria-hidden=\"true\">\u25C6</span> Introspected\n </span>\n </div>\n\n <!-- Subheading -->\n <p class=\"cqa-graphql-operation-picker-subheading cqa-m-0 cqa-text-[12px] cqa-text-[#6B7280] cqa-leading-[1.5]\">\n Phase 1 supports one operation per test step. Select one to auto-fill the request.\n </p>\n\n <!-- Schema source bar: URL + counts + Re-introspect / Change buttons -->\n <div class=\"cqa-graphql-operation-picker-source cqa-flex cqa-items-center cqa-justify-between cqa-gap-3 cqa-bg-[#EEF2FF] cqa-rounded-lg cqa-px-4 cqa-py-3 cqa-border cqa-border-solid cqa-border-[#E0E7FF]\">\n <div class=\"cqa-graphql-operation-picker-source-info cqa-flex cqa-flex-col cqa-gap-0.5 cqa-min-w-0\">\n <div class=\"cqa-graphql-operation-picker-source-label cqa-text-[10px] cqa-uppercase cqa-tracking-wide cqa-text-[#6B7280] cqa-font-semibold\">\n Schema source\n </div>\n <div *ngIf=\"schemaSource?.url\"\n class=\"cqa-graphql-operation-picker-source-url cqa-text-[13px] cqa-font-mono cqa-text-[#1F2937] cqa-truncate cqa-min-w-0\"\n [title]=\"schemaSource?.url\">\n {{ schemaSource?.url }}\n </div>\n <div *ngIf=\"!schemaSource?.url\"\n class=\"cqa-graphql-operation-picker-source-url cqa-text-[13px] cqa-italic cqa-text-[#6B7280]\">\n No schema loaded yet.\n </div>\n </div>\n <div class=\"cqa-graphql-operation-picker-source-meta cqa-flex cqa-items-center cqa-gap-3 cqa-shrink-0\">\n <span *ngIf=\"headerCounts\" class=\"cqa-graphql-operation-picker-source-counts cqa-text-[12px] cqa-text-[#4B5563]\">\n {{ headerCounts }}\n </span>\n <cqa-button type=\"button\" variant=\"outlined\" [icon]=\"'refresh'\" btnSize=\"md\" text=\"Re-introspect\"\n customClass=\"!cqa-bg-white !cqa-text-[#374151] !cqa-border !cqa-border-solid !cqa-border-[#D1D5DB] cqa-text-[12px] cqa-font-medium hover:!cqa-bg-[#F9FAFB]\"\n (clicked)=\"onReIntrospect()\">\n </cqa-button>\n <cqa-button type=\"button\" variant=\"text\" btnSize=\"md\" text=\"Close\"\n customClass=\"!cqa-text-[#4F46E5] cqa-text-[12px] cqa-font-medium hover:!cqa-bg-[#EEF2FF]\"\n (clicked)=\"onChange()\">\n </cqa-button>\n </div>\n </div>\n\n <!-- Filter -->\n <cqa-custom-input\n [(value)]=\"filterTerm\"\n [label]=\"''\"\n placeholder=\"Filter operations...\"\n [fullWidth]=\"true\"\n size=\"md\"\n ariaLabel=\"Filter GraphQL operations\">\n </cqa-custom-input>\n\n <!-- Loading spinner shown while the parent is introspecting. Replaces the empty state. -->\n <div *ngIf=\"isLoading\"\n class=\"cqa-graphql-operation-picker-loading cqa-flex cqa-flex-col cqa-items-center cqa-justify-center cqa-gap-3 cqa-py-12 cqa-text-center cqa-bg-[#F9FAFB] cqa-rounded-md cqa-border cqa-border-dashed cqa-border-[#E5E7EB]\">\n <span class=\"cqa-graphql-operation-picker-spinner cqa-inline-block cqa-w-7 cqa-h-7 cqa-rounded-full cqa-border-2 cqa-border-solid cqa-border-[#E5E7EB] cqa-border-t-[#4F46E5] cqa-animate-spin\" aria-hidden=\"true\"></span>\n <span class=\"cqa-text-[13px] cqa-font-medium cqa-text-[#374151]\">Loading schema\u2026</span>\n <span class=\"cqa-text-[12px] cqa-text-[#6B7280]\">Introspecting GraphQL endpoint, this may take a moment.</span>\n </div>\n\n <!-- Empty state when no operations are loaded yet (or filter rules everything out). -->\n <div *ngIf=\"!isLoading && !filteredOperations.length\"\n class=\"cqa-graphql-operation-picker-empty cqa-flex cqa-flex-col cqa-items-center cqa-justify-center cqa-gap-2 cqa-py-10 cqa-text-center cqa-bg-[#F9FAFB] cqa-rounded-md cqa-border cqa-border-dashed cqa-border-[#E5E7EB]\">\n <span class=\"cqa-text-[13px] cqa-font-medium cqa-text-[#374151]\">\n {{ filterTerm ? 'No operations match your filter.' : 'No operations available.' }}\n </span>\n <span *ngIf=\"!filterTerm\" class=\"cqa-text-[12px] cqa-text-[#6B7280]\">\n Click <strong>Re-introspect</strong> above to load the schema.\n </span>\n </div>\n\n <!-- Operation sections (Queries / Mutations / Subscriptions) -->\n <ng-container *ngIf=\"!isLoading && filteredOperations.length\">\n <ng-container *ngTemplateOutlet=\"opSection; context: { $implicit: queryOperations, label: 'Queries', accent: 'cqa-text-[#059669]' }\"></ng-container>\n <ng-container *ngTemplateOutlet=\"opSection; context: { $implicit: mutationOperations, label: 'Mutations', accent: 'cqa-text-[#D97706]' }\"></ng-container>\n <ng-container *ngTemplateOutlet=\"opSection; context: { $implicit: subscriptionOperations, label: 'Subscriptions', accent: 'cqa-text-[#7C3AED]' }\"></ng-container>\n </ng-container>\n\n <!-- Footer: selection summary. No Back / Use buttons \u2014 clicking a row commits + returns. -->\n <div *ngIf=\"!isLoading && filteredOperations.length\" class=\"cqa-graphql-operation-picker-footer cqa-flex cqa-items-center cqa-pt-2 cqa-text-[12px] cqa-text-[#6B7280]\">\n <span>Click an operation to use it \u2014 the picker closes and the request is auto-filled.</span>\n </div>\n\n <!-- \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 Section template \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\n <ng-template #opSection let-ops let-label=\"label\" let-accent=\"accent\">\n <div *ngIf=\"ops?.length\" class=\"cqa-graphql-operation-picker-section cqa-flex cqa-flex-col cqa-gap-2\">\n <div class=\"cqa-graphql-operation-picker-section-head cqa-flex cqa-items-center cqa-gap-2 cqa-bg-[#F9FAFB] cqa-px-3 cqa-py-1.5 cqa-rounded-md cqa-border cqa-border-solid cqa-border-[#F3F4F6]\">\n <span class=\"cqa-graphql-operation-picker-section-label cqa-text-[12px] cqa-font-semibold\" [ngClass]=\"accent\">{{ label }}</span>\n <span class=\"cqa-graphql-operation-picker-section-count cqa-text-[11px] cqa-text-[#6B7280]\">\n {{ ops.length }} {{ ops.length === 1 ? 'operation' : 'operations' }}\n </span>\n </div>\n <div *ngFor=\"let op of ops; trackBy: trackByOperation\"\n class=\"cqa-graphql-operation-picker-row cqa-flex cqa-items-start cqa-justify-between cqa-gap-3 cqa-px-3 cqa-py-2.5 cqa-rounded-md cqa-cursor-pointer cqa-border cqa-border-solid cqa-border-transparent hover:cqa-bg-[#F9FAFB] hover:cqa-border-[#E5E7EB] focus-within:cqa-bg-[#F5F3FF]\"\n role=\"button\"\n tabindex=\"0\"\n [attr.aria-label]=\"'Use operation ' + op.name\"\n (click)=\"onSelect(op)\"\n (keydown.enter)=\"onSelect(op)\"\n (keydown.space)=\"onSelect(op); $event.preventDefault()\">\n <span class=\"cqa-graphql-operation-picker-row-radio cqa-mt-1 cqa-shrink-0\">\n <span class=\"cqa-inline-block cqa-w-4 cqa-h-4 cqa-rounded-full cqa-border cqa-border-solid cqa-border-[#9CA3AF] cqa-bg-white\" aria-hidden=\"true\"></span>\n </span>\n <div class=\"cqa-graphql-operation-picker-row-body cqa-flex cqa-flex-col cqa-gap-0.5 cqa-flex-1 cqa-min-w-0\">\n <div class=\"cqa-graphql-operation-picker-row-signature cqa-flex cqa-flex-wrap cqa-items-baseline cqa-gap-x-1 cqa-text-[13px] cqa-font-mono cqa-leading-[1.4] cqa-min-w-0\">\n <span class=\"cqa-text-[#111827] cqa-font-semibold\">{{ op.name }}</span>\n <span class=\"cqa-text-[#6B7280]\">(</span>\n <ng-container *ngFor=\"let arg of op.args ?? []; let last = last\">\n <span class=\"cqa-text-[#374151]\">{{ arg.name }}</span>\n <span class=\"cqa-text-[#6B7280]\">:&nbsp;</span>\n <span [ngClass]=\"returnTypeColorClass(op.kind)\">{{ arg.type }}</span>\n <span *ngIf=\"!last\" class=\"cqa-text-[#6B7280]\">,&nbsp;</span>\n </ng-container>\n <span class=\"cqa-text-[#6B7280]\">)</span>\n \n </div>\n <div *ngIf=\"op.description\"\n class=\"cqa-flex cqa-items-center cqa-gap-1 cqa-graphql-operation-picker-row-description cqa-text-[12px] cqa-text-[#6B7280] cqa-leading-[1.5]\">\n <span>{{ op.description }}</span>\n <mat-icon *ngIf=\"op.returnType\" style=\"display:flex;font-size:14px;font-weight: 600;\" class=\"cqa-flex cqa-items-center cqa-justify-center cqa-text-[#6B7280] cqa-mx-1\">arrow_forward</mat-icon>\n <span *ngIf=\"op.returnType\" [ngClass]=\"returnTypeColorClass(op.kind)\" class=\"cqa-font-semibold\">{{ op.returnType }}</span>\n </div>\n </div>\n <!-- <button type=\"button\"\n class=\"cqa-graphql-operation-picker-row-preview cqa-shrink-0 cqa-inline-flex cqa-items-center cqa-gap-1 cqa-bg-transparent cqa-border-0 cqa-cursor-pointer cqa-text-[#4F46E5] cqa-text-[12px] cqa-font-medium hover:cqa-underline focus:cqa-outline-none focus-visible:cqa-ring-2 focus-visible:cqa-ring-[#A5B4FC] cqa-rounded-md cqa-px-2 cqa-py-1\"\n [attr.aria-label]=\"'Preview operation ' + op.name\"\n (click)=\"onPreview(op, $event)\">\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\">\n <path d=\"M2 12s3.5-7 10-7 10 7 10 7-3.5 7-10 7-10-7-10-7z\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/>\n <circle cx=\"12\" cy=\"12\" r=\"3\" stroke=\"currentColor\" stroke-width=\"2\"/>\n </svg>\n <span>Preview</span>\n </button> -->\n </div>\n </div>\n </ng-template>\n</div>\n", components: [{ type: i1.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { type: ButtonComponent, selector: "cqa-button", inputs: ["variant", "btnSize", "disabled", "loading", "icon", "iconPosition", "fullWidth", "iconColor", "type", "text", "customClass", "inlineStyles", "tooltip", "tooltipPosition"], outputs: ["clicked"] }, { type: CustomInputComponent, selector: "cqa-custom-input", inputs: ["inputId", "label", "type", "placeholder", "value", "disabled", "errors", "required", "ariaLabel", "size", "fullWidth", "maxLength", "showCharCount", "inputInlineStyle", "labelInlineStyle"], outputs: ["valueChange", "blurred", "focused", "enterPressed"] }], directives: [{ type: i2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { type: i2.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet"] }, { type: i2.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { type: i2.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }], changeDetection: i0.ChangeDetectionStrategy.Default });
36494
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.4.0", ngImport: i0, type: GraphQLOperationPickerComponent, decorators: [{
36495
+ type: Component,
36496
+ args: [{ selector: 'cqa-graphql-operation-picker', host: { class: 'cqa-ui-root' }, changeDetection: ChangeDetectionStrategy.Default, template: "<div class=\"cqa-graphql-operation-picker cqa-flex cqa-flex-col cqa-gap-4 cqa-bg-white cqa-w-full\">\n\n <!-- Title row: back arrow + heading + Introspected badge -->\n <div class=\"cqa-graphql-operation-picker-header cqa-flex cqa-items-center cqa-gap-3\">\n <div class=\"cqa-cursor-pointer cqa-text-[#6B7280] cqa-text-[14px] cqa-font-semibold\" (click)=\"onBack()\">\n <mat-icon class=\"cqa-flex cqa-items-center cqa-justify-center cqa-text-[18px] cqa-text-[#6B7280] cqa-font-semibold\" style=\"display:flex;font-size:18px;font-weight: 600;\">arrow_back</mat-icon>\n </div>\n <h3 class=\"cqa-graphql-operation-picker-title cqa-m-0 cqa-text-[18px] cqa-font-semibold cqa-text-[#111827] cqa-leading-[1.25]\">\n Pick a GraphQL operation\n </h3>\n <span *ngIf=\"introspected\"\n class=\"cqa-graphql-operation-picker-badge cqa-inline-flex cqa-items-center cqa-gap-1 cqa-px-2 cqa-py-[2px] cqa-rounded-full cqa-bg-[#EDE9FE] cqa-text-[#7C3AED] cqa-text-[11px] cqa-font-medium cqa-leading-[1.4]\">\n <span aria-hidden=\"true\">\u25C6</span> Introspected\n </span>\n </div>\n\n <!-- Subheading -->\n <p class=\"cqa-graphql-operation-picker-subheading cqa-m-0 cqa-text-[12px] cqa-text-[#6B7280] cqa-leading-[1.5]\">\n Phase 1 supports one operation per test step. Select one to auto-fill the request.\n </p>\n\n <!-- Schema source bar: URL + counts + Re-introspect / Change buttons -->\n <div class=\"cqa-graphql-operation-picker-source cqa-flex cqa-items-center cqa-justify-between cqa-gap-3 cqa-bg-[#EEF2FF] cqa-rounded-lg cqa-px-4 cqa-py-3 cqa-border cqa-border-solid cqa-border-[#E0E7FF]\">\n <div class=\"cqa-graphql-operation-picker-source-info cqa-flex cqa-flex-col cqa-gap-0.5 cqa-min-w-0\">\n <div class=\"cqa-graphql-operation-picker-source-label cqa-text-[10px] cqa-uppercase cqa-tracking-wide cqa-text-[#6B7280] cqa-font-semibold\">\n Schema source\n </div>\n <div *ngIf=\"schemaSource?.url\"\n class=\"cqa-graphql-operation-picker-source-url cqa-text-[13px] cqa-font-mono cqa-text-[#1F2937] cqa-truncate cqa-min-w-0\"\n [title]=\"schemaSource?.url\">\n {{ schemaSource?.url }}\n </div>\n <div *ngIf=\"!schemaSource?.url\"\n class=\"cqa-graphql-operation-picker-source-url cqa-text-[13px] cqa-italic cqa-text-[#6B7280]\">\n No schema loaded yet.\n </div>\n </div>\n <div class=\"cqa-graphql-operation-picker-source-meta cqa-flex cqa-items-center cqa-gap-3 cqa-shrink-0\">\n <span *ngIf=\"headerCounts\" class=\"cqa-graphql-operation-picker-source-counts cqa-text-[12px] cqa-text-[#4B5563]\">\n {{ headerCounts }}\n </span>\n <cqa-button type=\"button\" variant=\"outlined\" [icon]=\"'refresh'\" btnSize=\"md\" text=\"Re-introspect\"\n customClass=\"!cqa-bg-white !cqa-text-[#374151] !cqa-border !cqa-border-solid !cqa-border-[#D1D5DB] cqa-text-[12px] cqa-font-medium hover:!cqa-bg-[#F9FAFB]\"\n (clicked)=\"onReIntrospect()\">\n </cqa-button>\n <cqa-button type=\"button\" variant=\"text\" btnSize=\"md\" text=\"Close\"\n customClass=\"!cqa-text-[#4F46E5] cqa-text-[12px] cqa-font-medium hover:!cqa-bg-[#EEF2FF]\"\n (clicked)=\"onChange()\">\n </cqa-button>\n </div>\n </div>\n\n <!-- Filter -->\n <cqa-custom-input\n [(value)]=\"filterTerm\"\n [label]=\"''\"\n placeholder=\"Filter operations...\"\n [fullWidth]=\"true\"\n size=\"md\"\n ariaLabel=\"Filter GraphQL operations\">\n </cqa-custom-input>\n\n <!-- Loading spinner shown while the parent is introspecting. Replaces the empty state. -->\n <div *ngIf=\"isLoading\"\n class=\"cqa-graphql-operation-picker-loading cqa-flex cqa-flex-col cqa-items-center cqa-justify-center cqa-gap-3 cqa-py-12 cqa-text-center cqa-bg-[#F9FAFB] cqa-rounded-md cqa-border cqa-border-dashed cqa-border-[#E5E7EB]\">\n <span class=\"cqa-graphql-operation-picker-spinner cqa-inline-block cqa-w-7 cqa-h-7 cqa-rounded-full cqa-border-2 cqa-border-solid cqa-border-[#E5E7EB] cqa-border-t-[#4F46E5] cqa-animate-spin\" aria-hidden=\"true\"></span>\n <span class=\"cqa-text-[13px] cqa-font-medium cqa-text-[#374151]\">Loading schema\u2026</span>\n <span class=\"cqa-text-[12px] cqa-text-[#6B7280]\">Introspecting GraphQL endpoint, this may take a moment.</span>\n </div>\n\n <!-- Empty state when no operations are loaded yet (or filter rules everything out). -->\n <div *ngIf=\"!isLoading && !filteredOperations.length\"\n class=\"cqa-graphql-operation-picker-empty cqa-flex cqa-flex-col cqa-items-center cqa-justify-center cqa-gap-2 cqa-py-10 cqa-text-center cqa-bg-[#F9FAFB] cqa-rounded-md cqa-border cqa-border-dashed cqa-border-[#E5E7EB]\">\n <span class=\"cqa-text-[13px] cqa-font-medium cqa-text-[#374151]\">\n {{ filterTerm ? 'No operations match your filter.' : 'No operations available.' }}\n </span>\n <span *ngIf=\"!filterTerm\" class=\"cqa-text-[12px] cqa-text-[#6B7280]\">\n Click <strong>Re-introspect</strong> above to load the schema.\n </span>\n </div>\n\n <!-- Operation sections (Queries / Mutations / Subscriptions) -->\n <ng-container *ngIf=\"!isLoading && filteredOperations.length\">\n <ng-container *ngTemplateOutlet=\"opSection; context: { $implicit: queryOperations, label: 'Queries', accent: 'cqa-text-[#059669]' }\"></ng-container>\n <ng-container *ngTemplateOutlet=\"opSection; context: { $implicit: mutationOperations, label: 'Mutations', accent: 'cqa-text-[#D97706]' }\"></ng-container>\n <ng-container *ngTemplateOutlet=\"opSection; context: { $implicit: subscriptionOperations, label: 'Subscriptions', accent: 'cqa-text-[#7C3AED]' }\"></ng-container>\n </ng-container>\n\n <!-- Footer: selection summary. No Back / Use buttons \u2014 clicking a row commits + returns. -->\n <div *ngIf=\"!isLoading && filteredOperations.length\" class=\"cqa-graphql-operation-picker-footer cqa-flex cqa-items-center cqa-pt-2 cqa-text-[12px] cqa-text-[#6B7280]\">\n <span>Click an operation to use it \u2014 the picker closes and the request is auto-filled.</span>\n </div>\n\n <!-- \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 Section template \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\n <ng-template #opSection let-ops let-label=\"label\" let-accent=\"accent\">\n <div *ngIf=\"ops?.length\" class=\"cqa-graphql-operation-picker-section cqa-flex cqa-flex-col cqa-gap-2\">\n <div class=\"cqa-graphql-operation-picker-section-head cqa-flex cqa-items-center cqa-gap-2 cqa-bg-[#F9FAFB] cqa-px-3 cqa-py-1.5 cqa-rounded-md cqa-border cqa-border-solid cqa-border-[#F3F4F6]\">\n <span class=\"cqa-graphql-operation-picker-section-label cqa-text-[12px] cqa-font-semibold\" [ngClass]=\"accent\">{{ label }}</span>\n <span class=\"cqa-graphql-operation-picker-section-count cqa-text-[11px] cqa-text-[#6B7280]\">\n {{ ops.length }} {{ ops.length === 1 ? 'operation' : 'operations' }}\n </span>\n </div>\n <div *ngFor=\"let op of ops; trackBy: trackByOperation\"\n class=\"cqa-graphql-operation-picker-row cqa-flex cqa-items-start cqa-justify-between cqa-gap-3 cqa-px-3 cqa-py-2.5 cqa-rounded-md cqa-cursor-pointer cqa-border cqa-border-solid cqa-border-transparent hover:cqa-bg-[#F9FAFB] hover:cqa-border-[#E5E7EB] focus-within:cqa-bg-[#F5F3FF]\"\n role=\"button\"\n tabindex=\"0\"\n [attr.aria-label]=\"'Use operation ' + op.name\"\n (click)=\"onSelect(op)\"\n (keydown.enter)=\"onSelect(op)\"\n (keydown.space)=\"onSelect(op); $event.preventDefault()\">\n <span class=\"cqa-graphql-operation-picker-row-radio cqa-mt-1 cqa-shrink-0\">\n <span class=\"cqa-inline-block cqa-w-4 cqa-h-4 cqa-rounded-full cqa-border cqa-border-solid cqa-border-[#9CA3AF] cqa-bg-white\" aria-hidden=\"true\"></span>\n </span>\n <div class=\"cqa-graphql-operation-picker-row-body cqa-flex cqa-flex-col cqa-gap-0.5 cqa-flex-1 cqa-min-w-0\">\n <div class=\"cqa-graphql-operation-picker-row-signature cqa-flex cqa-flex-wrap cqa-items-baseline cqa-gap-x-1 cqa-text-[13px] cqa-font-mono cqa-leading-[1.4] cqa-min-w-0\">\n <span class=\"cqa-text-[#111827] cqa-font-semibold\">{{ op.name }}</span>\n <span class=\"cqa-text-[#6B7280]\">(</span>\n <ng-container *ngFor=\"let arg of op.args ?? []; let last = last\">\n <span class=\"cqa-text-[#374151]\">{{ arg.name }}</span>\n <span class=\"cqa-text-[#6B7280]\">:&nbsp;</span>\n <span [ngClass]=\"returnTypeColorClass(op.kind)\">{{ arg.type }}</span>\n <span *ngIf=\"!last\" class=\"cqa-text-[#6B7280]\">,&nbsp;</span>\n </ng-container>\n <span class=\"cqa-text-[#6B7280]\">)</span>\n \n </div>\n <div *ngIf=\"op.description\"\n class=\"cqa-flex cqa-items-center cqa-gap-1 cqa-graphql-operation-picker-row-description cqa-text-[12px] cqa-text-[#6B7280] cqa-leading-[1.5]\">\n <span>{{ op.description }}</span>\n <mat-icon *ngIf=\"op.returnType\" style=\"display:flex;font-size:14px;font-weight: 600;\" class=\"cqa-flex cqa-items-center cqa-justify-center cqa-text-[#6B7280] cqa-mx-1\">arrow_forward</mat-icon>\n <span *ngIf=\"op.returnType\" [ngClass]=\"returnTypeColorClass(op.kind)\" class=\"cqa-font-semibold\">{{ op.returnType }}</span>\n </div>\n </div>\n <!-- <button type=\"button\"\n class=\"cqa-graphql-operation-picker-row-preview cqa-shrink-0 cqa-inline-flex cqa-items-center cqa-gap-1 cqa-bg-transparent cqa-border-0 cqa-cursor-pointer cqa-text-[#4F46E5] cqa-text-[12px] cqa-font-medium hover:cqa-underline focus:cqa-outline-none focus-visible:cqa-ring-2 focus-visible:cqa-ring-[#A5B4FC] cqa-rounded-md cqa-px-2 cqa-py-1\"\n [attr.aria-label]=\"'Preview operation ' + op.name\"\n (click)=\"onPreview(op, $event)\">\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\">\n <path d=\"M2 12s3.5-7 10-7 10 7 10 7-3.5 7-10 7-10-7-10-7z\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/>\n <circle cx=\"12\" cy=\"12\" r=\"3\" stroke=\"currentColor\" stroke-width=\"2\"/>\n </svg>\n <span>Preview</span>\n </button> -->\n </div>\n </div>\n </ng-template>\n</div>\n" }]
36497
+ }], propDecorators: { schemaSource: [{
36498
+ type: Input
36499
+ }], operations: [{
36500
+ type: Input
36501
+ }], introspected: [{
36502
+ type: Input
36503
+ }], isLoading: [{
36504
+ type: Input
36505
+ }], back: [{
36506
+ type: Output
36507
+ }], operationSelected: [{
36508
+ type: Output
36509
+ }], reIntrospect: [{
36510
+ type: Output
36511
+ }], change: [{
36512
+ type: Output
36513
+ }], preview: [{
36514
+ type: Output
36515
+ }] } });
36516
+
36374
36517
  class AdvancedVariablesFormComponent {
36375
36518
  constructor() {
36376
36519
  this.advancedVariables = [];
@@ -36867,11 +37010,16 @@ function parseCurl(curlString) {
36867
37010
  return { method: finalMethod, url, headers, body, formData };
36868
37011
  }
36869
37012
  class ApiEditStepComponent {
36870
- constructor(fb, cdr) {
37013
+ constructor(fb, cdr, dialogService) {
36871
37014
  this.fb = fb;
36872
37015
  this.cdr = cdr;
37016
+ this.dialogService = dialogService;
36873
37017
  /** True while parent is creating the step (API in progress); show loader on Create/Update button */
36874
37018
  this.isCreatingStep = false;
37019
+ /** Operations list for the picker. Empty until parent loads/introspects the schema. */
37020
+ this.graphQLOperations = [];
37021
+ /** True while the parent is introspecting; the picker swaps its empty state for a spinner. */
37022
+ this.isLoadingGraphQLSchema = false;
36875
37023
  /** Emits the cURL string when user clicks Import (value from the textarea control). */
36876
37024
  this.importCurl = new EventEmitter();
36877
37025
  /** Emits when user cancels the Import cURL panel (clicks Cancel). */
@@ -36933,6 +37081,8 @@ class ApiEditStepComponent {
36933
37081
  this.environmentSearch = new EventEmitter();
36934
37082
  /** Emit when user scrolls to load more environments. */
36935
37083
  this.environmentLoadMore = new EventEmitter();
37084
+ /** Emit when user clicks "Load Schema" in GraphQL mode. Parent handles the schema fetch (or shows a "coming soon" toast for now). */
37085
+ this.loadSchema = new EventEmitter();
36936
37086
  /** Auth type options: array of strings or objects with id, name, value, label (passed from parent). Falls back to built-in list when empty. */
36937
37087
  this.authTypeOptions = [];
36938
37088
  /** Config for Auth Type dropdown (updated when authTypeOptions changes, like environment) */
@@ -36963,13 +37113,23 @@ class ApiEditStepComponent {
36963
37113
  this._url = '';
36964
37114
  /** When true, params form changes should not update the URL (we're syncing from URL). */
36965
37115
  this.paramsSyncingFromUrl = false;
36966
- this.payloadTabs = [
36967
- { value: 'authorization', label: 'Authorization' },
36968
- { value: 'headers', label: 'Headers' },
36969
- { value: 'body', label: 'Body' },
36970
- { value: 'params', label: 'Params' },
36971
- ];
36972
37116
  this.activePayloadTab = 'headers';
37117
+ /** Currently selected API type. REST is the default and matches today's behaviour exactly. */
37118
+ this.activeApiType = 'rest';
37119
+ /** Segments rendered above the Method/URL row. SOAP/WebSocket/gRPC are intentionally `disabled` for now. */
37120
+ this.apiTypeSegments = [
37121
+ { label: 'REST API', value: 'rest' },
37122
+ { label: 'GraphQL', value: 'graphql' },
37123
+ { label: 'SOAP', value: 'soap', disabled: true, tooltip: 'Coming soon' },
37124
+ { label: 'WebSocket', value: 'websocket', disabled: true, tooltip: 'Coming soon' },
37125
+ { label: 'gRPC', value: 'grpc', disabled: true, tooltip: 'Coming soon' },
37126
+ ];
37127
+ /** Inline error displayed under the Query textarea when empty/blank on Save. Cleared as the user types. */
37128
+ this.graphQLQueryError = '';
37129
+ /** Inline error displayed under the Variables textarea when JSON is malformed. */
37130
+ this.graphQLVariablesError = '';
37131
+ /** Non-blocking warning shown under the Operation Name field when the query declares 2+ operations. */
37132
+ this.graphQLOperationWarning = '';
36973
37133
  /** Step 3: Response verification tabs */
36974
37134
  this.responseVerificationTabs = [
36975
37135
  { value: 'response-body', label: 'Response Body' },
@@ -37063,6 +37223,10 @@ class ApiEditStepComponent {
37063
37223
  this.advancedExpanded = true;
37064
37224
  this.advancedSettingsVariables = [];
37065
37225
  this.advancedVariablesForm = new FormArray([]);
37226
+ /** Per-API-type state snapshots. Populated when the user switches AWAY from a
37227
+ * mode so a later switch back restores everything they typed. Undefined for a
37228
+ * mode means "never visited" — the restore path resets to empty defaults. */
37229
+ this.snapshotByType = {};
37066
37230
  }
37067
37231
  /** The label for the current step only (used when showing a single step in the indicator). */
37068
37232
  get activeStepLabel() {
@@ -37287,6 +37451,12 @@ class ApiEditStepComponent {
37287
37451
  var _a, _c;
37288
37452
  return ((_c = (_a = this.urlOptions) === null || _a === void 0 ? void 0 : _a.length) !== null && _c !== void 0 ? _c : 0) > 0 ? 'Enter URL or select Parameter *' : 'Enter URL *';
37289
37453
  }
37454
+ /** Tabs visible under the request row, swapped based on `activeApiType`. */
37455
+ get payloadTabs() {
37456
+ return this.activeApiType === 'graphql'
37457
+ ? ApiEditStepComponent.GRAPHQL_PAYLOAD_TABS
37458
+ : ApiEditStepComponent.REST_PAYLOAD_TABS;
37459
+ }
37290
37460
  onPayloadTypeChange(val) {
37291
37461
  this.payloadType = val;
37292
37462
  }
@@ -37540,7 +37710,7 @@ class ApiEditStepComponent {
37540
37710
  return values.length > 0 ? values[0] : 'equals';
37541
37711
  }
37542
37712
  ngOnInit() {
37543
- var _a, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z, _0;
37713
+ var _a, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20;
37544
37714
  this.applyInitialStep(this.initialStep);
37545
37715
  this.updateMethodSelectConfig();
37546
37716
  this.updateHeaderNameSelectConfig();
@@ -37567,23 +37737,23 @@ class ApiEditStepComponent {
37567
37737
  authType: [defaultAuthType],
37568
37738
  });
37569
37739
  this.oauth2Form = this.fb.group({
37570
- grantType: ['client_credentials'],
37571
- accessTokenUrl: [''],
37572
- clientId: [''],
37573
- clientSecret: [''],
37574
- scope: [''],
37575
- clientAuthentication: ['basic'],
37576
- });
37577
- this.environmentFormChangesSub = (_o = (_m = this.environmentForm.get('environment')) === null || _m === void 0 ? void 0 : _m.valueChanges) === null || _o === void 0 ? void 0 : _o.subscribe((v) => {
37740
+ grantType: [(_o = (_m = this.initialOAuth2) === null || _m === void 0 ? void 0 : _m.grantType) !== null && _o !== void 0 ? _o : 'client_credentials'],
37741
+ accessTokenUrl: [(_q = (_p = this.initialOAuth2) === null || _p === void 0 ? void 0 : _p.accessTokenUrl) !== null && _q !== void 0 ? _q : ''],
37742
+ clientId: [(_s = (_r = this.initialOAuth2) === null || _r === void 0 ? void 0 : _r.clientId) !== null && _s !== void 0 ? _s : ''],
37743
+ clientSecret: [(_u = (_t = this.initialOAuth2) === null || _t === void 0 ? void 0 : _t.clientSecret) !== null && _u !== void 0 ? _u : ''],
37744
+ scope: [(_w = (_v = this.initialOAuth2) === null || _v === void 0 ? void 0 : _v.scope) !== null && _w !== void 0 ? _w : ''],
37745
+ clientAuthentication: [(_y = (_x = this.initialOAuth2) === null || _x === void 0 ? void 0 : _x.clientAuthentication) !== null && _y !== void 0 ? _y : 'basic'],
37746
+ });
37747
+ this.environmentFormChangesSub = (_0 = (_z = this.environmentForm.get('environment')) === null || _z === void 0 ? void 0 : _z.valueChanges) === null || _0 === void 0 ? void 0 : _0.subscribe((v) => {
37578
37748
  const s = v != null && v !== '' ? (typeof v === 'object' ? ApiEditStepComponent.getOptionValue(v) : String(v)) : '';
37579
37749
  this.currentEnvironmentValue = s !== null && s !== void 0 ? s : '';
37580
37750
  });
37581
- this.methodFormChangesSub = (_q = (_p = this.methodForm.get('method')) === null || _p === void 0 ? void 0 : _p.valueChanges) === null || _q === void 0 ? void 0 : _q.subscribe((v) => {
37751
+ this.methodFormChangesSub = (_2 = (_1 = this.methodForm.get('method')) === null || _1 === void 0 ? void 0 : _1.valueChanges) === null || _2 === void 0 ? void 0 : _2.subscribe((v) => {
37582
37752
  const s = v != null && v !== '' ? (typeof v === 'object' ? ApiEditStepComponent.getOptionValue(v) : String(v)) : '';
37583
37753
  this.currentMethodValue = s !== null && s !== void 0 ? s : '';
37584
37754
  });
37585
- this.buildHeadersFormArray((_r = this.initialHeaders) !== null && _r !== void 0 ? _r : [{ name: '', type: 'string', value: '' }]);
37586
- this.headerNameOptionsChangesSub = (_t = (_s = this.headersFormArray) === null || _s === void 0 ? void 0 : _s.valueChanges) === null || _t === void 0 ? void 0 : _t.subscribe(() => {
37755
+ this.buildHeadersFormArray((_3 = this.initialHeaders) !== null && _3 !== void 0 ? _3 : [{ name: '', type: 'string', value: '' }]);
37756
+ this.headerNameOptionsChangesSub = (_5 = (_4 = this.headersFormArray) === null || _4 === void 0 ? void 0 : _4.valueChanges) === null || _5 === void 0 ? void 0 : _5.subscribe(() => {
37587
37757
  this.updateHeaderNameSelectConfig();
37588
37758
  this.headersChange.emit(this.headers);
37589
37759
  });
@@ -37592,7 +37762,7 @@ class ApiEditStepComponent {
37592
37762
  this.updateEnvironmentSelectConfig();
37593
37763
  this.updatePayloadFormatSelectConfig();
37594
37764
  const firstFormatValue = this.getFormatValues()[0];
37595
- const defaultFormat = (_v = (_u = this.initialFormat) !== null && _u !== void 0 ? _u : firstFormatValue) !== null && _v !== void 0 ? _v : 'json';
37765
+ const defaultFormat = (_7 = (_6 = this.initialFormat) !== null && _6 !== void 0 ? _6 : firstFormatValue) !== null && _7 !== void 0 ? _7 : 'json';
37596
37766
  this.payloadFormatForm = this.fb.group({ format: [defaultFormat] });
37597
37767
  // Initialize payload type and text from inputs when editing
37598
37768
  if (this.initialPayloadType) {
@@ -37604,7 +37774,7 @@ class ApiEditStepComponent {
37604
37774
  this.buildKeyValueFormArray();
37605
37775
  this.buildKeyTypeValueFormArray();
37606
37776
  this.buildParamsFormArray();
37607
- this.paramsFormArrayChangesSub = (_w = this.paramsFormArray.valueChanges) === null || _w === void 0 ? void 0 : _w.subscribe(() => this.syncUrlFromParams());
37777
+ this.paramsFormArrayChangesSub = (_8 = this.paramsFormArray.valueChanges) === null || _8 === void 0 ? void 0 : _8.subscribe(() => this.syncUrlFromParams());
37608
37778
  if (this.initialUrl != null && this.initialUrl !== '') {
37609
37779
  this._url = ApiEditStepComponent.decodeUrlForDisplay(this.initialUrl);
37610
37780
  this.syncParamsFromUrl();
@@ -37629,14 +37799,14 @@ class ApiEditStepComponent {
37629
37799
  ? this.initialStatusVerifications
37630
37800
  : undefined;
37631
37801
  this.buildStatusVerificationFormArray(statusRows);
37632
- this.methodChangesSub = (_y = (_x = this.methodForm.get('method')) === null || _x === void 0 ? void 0 : _x.valueChanges) === null || _y === void 0 ? void 0 : _y.subscribe((value) => {
37802
+ this.methodChangesSub = (_10 = (_9 = this.methodForm.get('method')) === null || _9 === void 0 ? void 0 : _9.valueChanges) === null || _10 === void 0 ? void 0 : _10.subscribe((value) => {
37633
37803
  const method = value != null ? String(value).toUpperCase() : '';
37634
37804
  if (METHODS_WITHOUT_BODY.includes(method) && this.activePayloadTab !== 'headers') {
37635
37805
  this.activePayloadTab = 'headers';
37636
37806
  this.cdr.detectChanges();
37637
37807
  }
37638
37808
  });
37639
- this.formatChangesSub = (_0 = (_z = this.payloadFormatForm.get('format')) === null || _z === void 0 ? void 0 : _z.valueChanges) === null || _0 === void 0 ? void 0 : _0.subscribe(() => {
37809
+ this.formatChangesSub = (_12 = (_11 = this.payloadFormatForm.get('format')) === null || _11 === void 0 ? void 0 : _11.valueChanges) === null || _12 === void 0 ? void 0 : _12.subscribe(() => {
37640
37810
  this.validatePayloadJson();
37641
37811
  });
37642
37812
  this.validatePayloadJson();
@@ -37648,6 +37818,50 @@ class ApiEditStepComponent {
37648
37818
  if (this.advancedSettingsVariables && this.advancedSettingsVariables.length > 0) {
37649
37819
  this.buildAdvancedVariablesForm();
37650
37820
  }
37821
+ // GraphQL form + initial state. Hydrate from `initialApiType` / `initialGraphQL` if the parent
37822
+ // is editing a GraphQL step. Variables come in as either an object (preferred) or a JSON string —
37823
+ // we always store the form value as a pretty-printed JSON string so the textarea round-trips cleanly.
37824
+ this.activeApiType = this.initialApiType === 'graphql' ? 'graphql' : 'rest';
37825
+ const initialQuery = (_14 = (_13 = this.initialGraphQL) === null || _13 === void 0 ? void 0 : _13.query) !== null && _14 !== void 0 ? _14 : '';
37826
+ const initialVarsRaw = (_15 = this.initialGraphQL) === null || _15 === void 0 ? void 0 : _15.variables;
37827
+ let initialVariablesString = '';
37828
+ if (initialVarsRaw != null) {
37829
+ if (typeof initialVarsRaw === 'string') {
37830
+ initialVariablesString = initialVarsRaw;
37831
+ }
37832
+ else {
37833
+ try {
37834
+ initialVariablesString = JSON.stringify(initialVarsRaw, null, 2);
37835
+ }
37836
+ catch (_21) {
37837
+ initialVariablesString = '';
37838
+ }
37839
+ }
37840
+ }
37841
+ const initialOperationName = (_17 = (_16 = this.initialGraphQL) === null || _16 === void 0 ? void 0 : _16.operationName) !== null && _17 !== void 0 ? _17 : '';
37842
+ this.graphQLForm = this.fb.group({
37843
+ query: [initialQuery],
37844
+ variables: [initialVariablesString],
37845
+ operationName: [initialOperationName],
37846
+ });
37847
+ // Re-validate as the user types so inline errors clear/re-render in real time.
37848
+ (_18 = this.graphQLForm.get('query')) === null || _18 === void 0 ? void 0 : _18.valueChanges.subscribe(() => {
37849
+ if (this.graphQLQueryError)
37850
+ this.graphQLQueryError = '';
37851
+ this.recomputeGraphQLOperationWarning();
37852
+ });
37853
+ (_19 = this.graphQLForm.get('variables')) === null || _19 === void 0 ? void 0 : _19.valueChanges.subscribe(() => {
37854
+ this.validateGraphQLVariables(false);
37855
+ });
37856
+ (_20 = this.graphQLForm.get('operationName')) === null || _20 === void 0 ? void 0 : _20.valueChanges.subscribe(() => {
37857
+ this.recomputeGraphQLOperationWarning();
37858
+ });
37859
+ // Default the active tab to "graphql" when the user is editing a GraphQL step,
37860
+ // so the Query / Variables / Operation Name fields are visible immediately.
37861
+ if (this.activeApiType === 'graphql') {
37862
+ this.activePayloadTab = 'graphql';
37863
+ }
37864
+ this.recomputeGraphQLOperationWarning();
37651
37865
  }
37652
37866
  ngAfterViewInit() {
37653
37867
  this.setupPayloadTextareaScrollSync();
@@ -38151,6 +38365,23 @@ class ApiEditStepComponent {
38151
38365
  return false;
38152
38366
  }
38153
38367
  }
38368
+ /**
38369
+ * Aggregate disabled state for the Send Request button. URL validity is
38370
+ * always required; in GraphQL mode the Query field must also be non-blank
38371
+ * (without it the request body would be `{ query: "" }`, which the engine
38372
+ * rejects up-front anyway).
38373
+ */
38374
+ get isSendRequestDisabled() {
38375
+ var _a, _c, _d;
38376
+ if (!this.isUrlValid)
38377
+ return true;
38378
+ if (this.activeApiType === 'graphql') {
38379
+ const query = ((_d = (_c = (_a = this.graphQLForm) === null || _a === void 0 ? void 0 : _a.get('query')) === null || _c === void 0 ? void 0 : _c.value) !== null && _d !== void 0 ? _d : '').trim();
38380
+ if (!query)
38381
+ return true;
38382
+ }
38383
+ return false;
38384
+ }
38154
38385
  onSendRequest() {
38155
38386
  var _a, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m;
38156
38387
  // Validate URL before sending request
@@ -38162,21 +38393,7 @@ class ApiEditStepComponent {
38162
38393
  const method = this.selectedMethod != null ? String(this.selectedMethod) : '';
38163
38394
  try {
38164
38395
  const step1 = this.getCreatePayload().step1;
38165
- this.sendRequest.emit({
38166
- environment: (_a = step1.environment) !== null && _a !== void 0 ? _a : environment,
38167
- method: (_c = step1.method) !== null && _c !== void 0 ? _c : method,
38168
- url: (_d = step1.url) !== null && _d !== void 0 ? _d : ((_e = this.url) !== null && _e !== void 0 ? _e : ''),
38169
- headers: (_f = step1.headers) !== null && _f !== void 0 ? _f : this.headers,
38170
- authType: step1.authType,
38171
- bearerToken: step1.bearerToken,
38172
- oauth2: step1.oauth2,
38173
- payloadType: step1.payloadType,
38174
- payloadFormat: step1.payloadFormat,
38175
- payloadText: step1.payloadText,
38176
- keyValueRows: step1.keyValueRows,
38177
- keyTypeValueRows: step1.keyTypeValueRows,
38178
- paramsRows: step1.paramsRows,
38179
- });
38396
+ this.sendRequest.emit(Object.assign(Object.assign({ environment: (_a = step1.environment) !== null && _a !== void 0 ? _a : environment, method: (_c = step1.method) !== null && _c !== void 0 ? _c : method, url: (_d = step1.url) !== null && _d !== void 0 ? _d : ((_e = this.url) !== null && _e !== void 0 ? _e : ''), headers: (_f = step1.headers) !== null && _f !== void 0 ? _f : this.headers, authType: step1.authType, bearerToken: step1.bearerToken, oauth2: step1.oauth2, payloadType: step1.payloadType, payloadFormat: step1.payloadFormat, payloadText: step1.payloadText, keyValueRows: step1.keyValueRows, keyTypeValueRows: step1.keyTypeValueRows, paramsRows: step1.paramsRows }, (step1.apiType && { apiType: step1.apiType })), (step1.graphQL && { graphQL: step1.graphQL })));
38180
38397
  }
38181
38398
  catch (_o) {
38182
38399
  this.sendRequest.emit({
@@ -38210,16 +38427,28 @@ class ApiEditStepComponent {
38210
38427
  this.onBack();
38211
38428
  }
38212
38429
  onNext() {
38213
- var _a;
38430
+ var _a, _c, _d, _e;
38214
38431
  // Validate URL on step 1
38215
38432
  if (this.currentStep === 1) {
38216
38433
  if (!this.validateUrl()) {
38217
38434
  this.cdr.markForCheck();
38218
38435
  return;
38219
38436
  }
38437
+ // GraphQL step 1 also requires a non-empty Query; mirror the final-step
38438
+ // check in `onCreate` so the user can't reach step 2 with an empty query
38439
+ // and only discover it on submit.
38440
+ if (this.activeApiType === 'graphql') {
38441
+ const query = ((_d = (_c = (_a = this.graphQLForm) === null || _a === void 0 ? void 0 : _a.get('query')) === null || _c === void 0 ? void 0 : _c.value) !== null && _d !== void 0 ? _d : '').trim();
38442
+ if (!query) {
38443
+ this.graphQLQueryError = 'Query is required.';
38444
+ this.activePayloadTab = 'graphql';
38445
+ this.cdr.markForCheck();
38446
+ return;
38447
+ }
38448
+ }
38220
38449
  }
38221
38450
  // Validate variable name on step 2
38222
- if (this.currentStep === 2 && !((_a = this.variableName) === null || _a === void 0 ? void 0 : _a.trim())) {
38451
+ if (this.currentStep === 2 && !((_e = this.variableName) === null || _e === void 0 ? void 0 : _e.trim())) {
38223
38452
  this.variableNameError = 'Variable Name is required.';
38224
38453
  return;
38225
38454
  }
@@ -38234,6 +38463,277 @@ class ApiEditStepComponent {
38234
38463
  setPayloadTab(value) {
38235
38464
  this.activePayloadTab = value;
38236
38465
  }
38466
+ /**
38467
+ * Handle API Type segment toggle (REST ↔ GraphQL). When switching to GraphQL we
38468
+ * jump to the GraphQL tab so the Query / Variables / Operation Name fields are
38469
+ * immediately visible. When switching back to REST we land on Headers (REST default).
38470
+ * Disabled segments (SOAP/WebSocket/gRPC) cannot fire here — `cqa-segment-control`
38471
+ * blocks the click — but we guard anyway in case a future caller bypasses that.
38472
+ */
38473
+ onApiTypeChange(value) {
38474
+ if (value !== 'rest' && value !== 'graphql')
38475
+ return;
38476
+ if (this.activeApiType === value)
38477
+ return;
38478
+ const previousType = this.activeApiType;
38479
+ const nextType = value;
38480
+ // Snapshot the current mode's full state so a switch back restores it. Each
38481
+ // mode keeps its OWN url / headers / auth / body / params / graphQL — bleed
38482
+ // between modes is the bug this isolates.
38483
+ this.snapshotByType[previousType] = this.captureCurrentApiTypeSnapshot();
38484
+ this.activeApiType = nextType;
38485
+ // Restore the target mode's saved state, or reset to a clean slate if the
38486
+ // user has never visited that mode this session.
38487
+ this.applyApiTypeSnapshot(this.snapshotByType[nextType]);
38488
+ if (this.activeApiType === 'graphql') {
38489
+ this.activePayloadTab = 'graphql';
38490
+ // Re-validate variables on first switch — the textarea may already have content
38491
+ // from a previous edit and we want the inline error to surface immediately.
38492
+ this.validateGraphQLVariables(false);
38493
+ this.recomputeGraphQLOperationWarning();
38494
+ }
38495
+ else {
38496
+ // Returning to REST — drop any GraphQL-only inline state so it doesn't linger.
38497
+ this.graphQLQueryError = '';
38498
+ this.graphQLVariablesError = '';
38499
+ this.graphQLOperationWarning = '';
38500
+ this.activePayloadTab = 'headers';
38501
+ }
38502
+ this.cdr.markForCheck();
38503
+ }
38504
+ /** Read every user-editable field for the currently-active API type into a
38505
+ * plain snapshot. Mirrors {@link applyApiTypeSnapshot}; any field added there
38506
+ * must also be captured here or it won't survive a mode round-trip. */
38507
+ captureCurrentApiTypeSnapshot() {
38508
+ var _a, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z;
38509
+ const headers = ((_c = (_a = this.headersFormArray) === null || _a === void 0 ? void 0 : _a.value) !== null && _c !== void 0 ? _c : []).map((h) => {
38510
+ var _a, _c;
38511
+ return ({
38512
+ name: (_a = h === null || h === void 0 ? void 0 : h.name) !== null && _a !== void 0 ? _a : '',
38513
+ value: (_c = h === null || h === void 0 ? void 0 : h.value) !== null && _c !== void 0 ? _c : '',
38514
+ });
38515
+ });
38516
+ const keyValueRows = ((_e = (_d = this.keyValueFormArray) === null || _d === void 0 ? void 0 : _d.value) !== null && _e !== void 0 ? _e : []).map((r) => {
38517
+ var _a, _c;
38518
+ return ({
38519
+ key: (_a = r === null || r === void 0 ? void 0 : r.key) !== null && _a !== void 0 ? _a : '',
38520
+ value: (_c = r === null || r === void 0 ? void 0 : r.value) !== null && _c !== void 0 ? _c : '',
38521
+ });
38522
+ });
38523
+ const keyTypeValueRows = ((_g = (_f = this.keyTypeValueFormArray) === null || _f === void 0 ? void 0 : _f.value) !== null && _g !== void 0 ? _g : []).map((r) => {
38524
+ var _a, _c, _d;
38525
+ return ({
38526
+ key: (_a = r === null || r === void 0 ? void 0 : r.key) !== null && _a !== void 0 ? _a : '',
38527
+ type: (_c = r === null || r === void 0 ? void 0 : r.type) !== null && _c !== void 0 ? _c : 'text',
38528
+ value: (_d = r === null || r === void 0 ? void 0 : r.value) !== null && _d !== void 0 ? _d : '',
38529
+ });
38530
+ });
38531
+ const paramsRows = ((_j = (_h = this.paramsFormArray) === null || _h === void 0 ? void 0 : _h.value) !== null && _j !== void 0 ? _j : []).map((r) => {
38532
+ var _a, _c;
38533
+ return ({
38534
+ key: (_a = r === null || r === void 0 ? void 0 : r.key) !== null && _a !== void 0 ? _a : '',
38535
+ value: (_c = r === null || r === void 0 ? void 0 : r.value) !== null && _c !== void 0 ? _c : '',
38536
+ });
38537
+ });
38538
+ return {
38539
+ url: this.url,
38540
+ method: this.selectedMethod,
38541
+ environment: this.currentEnvironmentValue,
38542
+ headers,
38543
+ authType: this.selectedAuthType,
38544
+ bearerToken: this.bearerToken,
38545
+ oauth2: ((_k = this.oauth2Form) === null || _k === void 0 ? void 0 : _k.value) ? Object.assign({}, this.oauth2Form.value) : null,
38546
+ payloadType: this.payloadType,
38547
+ payloadText: this.payloadText,
38548
+ payloadFormat: (_o = (_m = (_l = this.payloadFormatForm) === null || _l === void 0 ? void 0 : _l.get('format')) === null || _m === void 0 ? void 0 : _m.value) !== null && _o !== void 0 ? _o : '',
38549
+ keyValueRows,
38550
+ keyTypeValueRows,
38551
+ paramsRows,
38552
+ importCurl: (_q = (_p = this.importCurlControl) === null || _p === void 0 ? void 0 : _p.value) !== null && _q !== void 0 ? _q : '',
38553
+ graphQL: {
38554
+ query: (_t = (_s = (_r = this.graphQLForm) === null || _r === void 0 ? void 0 : _r.get('query')) === null || _s === void 0 ? void 0 : _s.value) !== null && _t !== void 0 ? _t : '',
38555
+ variables: (_w = (_v = (_u = this.graphQLForm) === null || _u === void 0 ? void 0 : _u.get('variables')) === null || _v === void 0 ? void 0 : _v.value) !== null && _w !== void 0 ? _w : '',
38556
+ operationName: (_z = (_y = (_x = this.graphQLForm) === null || _x === void 0 ? void 0 : _x.get('operationName')) === null || _y === void 0 ? void 0 : _y.value) !== null && _z !== void 0 ? _z : '',
38557
+ },
38558
+ };
38559
+ }
38560
+ /** Write a previously-captured snapshot back into the form. When `snap` is
38561
+ * undefined the mode is reset to empty defaults so the user lands on a
38562
+ * fresh state instead of inheriting whatever the other mode had. */
38563
+ applyApiTypeSnapshot(snap) {
38564
+ var _a, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z, _0;
38565
+ // Suppress the URL↔params two-way sync while we're swapping snapshot values
38566
+ // in — otherwise restoring `url` would clobber `paramsRows` we're about to
38567
+ // restore on the next line.
38568
+ this.paramsSyncingFromUrl = true;
38569
+ try {
38570
+ this._url = (_a = snap === null || snap === void 0 ? void 0 : snap.url) !== null && _a !== void 0 ? _a : '';
38571
+ if (this.methodForm) {
38572
+ const firstMethodValue = ((_c = this.httpMethodOptions) === null || _c === void 0 ? void 0 : _c.length)
38573
+ ? ApiEditStepComponent.getOptionValue(this.httpMethodOptions[0])
38574
+ : 'GET';
38575
+ this.methodForm.patchValue({ method: (_e = (_d = snap === null || snap === void 0 ? void 0 : snap.method) !== null && _d !== void 0 ? _d : firstMethodValue) !== null && _e !== void 0 ? _e : 'GET' });
38576
+ }
38577
+ if (this.environmentForm) {
38578
+ const firstEnvValue = ((_f = this.environmentOptions) === null || _f === void 0 ? void 0 : _f.length)
38579
+ ? ApiEditStepComponent.getOptionValue(this.environmentOptions[0])
38580
+ : '';
38581
+ const restoredEnv = (_h = (_g = snap === null || snap === void 0 ? void 0 : snap.environment) !== null && _g !== void 0 ? _g : firstEnvValue) !== null && _h !== void 0 ? _h : '';
38582
+ this.environmentForm.patchValue({ environment: restoredEnv });
38583
+ // The dropdown's `(selectionChange)` only fires on user interaction, so a
38584
+ // patchValue restore won't notify the parent to refresh `urlOptions`. Emit
38585
+ // `environmentChange` manually here so the URL parameter list re-fetches
38586
+ // for the target mode's environment (or clears when restored to 'none').
38587
+ const envEmit = restoredEnv === '' || restoredEnv === 'none'
38588
+ ? null
38589
+ : restoredEnv;
38590
+ this.environmentChange.emit(envEmit);
38591
+ }
38592
+ this.buildHeadersFormArray(((_j = snap === null || snap === void 0 ? void 0 : snap.headers) === null || _j === void 0 ? void 0 : _j.length)
38593
+ ? snap.headers.map(h => { var _a; return ({ name: h.name, type: (_a = h.type) !== null && _a !== void 0 ? _a : 'string', value: h.value }); })
38594
+ : [{ name: '', type: 'string', value: '' }]);
38595
+ if (this.authTypeForm) {
38596
+ const firstAuthValue = (_k = this.getAuthTypeValues()[0]) !== null && _k !== void 0 ? _k : 'no-auth';
38597
+ this.authTypeForm.patchValue({ authType: (_l = snap === null || snap === void 0 ? void 0 : snap.authType) !== null && _l !== void 0 ? _l : firstAuthValue });
38598
+ }
38599
+ this.bearerToken = (_m = snap === null || snap === void 0 ? void 0 : snap.bearerToken) !== null && _m !== void 0 ? _m : '';
38600
+ if (this.oauth2Form) {
38601
+ this.oauth2Form.reset(Object.assign({ grantType: 'client_credentials', accessTokenUrl: '', clientId: '', clientSecret: '', scope: '', clientAuthentication: 'basic' }, ((_o = snap === null || snap === void 0 ? void 0 : snap.oauth2) !== null && _o !== void 0 ? _o : {})));
38602
+ }
38603
+ this.payloadType = (_p = snap === null || snap === void 0 ? void 0 : snap.payloadType) !== null && _p !== void 0 ? _p : 'raw';
38604
+ this.payloadText = (_q = snap === null || snap === void 0 ? void 0 : snap.payloadText) !== null && _q !== void 0 ? _q : '';
38605
+ if (this.payloadFormatForm) {
38606
+ this.payloadFormatForm.patchValue({ format: (_r = snap === null || snap === void 0 ? void 0 : snap.payloadFormat) !== null && _r !== void 0 ? _r : '' });
38607
+ }
38608
+ this.buildKeyValueFormArray(snap === null || snap === void 0 ? void 0 : snap.keyValueRows);
38609
+ this.buildKeyTypeValueFormArray(snap === null || snap === void 0 ? void 0 : snap.keyTypeValueRows);
38610
+ this.buildParamsFormArray(snap === null || snap === void 0 ? void 0 : snap.paramsRows);
38611
+ (_s = this.importCurlControl) === null || _s === void 0 ? void 0 : _s.setValue((_t = snap === null || snap === void 0 ? void 0 : snap.importCurl) !== null && _t !== void 0 ? _t : '', { emitEvent: false });
38612
+ (_u = this.graphQLForm) === null || _u === void 0 ? void 0 : _u.patchValue({
38613
+ query: (_w = (_v = snap === null || snap === void 0 ? void 0 : snap.graphQL) === null || _v === void 0 ? void 0 : _v.query) !== null && _w !== void 0 ? _w : '',
38614
+ variables: (_y = (_x = snap === null || snap === void 0 ? void 0 : snap.graphQL) === null || _x === void 0 ? void 0 : _x.variables) !== null && _y !== void 0 ? _y : '',
38615
+ operationName: (_0 = (_z = snap === null || snap === void 0 ? void 0 : snap.graphQL) === null || _z === void 0 ? void 0 : _z.operationName) !== null && _0 !== void 0 ? _0 : '',
38616
+ });
38617
+ }
38618
+ finally {
38619
+ this.paramsSyncingFromUrl = false;
38620
+ }
38621
+ }
38622
+ /**
38623
+ * Parse the Variables textarea as JSON. Empty string is valid (= no variables).
38624
+ * Sets `graphQLVariablesError` for inline display; returns the parsed object or
38625
+ * the raw string when the user typed something non-empty that didn't parse.
38626
+ *
38627
+ * @param strict When true (called from `onCreate`), an invalid JSON blocks save
38628
+ * — we leave the error visible and return `null` to signal abort.
38629
+ */
38630
+ validateGraphQLVariables(strict) {
38631
+ var _a, _c, _d;
38632
+ const raw = ((_d = (_c = (_a = this.graphQLForm) === null || _a === void 0 ? void 0 : _a.get('variables')) === null || _c === void 0 ? void 0 : _c.value) !== null && _d !== void 0 ? _d : '').trim();
38633
+ if (!raw) {
38634
+ this.graphQLVariablesError = '';
38635
+ return { value: undefined, valid: true };
38636
+ }
38637
+ try {
38638
+ const parsed = JSON.parse(raw);
38639
+ this.graphQLVariablesError = '';
38640
+ return { value: parsed, valid: true };
38641
+ }
38642
+ catch (err) {
38643
+ const message = err instanceof Error ? err.message : String(err);
38644
+ this.graphQLVariablesError = `Invalid JSON: ${message}`;
38645
+ // In non-strict mode (live typing) we still echo back the raw string so the parent
38646
+ // can decide what to do; in strict mode the caller checks `valid` to abort save.
38647
+ return { value: strict ? undefined : raw, valid: false };
38648
+ }
38649
+ }
38650
+ /**
38651
+ * Detect when the GraphQL query declares 2+ named operations. GraphQL requires the
38652
+ * client to specify `operationName` in that case; absence is a runtime error, not a
38653
+ * syntax one, so we surface it as a non-blocking warning.
38654
+ */
38655
+ recomputeGraphQLOperationWarning() {
38656
+ var _a, _c, _d, _e;
38657
+ if (this.activeApiType !== 'graphql' || !this.graphQLForm) {
38658
+ this.graphQLOperationWarning = '';
38659
+ return;
38660
+ }
38661
+ const query = ((_c = (_a = this.graphQLForm.get('query')) === null || _a === void 0 ? void 0 : _a.value) !== null && _c !== void 0 ? _c : '');
38662
+ const opName = ((_e = (_d = this.graphQLForm.get('operationName')) === null || _d === void 0 ? void 0 : _d.value) !== null && _e !== void 0 ? _e : '').trim();
38663
+ if (!query.trim() || opName) {
38664
+ this.graphQLOperationWarning = '';
38665
+ return;
38666
+ }
38667
+ // Match `query`, `mutation`, `subscription` as standalone tokens at the start of a line.
38668
+ // Strip block/line comments first so commented-out operations don't trip the count.
38669
+ const stripped = query
38670
+ .replace(/#[^\n]*/g, '')
38671
+ .replace(/"""[\s\S]*?"""/g, '');
38672
+ const matches = stripped.match(/^[\s]*(query|mutation|subscription)\b/gm) || [];
38673
+ this.graphQLOperationWarning = matches.length >= 2
38674
+ ? 'Multiple operations detected — Operation Name is required.'
38675
+ : '';
38676
+ }
38677
+ /**
38678
+ * Click handler for the "Load Schema" button (GraphQL mode only). Switches the body view
38679
+ * to the operation picker AND emits `loadSchema` so the parent can fetch / refresh the
38680
+ * schema in the background. The picker renders whatever `graphQLSchemaSource` /
38681
+ * `graphQLOperations` the parent has supplied at any given time — empty state by default.
38682
+ */
38683
+ onLoadSchema() {
38684
+ var _a;
38685
+ this.bodyView = 'graphql-schema';
38686
+ this.loadSchema.emit({ url: (_a = this.url) !== null && _a !== void 0 ? _a : '', headers: this.headers, environment: this.currentEnvironmentValue });
38687
+ this.cdr.markForCheck();
38688
+ }
38689
+ /** Picker → back: drop the picker view and return to the headers/tabs panel. */
38690
+ onGraphQLPickerBack() {
38691
+ this.bodyView = 'headers';
38692
+ this.cdr.markForCheck();
38693
+ }
38694
+ /** Picker → re-introspect: forward to parent so it can refetch the schema. */
38695
+ onGraphQLPickerReIntrospect() {
38696
+ var _a;
38697
+ this.loadSchema.emit({ url: (_a = this.url) !== null && _a !== void 0 ? _a : '', headers: this.headers, environment: this.currentEnvironmentValue });
38698
+ }
38699
+ /**
38700
+ * Picker → operation selected: patch the GraphQL form (query / variables / operationName)
38701
+ * and pop back to the main view with the GraphQL tab active so the user can see the
38702
+ * filled-in fields.
38703
+ */
38704
+ onGraphQLPickerOperationSelected(op) {
38705
+ var _a, _c, _d;
38706
+ if (!this.graphQLForm) {
38707
+ this.bodyView = 'headers';
38708
+ return;
38709
+ }
38710
+ let variablesString = '';
38711
+ if (op.variables != null) {
38712
+ try {
38713
+ variablesString = JSON.stringify(op.variables, null, 2);
38714
+ }
38715
+ catch (_e) {
38716
+ variablesString = '';
38717
+ }
38718
+ }
38719
+ this.graphQLForm.patchValue({
38720
+ query: (_a = op.query) !== null && _a !== void 0 ? _a : '',
38721
+ variables: variablesString,
38722
+ operationName: (_d = (_c = op.operationName) !== null && _c !== void 0 ? _c : op.name) !== null && _d !== void 0 ? _d : '',
38723
+ });
38724
+ this.graphQLQueryError = '';
38725
+ this.graphQLVariablesError = '';
38726
+ this.recomputeGraphQLOperationWarning();
38727
+ // Drop any stale response from a previously-picked operation. The next Send Request
38728
+ // will populate the preview again. Note: ngOnChanges on `initialResponsePreview` only
38729
+ // syncs non-null values down, so clearing here doesn't get clobbered when the parent
38730
+ // also clears its `initialResponsePreview` field.
38731
+ this.responsePreview = '';
38732
+ this.activeApiType = 'graphql';
38733
+ this.activePayloadTab = 'graphql';
38734
+ this.bodyView = 'headers';
38735
+ this.cdr.markForCheck();
38736
+ }
38237
38737
  setResponseVerificationTab(value) {
38238
38738
  this.activeResponseVerificationTab = value;
38239
38739
  }
@@ -38360,10 +38860,58 @@ class ApiEditStepComponent {
38360
38860
  }
38361
38861
  /** Emit all entered details (environment, HTTP method, URL, headers, body, step2 variable, step3 verifications) when user clicks Create. */
38362
38862
  onCreate() {
38363
- console.log('9999999');
38863
+ var _a, _c, _d;
38864
+ // GraphQL mode validation: query is required + non-blank, variables (if non-empty) must be valid JSON.
38865
+ if (this.activeApiType === 'graphql') {
38866
+ const query = ((_d = (_c = (_a = this.graphQLForm) === null || _a === void 0 ? void 0 : _a.get('query')) === null || _c === void 0 ? void 0 : _c.value) !== null && _d !== void 0 ? _d : '').trim();
38867
+ if (!query) {
38868
+ this.graphQLQueryError = 'Query is required.';
38869
+ this.activePayloadTab = 'graphql';
38870
+ this.cdr.markForCheck();
38871
+ return;
38872
+ }
38873
+ const variables = this.validateGraphQLVariables(true);
38874
+ if (!variables.valid) {
38875
+ this.activePayloadTab = 'graphql';
38876
+ this.cdr.markForCheck();
38877
+ return;
38878
+ }
38879
+ }
38880
+ // Each mode keeps its own snapshot (URL/headers/body/etc.) — only the active
38881
+ // mode's data is emitted on Create. Warn the user if there is content in the
38882
+ // OTHER mode that would be discarded so they can switch back if needed.
38883
+ const otherType = this.activeApiType === 'graphql' ? 'rest' : 'graphql';
38884
+ if (this.snapshotHasUserData(this.snapshotByType[otherType])) {
38885
+ const activeLabel = this.activeApiType === 'graphql' ? 'GraphQL' : 'REST API';
38886
+ const otherLabel = otherType === 'graphql' ? 'GraphQL' : 'REST API';
38887
+ this.dialogService.open({
38888
+ title: 'Discard data from the other API type?',
38889
+ description: `You have entered data in the ${otherLabel} tab. Only the data from the currently ` +
38890
+ `selected ${activeLabel} tab will be saved when you create this step. The ` +
38891
+ `${otherLabel} data will be discarded.`,
38892
+ width: '460px',
38893
+ // Opt out of the default backdrop blur — there's already a matching rule
38894
+ // for this panelClass in tailwind.css under the "Disable backdrop blur
38895
+ // for specific dialogs only" block.
38896
+ panelClass: 'api-edit-discard-warning-dialog',
38897
+ buttons: [
38898
+ { label: 'Cancel', role: 'secondary', handler: r => r.close(false) },
38899
+ { label: `Save ${activeLabel} only`, role: 'primary', handler: r => r.close(true) },
38900
+ ],
38901
+ buttonAlignment: 'right',
38902
+ }).afterClosed().subscribe(confirmed => {
38903
+ if (confirmed)
38904
+ this.emitCreatePayload();
38905
+ });
38906
+ return;
38907
+ }
38908
+ this.emitCreatePayload();
38909
+ }
38910
+ /** Build and emit the Create payload. Extracted from {@link onCreate} so the
38911
+ * cross-mode-discard confirmation flow can defer the emit behind a dialog. */
38912
+ emitCreatePayload() {
38364
38913
  try {
38365
38914
  const payload = this.getCreatePayload();
38366
- console.log('9999999 payload', payload);
38367
38915
  this.create.emit(payload);
38368
38916
  }
38369
38917
  catch (err) {
@@ -38371,6 +38919,38 @@ class ApiEditStepComponent {
38371
38919
  this.create.emit(this.getCreatePayloadFallback());
38372
38920
  }
38373
38921
  }
38922
+ /** True when the snapshot contains anything the user typed in that mode — URL,
38923
+ * headers, body text, params, key/value rows, or GraphQL query/vars/op name.
38924
+ * Returns false when the snapshot is missing (mode never visited) or holds
38925
+ * only the empty defaults. */
38926
+ snapshotHasUserData(snap) {
38927
+ var _a, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q;
38928
+ if (!snap)
38929
+ return false;
38930
+ if (((_a = snap.url) !== null && _a !== void 0 ? _a : '').trim())
38931
+ return true;
38932
+ if ((_c = snap.headers) === null || _c === void 0 ? void 0 : _c.some(h => { var _a, _c; return ((_a = h === null || h === void 0 ? void 0 : h.name) !== null && _a !== void 0 ? _a : '').trim() || ((_c = h === null || h === void 0 ? void 0 : h.value) !== null && _c !== void 0 ? _c : '').trim(); }))
38933
+ return true;
38934
+ if (((_d = snap.payloadText) !== null && _d !== void 0 ? _d : '').trim())
38935
+ return true;
38936
+ if ((_e = snap.keyValueRows) === null || _e === void 0 ? void 0 : _e.some(r => { var _a, _c; return ((_a = r === null || r === void 0 ? void 0 : r.key) !== null && _a !== void 0 ? _a : '').trim() || ((_c = r === null || r === void 0 ? void 0 : r.value) !== null && _c !== void 0 ? _c : '').trim(); }))
38937
+ return true;
38938
+ if ((_f = snap.keyTypeValueRows) === null || _f === void 0 ? void 0 : _f.some(r => { var _a, _c; return ((_a = r === null || r === void 0 ? void 0 : r.key) !== null && _a !== void 0 ? _a : '').trim() || ((_c = r === null || r === void 0 ? void 0 : r.value) !== null && _c !== void 0 ? _c : '').trim(); }))
38939
+ return true;
38940
+ if ((_g = snap.paramsRows) === null || _g === void 0 ? void 0 : _g.some(r => { var _a, _c; return ((_a = r === null || r === void 0 ? void 0 : r.key) !== null && _a !== void 0 ? _a : '').trim() || ((_c = r === null || r === void 0 ? void 0 : r.value) !== null && _c !== void 0 ? _c : '').trim(); }))
38941
+ return true;
38942
+ if (((_h = snap.bearerToken) !== null && _h !== void 0 ? _h : '').trim())
38943
+ return true;
38944
+ if (((_j = snap.importCurl) !== null && _j !== void 0 ? _j : '').trim())
38945
+ return true;
38946
+ if (((_l = (_k = snap.graphQL) === null || _k === void 0 ? void 0 : _k.query) !== null && _l !== void 0 ? _l : '').trim())
38947
+ return true;
38948
+ if (((_o = (_m = snap.graphQL) === null || _m === void 0 ? void 0 : _m.variables) !== null && _o !== void 0 ? _o : '').trim())
38949
+ return true;
38950
+ if (((_q = (_p = snap.graphQL) === null || _p === void 0 ? void 0 : _p.operationName) !== null && _q !== void 0 ? _q : '').trim())
38951
+ return true;
38952
+ return false;
38953
+ }
38374
38954
  /** Minimal payload when getCreatePayload throws (so create always emits). */
38375
38955
  getCreatePayloadFallback() {
38376
38956
  var _a, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p;
@@ -38410,7 +38990,7 @@ class ApiEditStepComponent {
38410
38990
  }
38411
38991
  /** Build full create payload: environment, HTTP method, URL, headers, body (step1), variable name (step2), verifications (step3). */
38412
38992
  getCreatePayload() {
38413
- var _a, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z;
38993
+ var _a, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z, _0, _1, _2, _3, _4, _5;
38414
38994
  const envFromForm = ApiEditStepComponent.getControlValue((_a = this.environmentForm) === null || _a === void 0 ? void 0 : _a.get('environment'));
38415
38995
  const environment = envFromForm !== '' ? envFromForm : (this.currentEnvironmentValue !== '' ? this.currentEnvironmentValue : this.selectedEnvironment);
38416
38996
  const methodFromForm = ApiEditStepComponent.getControlValue((_c = this.methodForm) === null || _c === void 0 ? void 0 : _c.get('method'));
@@ -38474,13 +39054,33 @@ class ApiEditStepComponent {
38474
39054
  clientAuthentication: clientAuth !== '' ? clientAuth : 'basic',
38475
39055
  };
38476
39056
  }
39057
+ // GraphQL extension: when in GraphQL mode, package the query/variables/operationName into a
39058
+ // single sub-object and ensure Content-Type defaults to application/json (engine sets it too,
39059
+ // but surfacing it on the headers list is cleaner for the user). Method is forced to POST so
39060
+ // legacy REST-flavoured downstream code paths keep working when they peek at it.
39061
+ const isGraphQL = this.activeApiType === 'graphql';
39062
+ let outputApiType;
39063
+ let outputGraphQL;
39064
+ let outputHeaders = this.headers;
39065
+ let outputMethod = method;
39066
+ if (isGraphQL) {
39067
+ outputApiType = 'graphql';
39068
+ const query = ((_y = (_x = (_w = this.graphQLForm) === null || _w === void 0 ? void 0 : _w.get('query')) === null || _x === void 0 ? void 0 : _x.value) !== null && _y !== void 0 ? _y : '').trim();
39069
+ const operationName = ((_1 = (_0 = (_z = this.graphQLForm) === null || _z === void 0 ? void 0 : _z.get('operationName')) === null || _0 === void 0 ? void 0 : _0.value) !== null && _1 !== void 0 ? _1 : '').trim();
39070
+ const variablesResult = this.validateGraphQLVariables(false);
39071
+ outputGraphQL = Object.assign(Object.assign({ query }, (variablesResult.value !== undefined && { variables: variablesResult.value })), (operationName && { operationName }));
39072
+ outputMethod = 'POST';
39073
+ const hasContentType = outputHeaders.some(h => (h.name || '').trim().toLowerCase() === 'content-type');
39074
+ if (!hasContentType) {
39075
+ outputHeaders = [{ name: 'Content-Type', type: 'string', value: 'application/json' }, ...outputHeaders];
39076
+ }
39077
+ }
38477
39078
  return {
38478
- step1: Object.assign(Object.assign(Object.assign({ summary: ((_w = this.summary) === null || _w === void 0 ? void 0 : _w.trim()) || undefined, environment,
38479
- method, url: (_x = this.url) !== null && _x !== void 0 ? _x : '', headers: this.headers, authType }, (bearerToken !== undefined && { bearerToken })), (oauth2 !== undefined && { oauth2 })), { activePayloadTab: this.activePayloadTab, payloadType: this.payloadType, payloadFormat: format, payloadText: (_y = this.payloadText) !== null && _y !== void 0 ? _y : '', keyValueRows,
39079
+ step1: Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({ summary: ((_2 = this.summary) === null || _2 === void 0 ? void 0 : _2.trim()) || undefined, environment, method: outputMethod, url: (_3 = this.url) !== null && _3 !== void 0 ? _3 : '', headers: outputHeaders, authType }, (bearerToken !== undefined && { bearerToken })), (oauth2 !== undefined && { oauth2 })), { activePayloadTab: this.activePayloadTab, payloadType: this.payloadType, payloadFormat: format, payloadText: (_4 = this.payloadText) !== null && _4 !== void 0 ? _4 : '', keyValueRows,
38480
39080
  keyTypeValueRows,
38481
- paramsRows }),
39081
+ paramsRows }), (outputApiType && { apiType: outputApiType })), (outputGraphQL && { graphQL: outputGraphQL })),
38482
39082
  step2: {
38483
- variableName: (_z = this.variableName) !== null && _z !== void 0 ? _z : '',
39083
+ variableName: (_5 = this.variableName) !== null && _5 !== void 0 ? _5 : '',
38484
39084
  },
38485
39085
  step3: {
38486
39086
  activeResponseVerificationTab: this.activeResponseVerificationTab,
@@ -38504,7 +39104,7 @@ class ApiEditStepComponent {
38504
39104
  });
38505
39105
  }
38506
39106
  ngOnChanges(changes) {
38507
- var _a, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u;
39107
+ var _a, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z, _0, _1;
38508
39108
  if (((_a = changes['initialMethod']) === null || _a === void 0 ? void 0 : _a.currentValue) != null && this.methodForm) {
38509
39109
  const v = changes['initialMethod'].currentValue;
38510
39110
  this.currentMethodValue = typeof v === 'string' ? v : ((_c = ApiEditStepComponent.getOptionValue(v)) !== null && _c !== void 0 ? _c : '');
@@ -38554,12 +39154,23 @@ class ApiEditStepComponent {
38554
39154
  if (resolved != null)
38555
39155
  this.authTypeForm.patchValue({ authType: v });
38556
39156
  }
39157
+ if (((_l = changes['initialOAuth2']) === null || _l === void 0 ? void 0 : _l.currentValue) != null && this.oauth2Form) {
39158
+ const v = changes['initialOAuth2'].currentValue;
39159
+ this.oauth2Form.patchValue({
39160
+ grantType: (_m = v.grantType) !== null && _m !== void 0 ? _m : 'client_credentials',
39161
+ accessTokenUrl: (_o = v.accessTokenUrl) !== null && _o !== void 0 ? _o : '',
39162
+ clientId: (_p = v.clientId) !== null && _p !== void 0 ? _p : '',
39163
+ clientSecret: (_q = v.clientSecret) !== null && _q !== void 0 ? _q : '',
39164
+ scope: (_r = v.scope) !== null && _r !== void 0 ? _r : '',
39165
+ clientAuthentication: (_s = v.clientAuthentication) !== null && _s !== void 0 ? _s : 'basic',
39166
+ });
39167
+ }
38557
39168
  if (changes['authTypeOptions']) {
38558
39169
  this.updateAuthTypeSelectConfig();
38559
39170
  if (this.authTypeForm) {
38560
39171
  const values = this.getAuthTypeValues();
38561
39172
  if (values.length > 0) {
38562
- const current = (_l = this.authTypeForm.get('authType')) === null || _l === void 0 ? void 0 : _l.value;
39173
+ const current = (_t = this.authTypeForm.get('authType')) === null || _t === void 0 ? void 0 : _t.value;
38563
39174
  const currentVal = current != null && current !== ''
38564
39175
  ? typeof current === 'object'
38565
39176
  ? ApiEditStepComponent.getOptionValue(current)
@@ -38578,38 +39189,38 @@ class ApiEditStepComponent {
38578
39189
  if (changes['initialStep']) {
38579
39190
  this.applyInitialStep(changes['initialStep'].currentValue);
38580
39191
  }
38581
- if (((_m = changes['initialUrl']) === null || _m === void 0 ? void 0 : _m.currentValue) != null) {
39192
+ if (((_u = changes['initialUrl']) === null || _u === void 0 ? void 0 : _u.currentValue) != null) {
38582
39193
  this.url = ApiEditStepComponent.decodeUrlForDisplay(changes['initialUrl'].currentValue);
38583
39194
  }
38584
- if (((_o = changes['initialPayloadTab']) === null || _o === void 0 ? void 0 : _o.currentValue) != null) {
39195
+ if (((_v = changes['initialPayloadTab']) === null || _v === void 0 ? void 0 : _v.currentValue) != null) {
38585
39196
  this.activePayloadTab = changes['initialPayloadTab'].currentValue;
38586
39197
  }
38587
39198
  if (changes['headerNameOptions']) {
38588
39199
  this.updateHeaderNameSelectConfig();
38589
39200
  }
38590
- if (((_p = changes['initialHeaders']) === null || _p === void 0 ? void 0 : _p.currentValue) != null) {
39201
+ if (((_w = changes['initialHeaders']) === null || _w === void 0 ? void 0 : _w.currentValue) != null) {
38591
39202
  this.buildHeadersFormArray(changes['initialHeaders'].currentValue);
38592
39203
  this.updateHeaderNameSelectConfig();
38593
39204
  }
38594
- if (((_q = changes['initialResponsePreview']) === null || _q === void 0 ? void 0 : _q.currentValue) != null) {
39205
+ if (((_x = changes['initialResponsePreview']) === null || _x === void 0 ? void 0 : _x.currentValue) != null) {
38595
39206
  console.log('9999999 initialResponsePreview', this.initialResponsePreview);
38596
39207
  this.responsePreview = changes['initialResponsePreview'].currentValue;
38597
39208
  // When a new response preview is provided (after Send Request), scroll preview section into view
38598
39209
  this.scrollPreviewIntoView();
38599
39210
  }
38600
- if (((_r = changes['initialFormat']) === null || _r === void 0 ? void 0 : _r.currentValue) != null && this.payloadFormatForm) {
39211
+ if (((_y = changes['initialFormat']) === null || _y === void 0 ? void 0 : _y.currentValue) != null && this.payloadFormatForm) {
38601
39212
  this.payloadFormatForm.patchValue({ format: changes['initialFormat'].currentValue });
38602
39213
  }
38603
- if (((_s = changes['initialVariableName']) === null || _s === void 0 ? void 0 : _s.currentValue) != null) {
39214
+ if (((_z = changes['initialVariableName']) === null || _z === void 0 ? void 0 : _z.currentValue) != null) {
38604
39215
  this.variableName = changes['initialVariableName'].currentValue;
38605
39216
  }
38606
- if (((_t = changes['initialSummary']) === null || _t === void 0 ? void 0 : _t.currentValue) != null) {
39217
+ if (((_0 = changes['initialSummary']) === null || _0 === void 0 ? void 0 : _0.currentValue) != null) {
38607
39218
  this.summary = changes['initialSummary'].currentValue;
38608
39219
  }
38609
39220
  if (changes['formatOptions']) {
38610
39221
  this.updatePayloadFormatSelectConfig();
38611
39222
  if (this.payloadFormatForm) {
38612
- const current = (_u = this.payloadFormatForm.get('format')) === null || _u === void 0 ? void 0 : _u.value;
39223
+ const current = (_1 = this.payloadFormatForm.get('format')) === null || _1 === void 0 ? void 0 : _1.value;
38613
39224
  const values = this.getFormatValues();
38614
39225
  const valid = values.includes(current);
38615
39226
  if (!valid && values.length > 0) {
@@ -38704,6 +39315,22 @@ ApiEditStepComponent.OAUTH_CLIENT_AUTH_OPTIONS = [
38704
39315
  { id: 'basic', value: 'basic', name: 'Send as Basic Auth header', label: 'Send as Basic Auth header' },
38705
39316
  { id: 'body', value: 'body', name: 'Send in body', label: 'Send in body' },
38706
39317
  ];
39318
+ /**
39319
+ * The two tab arrangements. REST keeps the legacy four-tab layout. GraphQL drops Body & Params
39320
+ * because the body is a single { query, variables, operationName } object (rendered in the new
39321
+ * GraphQL tab below) and query params have no meaning for a GraphQL POST.
39322
+ */
39323
+ ApiEditStepComponent.REST_PAYLOAD_TABS = [
39324
+ { value: 'authorization', label: 'Authorization' },
39325
+ { value: 'headers', label: 'Headers' },
39326
+ { value: 'body', label: 'Body' },
39327
+ { value: 'params', label: 'Params' },
39328
+ ];
39329
+ ApiEditStepComponent.GRAPHQL_PAYLOAD_TABS = [
39330
+ { value: 'authorization', label: 'Authorization' },
39331
+ { value: 'headers', label: 'Headers' },
39332
+ { value: 'graphql', label: 'GraphQL' },
39333
+ ];
38707
39334
  ApiEditStepComponent.DEFAULT_FORMAT_OPTIONS = [
38708
39335
  { id: 'json', value: 'json', name: 'JSON', label: 'JSON' },
38709
39336
  { id: 'xml', value: 'xml', name: 'XML', label: 'XML' },
@@ -38774,12 +39401,12 @@ ApiEditStepComponent.DEFAULT_STATUS_VERIFICATION_OPTIONS = [
38774
39401
  { id: 'less-than', value: 'less-than', name: 'Less than', label: 'Less than' },
38775
39402
  { id: 'greater-than', value: 'greater-than', name: 'Greater than', label: 'Greater than' },
38776
39403
  ];
38777
- ApiEditStepComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.4.0", ngImport: i0, type: ApiEditStepComponent, deps: [{ token: i1$1.FormBuilder }, { token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Component });
38778
- ApiEditStepComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version: "13.4.0", type: ApiEditStepComponent, selector: "cqa-api-edit-step", inputs: { initialMethod: "initialMethod", initialEnvironment: "initialEnvironment", initialStep: "initialStep", initialUrl: "initialUrl", initialPayloadTab: "initialPayloadTab", initialPayloadType: "initialPayloadType", initialPayloadText: "initialPayloadText", initialSummary: "initialSummary", initialHeaders: "initialHeaders", initialResponsePreview: "initialResponsePreview", initialVariableName: "initialVariableName", initialResponseBodyVerifications: "initialResponseBodyVerifications", initialStatusVerifications: "initialStatusVerifications", initialActiveResponseVerificationTab: "initialActiveResponseVerificationTab", editMode: "editMode", isCreatingStep: "isCreatingStep", httpMethodOptions: "httpMethodOptions", environmentOptions: "environmentOptions", hasMoreEnvironments: "hasMoreEnvironments", isLoadingEnvironments: "isLoadingEnvironments", urlOptions: "urlOptions", authTypeOptions: "authTypeOptions", initialAuthType: "initialAuthType", formatOptions: "formatOptions", initialFormat: "initialFormat", headerNameOptions: "headerNameOptions", verificationOptions: "verificationOptions", verificationDataTypeOptions: "verificationDataTypeOptions", statusVerificationOptions: "statusVerificationOptions", advancedSettingsVariables: "advancedSettingsVariables" }, outputs: { importCurl: "importCurl", importCurlCancel: "importCurlCancel", sendRequest: "sendRequest", back: "back", cancel: "cancel", next: "next", create: "create", headersChange: "headersChange", environmentChange: "environmentChange", environmentSearch: "environmentSearch", environmentLoadMore: "environmentLoadMore" }, host: { classAttribute: "cqa-ui-root" }, viewQueries: [{ propertyName: "payloadEditorWithLinesRef", first: true, predicate: ["payloadEditorWithLinesRef"], descendants: true }, { propertyName: "payloadTextareaRef", first: true, predicate: ["payloadTextareaRef"], descendants: true }, { propertyName: "responsePreviewRef", first: true, predicate: ["responsePreviewRef"], descendants: true }], usesOnChanges: true, ngImport: i0, template: "<div class=\"cqa-api-edit-step-container cqa-bg-[#ffffff] cqa-flex cqa-flex-col cqa-w-full cqa-h-full cqa-p-[16px] cqa-gap-[12px] cqa-overflow-y-auto cqa-overflow-x-hidden cqa-box-border\">\n\n <h2 class=\"cqa-api-edit-step-title cqa-font-inter cqa-text-[12px] cqa-font-semibold cqa-leading-[100%] cqa-tracking-normal cqa-text-[#000000] cqa-m-0\">\n {{ editMode ? 'Update API Test Step' : 'Create API Test Step' }}\n </h2>\n\n <div class=\"cqa-api-edit-step-indicator cqa-flex cqa-items-center cqa-gap-0 cqa-max-[767px]:cqa-flex-wrap cqa-max-[767px]:cqa-gap-2 cqa-max-[767px]:cqa-justify-start\"\n [class.cqa-justify-start]=\"stepLabels.length === 1\"\n [class.cqa-justify-between]=\"stepLabels.length !== 1\">\n <ng-container *ngFor=\"let step of stepLabels; let i = index\">\n <cqa-button type=\"button\" variant=\"text\"\n customClass=\"cqa-api-edit-step-indicator-item cqa-api-edit-step-indicator-item--clickable cqa-flex cqa-items-center cqa-gap-[10px] cqa-cursor-pointer cqa-border-none cqa-p-0 cqa-font-inherit cqa-text-inherit cqa-text-left !cqa-bg-transparent\"\n [attr.aria-label]=\"'Step ' + step.index + ': ' + step.label\" [attr.aria-pressed]=\"step.index === currentStep\"\n (clicked)=\"setStep(step.index)\">\n <span class=\"cqa-api-edit-step-indicator-circle cqa-w-8 cqa-h-8 cqa-rounded-full cqa-inline-flex cqa-items-center cqa-justify-center cqa-text-sm cqa-font-medium cqa-leading-none cqa-border-none cqa-shrink-0\"\n [ngClass]=\"step.index === currentStep ? 'cqa-bg-[#4F46E5] cqa-text-white' : 'cqa-bg-[#E0E0E0] cqa-text-[#666666]'\">\n {{ step.index }}\n </span>\n <span class=\"cqa-api-edit-step-indicator-label cqa-text-sm cqa-leading-[18px] cqa-font-normal cqa-max-[767px]:cqa-text-xs\"\n [ngClass]=\"step.index === currentStep ? 'cqa-text-[#4F46E5]' : 'cqa-text-[#666666]'\">\n {{ step.label }}\n </span>\n <div *ngIf=\"i < stepLabels.length - 1\" class=\"cqa-api-edit-step-indicator-line cqa-w-[96px] cqa-h-[2px] cqa-bg-[#E0E0E0] cqa-my-0 cqa-mx-[6px] cqa-shrink-0 cqa-self-center cqa-max-[767px]:cqa-w-[24px] cqa-max-[767px]:cqa-mx-1\" aria-hidden=\"true\"></div>\n </cqa-button>\n </ng-container>\n </div>\n\n <!-- Step content viewport: only the active step body is shown (*ngIf) -->\n <div class=\"cqa-api-edit-step-viewport cqa-w-full\">\n <!-- Step 1: Environment, request, body, response -->\n <div *ngIf=\"currentStep === 1\" class=\"cqa-api-edit-step-panel cqa-flex cqa-flex-col cqa-gap-6 cqa-box-border cqa-w-full\">\n <!-- Summary field: label on top, input below (above Environment) -->\n <div class=\"cqa-api-edit-step-summary-row cqa-flex cqa-flex-col cqa-gap-1.5 cqa-items-stretch\">\n <label class=\"cqa-api-edit-step-summary-label cqa-m-0 cqa-text-[14px] cqa-font-semibold cqa-leading-[100%] cqa-tracking-normal\">Summary</label>\n <cqa-custom-input [(value)]=\"summary\"\n [label]=\"''\" placeholder=\"Enter Request summary\" [fullWidth]=\"true\" size=\"md\"\n ariaLabel=\"Summary\">\n </cqa-custom-input>\n </div>\n <!-- Environment row: label on top, select below (aligned right on desktop) -->\n <div class=\"cqa-api-edit-step-environment-row cqa-flex cqa-flex-col cqa-gap-1.5 cqa-items-end cqa-max-[767px]:cqa-items-stretch\">\n <span class=\"cqa-flex cqa-flex-col cqa-gap-1.5 cqa-max-[767px]:cqa-items-stretch\">\n <label class=\"cqa-api-edit-step-environment-label cqa-m-0 cqa-text-[14px] cqa-font-semibold cqa-leading-[100%] cqa-tracking-normal\">Environments</label>\n <cqa-dynamic-select *ngIf=\"environmentForm\"\n [form]=\"environmentForm\"\n [config]=\"environmentSelectConfig\"\n class=\"cqa-api-edit-step-environment-select cqa-shrink-0 cqa-w-auto cqa-min-w-[315px] cqa-max-w-[315px] cqa-max-[767px]:cqa-min-w-0 cqa-max-[767px]:cqa-max-w-full cqa-max-[767px]:cqa-w-full [&_.mat-form-field-wrapper]:cqa-pb-0\"\n aria-label=\"Environment\"\n (selectionChange)=\"onEnvironmentSelectionChange($event)\">\n </cqa-dynamic-select>\n </span>\n </div>\n\n <!-- Request row: method, URL, buttons -->\n <div class=\"cqa-api-edit-step-request-row cqa-flex cqa-items-start cqa-gap-3 cqa-flex-wrap cqa-max-[767px]:cqa-flex-col cqa-max-[767px]:cqa-items-stretch cqa-max-[767px]:cqa-gap-2.5\">\n <cqa-dynamic-select *ngIf=\"methodForm\" [form]=\"methodForm\" [config]=\"methodSelectConfig\"\n class=\"cqa-api-edit-step-method-select cqa-shrink-0 cqa-w-auto cqa-min-w-[152px] cqa-max-w-[152px] cqa-max-[767px]:cqa-min-w-0 cqa-max-[767px]:cqa-max-w-full cqa-max-[767px]:cqa-w-full\" aria-label=\"HTTP method\"\n (selectionChange)=\"onMethodSelectionChange($event)\">\n </cqa-dynamic-select>\n <div class=\"cqa-api-edit-step-url-wrap cqa-flex-1 cqa-min-w-[200px] cqa-max-[767px]:cqa-min-w-0 cqa-max-[767px]:cqa-w-full\">\n <cqa-autocomplete\n [value]=\"url\"\n (valueChange)=\"url = $event\"\n [options]=\"urlOptions\"\n [placeholder]=\"urlPlaceholder\"\n ariaLabel=\"Enter URL or select Parameter\"\n [fullWidth]=\"true\"\n (optionSelect)=\"onUrlOptionSelect($event)\">\n </cqa-autocomplete>\n <div *ngIf=\"urlError\" class=\"cqa-text-red-600 cqa-text-sm cqa-mt-1\">\n {{ urlError }}\n </div>\n </div>\n <div class=\"cqa-api-edit-step-import-curl-trigger cqa-contents cqa-max-[767px]:cqa-w-full cqa-max-[767px]:!cqa-flex\" (click)=\"openImportCurlPanel()\">\n <cqa-button type=\"button\" variant=\"outlined\" btnSize=\"lg\" text=\"Import API cURL\"\n customClass=\"cqa-api-edit-step-btn-outline !cqa-bg-white !cqa-border !cqa-border-solid !cqa-border-[#6B7280] cqa-text-[#414146] cqa-font-inter cqa-text-[14px] cqa-font-semibold cqa-leading-[100%] cqa-tracking-normal hover:!cqa-bg-[#F9FAFB] hover:!cqa-text-[#414146] cqa-max-[767px]:cqa-w-full cqa-mt-1\">\n </cqa-button>\n </div>\n <cqa-button variant=\"filled\" btnSize=\"lg\" text=\"Send Request\" (clicked)=\"onSendRequest()\"\n [disabled]=\"!isUrlValid\"\n customClass=\"cqa-api-edit-step-btn-primary cqa-font-inter cqa-text-[14px] cqa-font-semibold cqa-leading-[100%] cqa-tracking-normal !cqa-bg-[#3F43EE] cqa-text-[#FBFCFF] !cqa-border-none cqa-py-2.5 cqa-px-6 cqa-gap-2 cqa-rounded-lg cqa-min-h-[37px] cqa-box-border hover:!cqa-bg-[#1B1FEB] cqa-max-[767px]:cqa-w-full cqa-mt-1\"\n [ngClass]=\"{'!cqa-opacity-50 !cqa-cursor-not-allowed': !isUrlValid}\">\n </cqa-button>\n </div>\n\n\n\n <!-- Body content: header section (default) OR import cURL section -->\n <ng-container *ngIf=\"bodyView === 'import-curl'\">\n <div class=\"cqa-api-edit-step-import-curl-panel cqa-flex cqa-flex-col cqa-bg-white cqa-rounded-lg cqa-border cqa-border-solid cqa-border-[#E5E7EB] cqa-shadow-[0_1px_3px_rgba(0,0,0,0.08)] cqa-overflow-hidden\">\n <div class=\"cqa-api-edit-step-import-curl-header cqa-flex cqa-items-center cqa-justify-between cqa-py-3 cqa-px-5 cqa-border-b cqa-border-solid cqa-border-[#E2E8F0] cqa-bg-[#F9FAFB] cqa-rounded-t-lg cqa-max-[767px]:cqa-px-4\">\n <h3 class=\"cqa-api-edit-step-import-curl-title cqa-m-0 cqa-text-xs cqa-font-bold cqa-leading-[18px] cqa-text-[#374151]\">Import API cURL</h3>\n </div>\n <div class=\"cqa-api-edit-step-import-curl-separator cqa-h-px cqa-bg-[#E2E8F0] cqa-my-0 cqa-mx-5\"></div>\n <div class=\"cqa-api-edit-step-import-curl-note cqa-px-5 cqa-pt-3\">\n <cqa-badge label=\"cURL is supported in Bash format only.\" icon=\"info\" variant=\"info\" size=\"small\" [fullWidth]=\"true\"></cqa-badge>\n </div>\n <div class=\"cqa-api-edit-step-import-curl-body\">\n <cqa-custom-textarea [value]=\"importCurlControl.value\"\n (valueChange)=\"importCurlControl.setValue($event)\"\n placeholder=\"Paste your cURL command here (e.g., curl -X POST https://api.example.com/users)\"\n [fullWidth]=\"true\" [rows]=\"8\" resize=\"vertical\" size=\"md\"\n class=\"cqa-api-edit-step-import-curl-textarea\">\n </cqa-custom-textarea>\n </div>\n <div class=\"cqa-api-edit-step-import-curl-footer\">\n <cqa-button type=\"button\" variant=\"outlined\" btnSize=\"lg\" text=\"Cancel\"\n customClass=\"cqa-api-edit-step-import-curl-btn-cancel\" (clicked)=\"onCancelImportCurl()\"></cqa-button>\n <cqa-button type=\"button\" variant=\"filled\" btnSize=\"lg\" text=\"Import\"\n customClass=\"cqa-api-edit-step-import-curl-btn-import\" (clicked)=\"onImportCurlConfirm()\"></cqa-button>\n </div>\n </div>\n </ng-container>\n\n <ng-container *ngIf=\"bodyView === 'headers'\">\n <div class=\"cqa-api-edit-step-tabs-wrapper cqa-flex cqa-flex-col cqa-gap-3\">\n <div class=\"cqa-api-edit-step-tabs\">\n <cqa-button *ngFor=\"let tab of payloadTabs\" type=\"button\" variant=\"text\" [text]=\"tab.label\"\n [customClass]=\"'cqa-api-edit-step-tab' + (activePayloadTab === tab.value ? ' cqa-api-edit-step-tab--active' : '') + (isMethodWithoutBody && (tab.value === 'body' || tab.value === 'params') ? ' cqa-api-edit-step-tab--disabled' : '')\"\n [disabled]=\"isMethodWithoutBody && (tab.value === 'body' || tab.value === 'params')\"\n (clicked)=\"setPayloadTab(tab.value)\">\n </cqa-button>\n </div>\n\n <!-- Payload content (Authorization) -->\n <div *ngIf=\"activePayloadTab === 'authorization'\" class=\"cqa-api-edit-step-payload\"\n [attr.style]=\"'padding: 20px; min-height: 200px;'\">\n <div [attr.style]=\"'display: flex; flex-direction: column; gap: 20px;'\">\n <div [attr.style]=\"'display: flex; flex-direction: column; gap: 8px; width: fit-content;'\">\n <span [attr.style]=\"'font-size: 11px; font-weight: 600; letter-spacing: 0.02em; color: #6B7280; text-transform: uppercase; line-height: 1.25;'\">AUTH TYPE</span>\n <cqa-dynamic-select *ngIf=\"authTypeForm\" [form]=\"authTypeForm\" [config]=\"authTypeSelectConfig\"\n class=\"cqa-api-edit-step-auth-type-select\" aria-label=\"Auth type\"\n [attr.style]=\"'min-width: 160px;'\">\n </cqa-dynamic-select>\n </div>\n <!-- No Auth: centered message -->\n <div *ngIf=\"selectedAuthType === 'no-auth'\"\n [attr.style]=\"'flex: 1; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 12px; min-width: 0; text-align: center; border: 1px solid #E5E7EB; border-radius: 8px; padding-top: 16px; padding-bottom: 16px;'\">\n <div aria-hidden=\"true\"\n [attr.style]=\"'width: 48px; height: 48px; border-radius: 10px; background: #F3F4F6; display: flex; align-items: center; justify-content: center; flex-shrink: 0;'\">\n <svg width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\" style=\"display: block;\">\n <rect x=\"5\" y=\"11\" width=\"14\" height=\"2\" rx=\"1\" fill=\"#9CA3AF\"/>\n </svg>\n </div>\n <span [attr.style]=\"'font-size: 16px; font-weight: 600; color: #374151; line-height: 1.25;'\">No Auth</span>\n <div [attr.style]=\"'display: flex; align-items: center; justify-content: center; gap: 6px; flex-wrap: wrap;'\">\n <span [attr.style]=\"'font-size: 14px; font-weight: 400; color: #9CA3AF; line-height: 1.4;'\">This request does not use any authorization.</span>\n <span role=\"img\" aria-label=\"More information\" title=\"This request does not use any authorization.\"\n [attr.style]=\"'display: inline-flex; align-items: center; justify-content: center; width: 18px; height: 18px; color: #9CA3AF; flex-shrink: 0;'\">\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 16 16\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\" style=\"display: block;\">\n <circle cx=\"8\" cy=\"8\" r=\"7\" stroke=\"#9CA3AF\" stroke-width=\"1.5\" fill=\"none\"/>\n <path d=\"M8 7v4M8 5v.5\" stroke=\"#9CA3AF\" stroke-width=\"1.5\" stroke-linecap=\"round\"/>\n </svg>\n </span>\n </div>\n </div>\n <!-- Bearer Token: Token label + textarea -->\n <div *ngIf=\"selectedAuthType === 'bearer'\"\n [attr.style]=\"'flex: 1; display: flex; flex-direction: column; gap: 10px; min-width: 0;'\">\n <span [attr.style]=\"'font-size: 12px; font-weight: 600; color: #374151; line-height: 1.25;'\">Token</span>\n <cqa-custom-textarea [(value)]=\"bearerToken\"\n placeholder=\"Enter bearer token\"\n [fullWidth]=\"true\" [rows]=\"6\" resize=\"vertical\" size=\"md\"\n class=\"cqa-api-edit-step-auth-token-textarea\" ariaLabel=\"Bearer token\">\n </cqa-custom-textarea>\n </div>\n <!-- OAuth 2.0: Grant Type, Access Token URL, Client ID, Client Secret, Scope, Client Authentication -->\n <div *ngIf=\"selectedAuthType === 'oauth2' && oauth2Form\"\n class=\"cqa-api-edit-step-oauth2-grid\"\n [attr.style]=\"'display: grid; grid-template-columns: 1fr 1fr; gap: 16px 24px; width: 100%;'\">\n <div [attr.style]=\"'display: flex; flex-direction: column; gap: 6px; min-width: 0;'\">\n <span [attr.style]=\"'font-size: 12px; font-weight: 600; color: #374151; line-height: 1.25;'\">Grant Type</span>\n <cqa-dynamic-select *ngIf=\"oauth2Form\" [form]=\"oauth2Form\" [config]=\"oauth2GrantTypeSelectConfig\"\n class=\"cqa-api-edit-step-auth-oauth-select\" aria-label=\"OAuth grant type\"\n [attr.style]=\"'min-width: 0; width: 100%;'\">\n </cqa-dynamic-select>\n <span [attr.style]=\"'font-size: 11px; font-style: italic; color: #6B7280; line-height: 1.3;'\">OAuth grant type</span>\n </div>\n <div [attr.style]=\"'display: flex; flex-direction: column; gap: 6px; min-width: 0;'\">\n <span [attr.style]=\"'font-size: 12px; font-weight: 600; color: #374151; line-height: 1.25;'\">Access Token URL</span>\n <cqa-custom-input [value]=\"oauth2Form.get('accessTokenUrl')?.value\"\n (valueChange)=\"oauth2Form.get('accessTokenUrl')?.setValue($event)\"\n [label]=\"''\" placeholder=\"Enter OAuth token endpoint\" [fullWidth]=\"true\" size=\"md\"\n ariaLabel=\"Access Token URL\">\n </cqa-custom-input>\n <span [attr.style]=\"'font-size: 11px; font-style: italic; color: #6B7280; line-height: 1.3;'\">OAuth token endpoint</span>\n </div>\n <div [attr.style]=\"'display: flex; flex-direction: column; gap: 6px; min-width: 0;'\">\n <span [attr.style]=\"'font-size: 12px; font-weight: 600; color: #374151; line-height: 1.25;'\">Client ID</span>\n <cqa-custom-input [value]=\"oauth2Form.get('clientId')?.value\"\n (valueChange)=\"oauth2Form.get('clientId')?.setValue($event)\"\n [label]=\"''\" placeholder=\"Enter OAuth client identifier\" [fullWidth]=\"true\" size=\"md\"\n ariaLabel=\"Client ID\">\n </cqa-custom-input>\n <span [attr.style]=\"'font-size: 11px; font-style: italic; color: #6B7280; line-height: 1.3;'\">OAuth client identifier</span>\n </div>\n <div [attr.style]=\"'display: flex; flex-direction: column; gap: 6px; min-width: 0;'\">\n <span [attr.style]=\"'font-size: 12px; font-weight: 600; color: #374151; line-height: 1.25;'\">Client Secret</span>\n <cqa-custom-input [value]=\"oauth2Form.get('clientSecret')?.value\"\n (valueChange)=\"oauth2Form.get('clientSecret')?.setValue($event)\"\n type=\"password\" [label]=\"''\" placeholder=\"Enter OAuth client secret\" [fullWidth]=\"true\" size=\"md\"\n ariaLabel=\"Client Secret\">\n </cqa-custom-input>\n <span [attr.style]=\"'font-size: 11px; font-style: italic; color: #6B7280; line-height: 1.3;'\">OAuth client secret</span>\n </div>\n <div [attr.style]=\"'display: flex; flex-direction: column; gap: 6px; min-width: 0;'\">\n <span [attr.style]=\"'font-size: 12px; font-weight: 600; color: #374151; line-height: 1.25;'\">Scope</span>\n <cqa-custom-input [value]=\"oauth2Form.get('scope')?.value\"\n (valueChange)=\"oauth2Form.get('scope')?.setValue($event)\"\n [label]=\"''\" placeholder=\"Enter space-separated scopes\" [fullWidth]=\"true\" size=\"md\"\n ariaLabel=\"Scope\">\n </cqa-custom-input>\n <span [attr.style]=\"'font-size: 11px; font-style: italic; color: #6B7280; line-height: 1.3;'\">Space-separated scopes</span>\n </div>\n <div [attr.style]=\"'display: flex; flex-direction: column; gap: 6px; min-width: 0;'\">\n <span [attr.style]=\"'font-size: 12px; font-weight: 600; color: #374151; line-height: 1.25;'\">Client Authentication</span>\n <cqa-dynamic-select *ngIf=\"oauth2Form\" [form]=\"oauth2Form\" [config]=\"oauth2ClientAuthSelectConfig\"\n class=\"cqa-api-edit-step-auth-oauth-select\" aria-label=\"Client authentication method\"\n [attr.style]=\"'min-width: 0; width: 100%;'\">\n </cqa-dynamic-select>\n <span [attr.style]=\"'font-size: 11px; font-style: italic; color: #6B7280; line-height: 1.3;'\">Client authentication method</span>\n </div>\n </div>\n </div>\n </div>\n\n <!-- Payload content (Headers) -->\n <div *ngIf=\"activePayloadTab === 'headers'\" class=\"cqa-api-edit-step-payload\">\n <div class=\"cqa-api-edit-step-headers-grid\">\n <span class=\"cqa-api-edit-step-headers-label\">Header Name</span>\n <span class=\"cqa-api-edit-step-headers-label\">Header Value</span>\n <span class=\"cqa-api-edit-step-headers-label cqa-api-edit-step-headers-label--empty\"\n aria-hidden=\"true\"></span>\n </div>\n <div *ngFor=\"let row of headerRows; let i = index; trackBy: trackByHeader\" [formGroup]=\"row\"\n class=\"cqa-api-edit-step-header-row\">\n <cqa-dynamic-select [form]=\"row\" [config]=\"headerNameSelectConfig\"\n (addCustomValue)=\"onHeaderNameAddCustomValue($event, row)\"\n class=\"cqa-api-edit-step-header-type-select cqa-w-full\">\n </cqa-dynamic-select>\n <cqa-custom-input [value]=\"row.get('value')?.value ?? ''\"\n (valueChange)=\"row.get('value')?.setValue($event)\" placeholder=\"Value\" [fullWidth]=\"true\" size=\"sm\"\n class=\"cqa-api-edit-step-header-value-input\" ariaLabel=\"Header value\">\n </cqa-custom-input>\n <cqa-button type=\"button\" variant=\"text\" [customClass]=\"'cqa-api-edit-step-header-delete'\"\n [tooltip]=\"'Remove header'\" (clicked)=\"removeHeader(i)\">\n <svg class=\"cqa-api-edit-step-header-delete-icon\" width=\"16\" height=\"16\" viewBox=\"0 0 16 16\"\n fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\">\n <path\n d=\"M10.6663 6V12.6667H5.33301V6H10.6663ZM9.66634 2H6.33301L5.66634 2.66667H3.33301V4H12.6663V2.66667H10.333L9.66634 2ZM11.9997 4.66667H3.99967V12.6667C3.99967 13.4 4.59967 14 5.33301 14H10.6663C11.3997 14 11.9997 13.4 11.9997 12.6667V4.66667Z\"\n fill=\"#F9BFBF\" />\n </svg>\n </cqa-button>\n </div>\n <div class=\"cqa-api-edit-step-add-header-wrap\">\n <cqa-button type=\"button\" variant=\"text\" text=\"+ Add Header\"\n [customClass]=\"'cqa-api-edit-step-add-header-link'\" (clicked)=\"addHeader()\">\n </cqa-button>\n </div>\n </div>\n\n <!-- Payload content (Params): Key\u2013Value table with add/delete rows -->\n <div *ngIf=\"activePayloadTab === 'params'\" class=\"cqa-api-edit-step-payload\">\n <div class=\"cqa-api-edit-step-key-value-grid cqa-api-edit-step-key-value-header\">\n <span class=\"cqa-api-edit-step-key-value-label\">Key</span>\n <span class=\"cqa-api-edit-step-key-value-label\">Value</span>\n <span class=\"cqa-api-edit-step-key-value-label cqa-api-edit-step-key-value-label--empty\"\n aria-hidden=\"true\"></span>\n </div>\n <div *ngFor=\"let row of paramsRows; let i = index; trackBy: trackByParams\" [formGroup]=\"row\"\n class=\"cqa-api-edit-step-key-value-row\">\n <cqa-custom-input [value]=\"row.get('key')?.value ?? ''\"\n (valueChange)=\"row.get('key')?.setValue($event)\" placeholder=\"Key\" [fullWidth]=\"true\" size=\"sm\"\n class=\"cqa-api-edit-step-key-value-input\" ariaLabel=\"Key\">\n </cqa-custom-input>\n <cqa-custom-input [value]=\"row.get('value')?.value ?? ''\"\n (valueChange)=\"row.get('value')?.setValue($event)\" placeholder=\"Value\" [fullWidth]=\"true\" size=\"sm\"\n class=\"cqa-api-edit-step-key-value-input\" ariaLabel=\"Value\">\n </cqa-custom-input>\n <cqa-button type=\"button\" variant=\"text\" [customClass]=\"'cqa-api-edit-step-key-value-delete'\"\n [tooltip]=\"'Remove row'\" (clicked)=\"removeParamsRow(i)\">\n <svg class=\"cqa-api-edit-step-key-value-delete-icon\" width=\"16\" height=\"16\" viewBox=\"0 0 16 16\"\n fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\">\n <path\n d=\"M10.6663 6V12.6667H5.33301V6H10.6663ZM9.66634 2H6.33301L5.66634 2.66667H3.33301V4H12.6663V2.66667H10.333L9.66634 2ZM11.9997 4.66667H3.99967V12.6667C3.99967 13.4 4.59967 14 5.33301 14H10.6663C11.3997 14 11.9997 13.4 11.9997 12.6667V4.66667Z\"\n fill=\"#F9BFBF\" />\n </svg>\n </cqa-button>\n </div>\n <div class=\"cqa-api-edit-step-key-value-add-wrap\">\n <cqa-button type=\"button\" variant=\"filled\" btnSize=\"lg\" text=\"+ Add Row\"\n customClass=\"cqa-api-edit-step-key-value-add-btn\" (clicked)=\"addParamsRow()\">\n </cqa-button>\n </div>\n </div>\n\n <!-- Payload content (Body only): type, format, text area, Back/Next -->\n <div *ngIf=\"activePayloadTab === 'body'\"\n class=\"cqa-api-edit-step-payload cqa-api-edit-step-payload-editor\">\n <div class=\"cqa-api-edit-step-payload-type-row\">\n <span class=\"cqa-api-edit-step-payload-type-label\">Type</span>\n <div class=\"cqa-api-edit-step-payload-type-radios\">\n <cqa-segment-control [value]=\"payloadType\" [segments]=\"payloadTypeSegments\"\n (valueChange)=\"onPayloadTypeChange($event)\"\n class=\"cqa-api-edit-step-payload-type-segment\">\n </cqa-segment-control>\n </div>\n <div *ngIf=\"payloadType === 'raw'\" class=\"cqa-api-edit-step-payload-format-wrap\">\n <span class=\"cqa-api-edit-step-payload-format-label\">Format:</span>\n <cqa-dynamic-select *ngIf=\"payloadFormatForm\" [form]=\"payloadFormatForm\"\n [config]=\"payloadFormatSelectConfig\" class=\"cqa-api-edit-step-payload-format-select\"\n aria-label=\"Format\">\n </cqa-dynamic-select>\n </div>\n </div>\n <!-- Raw: text area with line numbers (Postman-style payload body) -->\n <div *ngIf=\"payloadType === 'raw'\" class=\"cqa-api-edit-step-payload-body\" #payloadEditorWithLinesRef>\n <div class=\"cqa-api-edit-step-payload-editor-with-lines\">\n <div class=\"cqa-api-edit-step-payload-line-numbers\" aria-hidden=\"true\">\n <span *ngFor=\"let n of payloadLineNumbers\" class=\"cqa-api-edit-step-payload-line-num\">\n <span *ngIf=\"payloadJsonError && payloadJsonError.line === n\"\n class=\"cqa-api-edit-step-payload-line-error-icon\" [title]=\"payloadJsonErrorTooltip\"\n aria-label=\"Parse error on this line\">\n <svg xmlns=\"http://www.w3.org/2000/svg\" width=\"14\" height=\"14\" viewBox=\"0 0 14 14\" fill=\"none\" aria-hidden=\"true\">\n <circle cx=\"7\" cy=\"7\" r=\"6\" fill=\"#EF4444\"/>\n <path d=\"M4 4l6 6M10 4l-6 6\" stroke=\"white\" stroke-width=\"1.5\" stroke-linecap=\"round\"/>\n </svg>\n </span>\n <span class=\"cqa-api-edit-step-payload-line-num-text\">{{ n }}</span>\n </span>\n </div>\n <div class=\"cqa-api-edit-step-payload-textarea-cell\">\n <div class=\"cqa-api-edit-step-payload-textarea cqa-w-full cqa-flex cqa-flex-col cqa-min-h-0\"\n [ngClass]=\"{'cqa-api-edit-step-payload-textarea--error': payloadJsonError}\">\n <textarea #payloadTextareaRef\n class=\"cqa-api-edit-step-payload-textarea-input cqa-w-full cqa-outline-none cqa-box-border\"\n [ngClass]=\"{'cqa-api-edit-step-payload-textarea-input--error': payloadJsonError}\"\n [value]=\"payloadText\"\n placeholder=\"\"\n [attr.aria-label]=\"'Payload'\"\n [attr.aria-invalid]=\"!!payloadJsonError\"\n (input)=\"onPayloadInput($event)\"\n (keydown)=\"onPayloadKeydown($event)\">\n </textarea>\n </div>\n </div>\n </div>\n <p *ngIf=\"payloadJsonError\" class=\"cqa-api-edit-step-payload-json-error-msg\">\n Invalid JSON format. Please check your syntax.\n </p>\n </div>\n\n <!-- x-www-form-urlencoded: Key\u2013Value rows, add/remove dynamically -->\n <div *ngIf=\"payloadType === 'x-www-form-urlencoded'\" class=\"cqa-api-edit-step-key-value\">\n <div class=\"cqa-api-edit-step-key-value-grid cqa-api-edit-step-key-value-header\">\n <span class=\"cqa-api-edit-step-key-value-label\">Key</span>\n <span class=\"cqa-api-edit-step-key-value-label\">Value</span>\n <span class=\"cqa-api-edit-step-key-value-label cqa-api-edit-step-key-value-label--empty\"\n aria-hidden=\"true\"></span>\n </div>\n <div *ngFor=\"let row of keyValueRows; let i = index; trackBy: trackByKeyValue\" [formGroup]=\"row\"\n class=\"cqa-api-edit-step-key-value-row\">\n <cqa-custom-input [value]=\"row.get('key')?.value ?? ''\"\n (valueChange)=\"row.get('key')?.setValue($event)\" placeholder=\"Key\" [fullWidth]=\"true\" size=\"sm\"\n class=\"cqa-api-edit-step-key-value-input\" ariaLabel=\"Key\">\n </cqa-custom-input>\n <cqa-custom-input [value]=\"row.get('value')?.value ?? ''\"\n (valueChange)=\"row.get('value')?.setValue($event)\" placeholder=\"Value\" [fullWidth]=\"true\" size=\"sm\"\n class=\"cqa-api-edit-step-key-value-input\" ariaLabel=\"Value\">\n </cqa-custom-input>\n <cqa-button type=\"button\" variant=\"text\" [customClass]=\"'cqa-api-edit-step-key-value-delete'\"\n [tooltip]=\"'Remove row'\" (clicked)=\"removeKeyValueRow(i)\">\n <svg class=\"cqa-api-edit-step-key-value-delete-icon\" width=\"16\" height=\"16\" viewBox=\"0 0 16 16\"\n fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\">\n <path\n d=\"M10.6663 6V12.6667H5.33301V6H10.6663ZM9.66634 2H6.33301L5.66634 2.66667H3.33301V4H12.6663V2.66667H10.333L9.66634 2ZM11.9997 4.66667H3.99967V12.6667C3.99967 13.4 4.59967 14 5.33301 14H10.6663C11.3997 14 11.9997 13.4 11.9997 12.6667V4.66667Z\"\n fill=\"#F9BFBF\" />\n </svg>\n </cqa-button>\n </div>\n <div class=\"cqa-api-edit-step-key-value-add-wrap\">\n <cqa-button type=\"button\" variant=\"filled\" btnSize=\"lg\" text=\"+ Add Row\"\n customClass=\"cqa-api-edit-step-key-value-add-btn\" (clicked)=\"addKeyValueRow()\">\n </cqa-button>\n </div>\n </div>\n\n <!-- Form Data: Key\u2013Type\u2013Value rows; Type is a dropdown (Text/File), add/remove dynamically -->\n <div *ngIf=\"payloadType === 'form-data'\" class=\"cqa-api-edit-step-key-type-value\">\n <div class=\"cqa-api-edit-step-key-type-value-header\">\n <span class=\"cqa-api-edit-step-key-type-value-label\">Key</span>\n <span class=\"cqa-api-edit-step-key-type-value-label\">Type</span>\n <span class=\"cqa-api-edit-step-key-type-value-label\">Value</span>\n <span class=\"cqa-api-edit-step-key-type-value-label cqa-api-edit-step-key-type-value-label--empty\"\n aria-hidden=\"true\"></span>\n </div>\n <div *ngFor=\"let row of keyTypeValueRows; let i = index; trackBy: trackByKeyTypeValue\" [formGroup]=\"row\"\n class=\"cqa-api-edit-step-key-type-value-row\">\n <cqa-custom-input [value]=\"row.get('key')?.value ?? ''\"\n (valueChange)=\"row.get('key')?.setValue($event)\" placeholder=\"Key\" [fullWidth]=\"true\" size=\"sm\"\n class=\"cqa-api-edit-step-key-type-value-input\" ariaLabel=\"Key\">\n </cqa-custom-input>\n <cqa-dynamic-select [form]=\"row\" [config]=\"keyTypeValueTypeSelectConfig\"\n class=\"cqa-api-edit-step-key-type-value-type-select cqa-w-full\">\n </cqa-dynamic-select>\n <cqa-custom-input [value]=\"row.get('value')?.value ?? ''\"\n (valueChange)=\"row.get('value')?.setValue($event)\" placeholder=\"Value\" [fullWidth]=\"true\" size=\"sm\"\n class=\"cqa-api-edit-step-key-type-value-input\" ariaLabel=\"Value\">\n </cqa-custom-input>\n <cqa-button type=\"button\" variant=\"text\" [customClass]=\"'cqa-api-edit-step-key-type-value-delete'\"\n [tooltip]=\"'Remove row'\" (clicked)=\"removeKeyTypeValueRow(i)\">\n <svg class=\"cqa-api-edit-step-key-type-value-delete-icon\" width=\"16\" height=\"16\" viewBox=\"0 0 16 16\"\n fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\">\n <path\n d=\"M10.6663 6V12.6667H5.33301V6H10.6663ZM9.66634 2H6.33301L5.66634 2.66667H3.33301V4H12.6663V2.66667H10.333L9.66634 2ZM11.9997 4.66667H3.99967V12.6667C3.99967 13.4 4.59967 14 5.33301 14H10.6663C11.3997 14 11.9997 13.4 11.9997 12.6667V4.66667Z\"\n fill=\"#F9BFBF\" />\n </svg>\n </cqa-button>\n </div>\n <div class=\"cqa-api-edit-step-key-type-value-add-wrap\">\n <cqa-button type=\"button\" variant=\"filled\" btnSize=\"lg\" text=\"+ Add Row\"\n customClass=\"cqa-api-edit-step-key-type-value-add-btn\" (clicked)=\"addKeyTypeValueRow()\">\n </cqa-button>\n </div>\n </div>\n </div>\n </div>\n </ng-container>\n\n <!-- Advanced settings (step-level) -->\n <div class=\"cqa-mt-4\">\n <button\n type=\"button\"\n class=\"cqa-flex cqa-items-center cqa-justify-between cqa-w-full cqa-text-left cqa-text-[12px] cqa-font-medium cqa-text-gray-700 cqa-py-2 cqa-border-b cqa-border-gray-200\"\n (click)=\"advancedExpanded = !advancedExpanded\">\n <span class=\"cqa-text-[10px]\">Advanced</span>\n <mat-icon class=\"cqa-text-lg cqa-transition-transform\" [class.cqa-rotate-180]=\"advancedExpanded\">\n expand_more\n </mat-icon>\n </button>\n <div *ngIf=\"advancedExpanded\" class=\"cqa-mt-2\">\n <cqa-advanced-variables-form\n [advancedVariables]=\"advancedSettingsVariables\"\n [advancedVariableForm]=\"advancedVariablesForm\"\n (variableBooleanChange)=\"onAdvancedVariableBooleanChange($event.name, $event.value)\"\n (variableValueChange)=\"onAdvancedVariableValueChange($event.name, $event.value)\">\n </cqa-advanced-variables-form>\n </div>\n </div>\n\n </div>\n <!-- Step 2: Variable Name: input, validation, Back / Next -->\n <div *ngIf=\"currentStep === 2\" class=\"cqa-api-edit-step-panel cqa-flex cqa-flex-col cqa-gap-6 cqa-box-border cqa-w-full\">\n <div class=\"cqa-api-edit-step-variable-section\">\n <div class=\"cqa-api-edit-step-variable-input-wrap\">\n <cqa-custom-input [(value)]=\"variableName\" [label]=\"''\" placeholder=\"Variable Name\" [fullWidth]=\"true\"\n size=\"md\" (valueChange)=\"onVariableNameChange()\" aria-label=\"Variable Name\">\n </cqa-custom-input>\n </div>\n <p *ngIf=\"variableNameError\" class=\"cqa-api-edit-step-variable-error\" role=\"alert\">{{ variableNameError }}</p>\n </div>\n </div>\n <!-- Step 3: Response Body / Status tabs -->\n <div *ngIf=\"currentStep === 3\" class=\"cqa-api-edit-step-panel cqa-flex cqa-flex-col cqa-gap-6 cqa-box-border cqa-w-full\">\n <div class=\"cqa-api-edit-step-tabs-wrapper cqa-flex cqa-flex-col cqa-gap-3\">\n <div class=\"cqa-api-edit-step-tabs\">\n <cqa-button *ngFor=\"let tab of responseVerificationTabs\" type=\"button\" variant=\"text\" [text]=\"tab.label\"\n [customClass]=\"'cqa-api-edit-step-tab' + (activeResponseVerificationTab === tab.value ? ' cqa-api-edit-step-tab--active' : '')\"\n (clicked)=\"setResponseVerificationTab(tab.value)\">\n </cqa-button>\n </div>\n\n <!-- Response Body tab content: verification grid -->\n <div *ngIf=\"activeResponseVerificationTab === 'response-body'\" class=\"cqa-api-edit-step-step3-content\">\n <div class=\"cqa-api-edit-step-verification-header-row cqa-flex cqa-flex-row cqa-justify-end cqa-items-center cqa-pt-0 cqa-pr-0 cqa-pb-2 cqa-pl-0\">\n <span></span>\n <cqa-button type=\"button\" variant=\"text\" text=\"Add Verification\"\n customClass=\"cqa-api-edit-step-verification-add-link\" (clicked)=\"addVerificationRow()\">\n </cqa-button>\n </div>\n <div class=\"cqa-api-edit-step-verification\">\n <div class=\"cqa-api-edit-step-verification-grid cqa-api-edit-step-verification-grid-header\">\n <span class=\"cqa-api-edit-step-verification-label\">S.no</span>\n <span class=\"cqa-api-edit-step-verification-label\">JSON Path</span>\n <span class=\"cqa-api-edit-step-verification-label\">Verification</span>\n <span class=\"cqa-api-edit-step-verification-label\">Data Type</span>\n <span class=\"cqa-api-edit-step-verification-label\">Expected Value</span>\n <span class=\"cqa-api-edit-step-verification-label cqa-api-edit-step-verification-label--empty\"\n aria-hidden=\"true\"></span>\n </div>\n <div *ngFor=\"let row of verificationRows; let i = index; trackBy: trackByVerification\" [formGroup]=\"row\"\n class=\"cqa-api-edit-step-verification-row\">\n <span class=\"cqa-api-edit-step-verification-sno\">{{ i + 1 }}</span>\n <cqa-custom-input [value]=\"row.get('jsonPath')?.value ?? ''\"\n (valueChange)=\"row.get('jsonPath')?.setValue($event)\" placeholder=\"Json Path\" [fullWidth]=\"true\"\n size=\"sm\" class=\"cqa-api-edit-step-verification-input\" ariaLabel=\"JSON Path\">\n </cqa-custom-input>\n <cqa-dynamic-select [form]=\"row\" [config]=\"verificationSelectConfig\"\n class=\"cqa-api-edit-step-verification-select cqa-w-full\">\n </cqa-dynamic-select>\n <cqa-dynamic-select [form]=\"row\" [config]=\"verificationDataTypeSelectConfig\"\n class=\"cqa-api-edit-step-verification-select cqa-w-full\"\n (selectionChange)=\"onVerificationDataTypeChange(row, $event)\">\n </cqa-dynamic-select>\n <!-- Expected Value: text for String/Array/Object, number for Number, dropdown for Boolean -->\n <ng-container [ngSwitch]=\"row.get('dataType')?.value\">\n <cqa-dynamic-select *ngSwitchCase=\"'boolean'\" [form]=\"row\"\n [config]=\"verificationExpectedValueBooleanSelectConfig\"\n class=\"cqa-api-edit-step-verification-select cqa-api-edit-step-verification-expected-select cqa-w-full\">\n </cqa-dynamic-select>\n <cqa-custom-input *ngSwitchCase=\"'number'\" [value]=\"row.get('expectedValue')?.value ?? ''\"\n (valueChange)=\"row.get('expectedValue')?.setValue($event)\" placeholder=\"Expected Value (number)\"\n type=\"number\" [fullWidth]=\"true\" size=\"sm\"\n class=\"cqa-api-edit-step-verification-input cqa-api-edit-step-verification-expected-number\"\n ariaLabel=\"Expected Value\">\n </cqa-custom-input>\n <cqa-custom-input *ngSwitchDefault [value]=\"row.get('expectedValue')?.value ?? ''\"\n (valueChange)=\"row.get('expectedValue')?.setValue($event)\"\n placeholder=\"Expected Value in String\" [fullWidth]=\"true\" size=\"sm\"\n class=\"cqa-api-edit-step-verification-input\" ariaLabel=\"Expected Value\">\n </cqa-custom-input>\n </ng-container>\n <cqa-button type=\"button\" variant=\"text\" [customClass]=\"'cqa-api-edit-step-verification-delete'\"\n [tooltip]=\"'Remove row'\" (clicked)=\"removeVerificationRow(i)\">\n <svg class=\"cqa-api-edit-step-verification-delete-icon\" width=\"16\" height=\"16\" viewBox=\"0 0 16 16\"\n fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\">\n <path\n d=\"M10.6663 6V12.6667H5.33301V6H10.6663ZM9.66634 2H6.33301L5.66634 2.66667H3.33301V4H12.6663V2.66667H10.333L9.66634 2ZM11.9997 4.66667H3.99967V12.6667C3.99967 13.4 4.59967 14 5.33301 14H10.6663C11.3997 14 11.9997 13.4 11.9997 12.6667V4.66667Z\"\n fill=\"#F9BFBF\" />\n </svg>\n </cqa-button>\n </div>\n </div>\n </div>\n\n <!-- Status tab content: S.no, Verification dropdown, Expected Value, add/remove rows -->\n <div *ngIf=\"activeResponseVerificationTab === 'status'\" class=\"cqa-api-edit-step-step3-content\">\n <div class=\"cqa-api-edit-step-verification-header-row cqa-flex cqa-flex-row cqa-justify-end cqa-items-center cqa-pt-0 cqa-pr-0 cqa-pb-2 cqa-pl-0\">\n <span></span>\n <cqa-button type=\"button\" variant=\"text\" text=\"Add Verification\"\n customClass=\"cqa-api-edit-step-verification-add-link\" (clicked)=\"addStatusVerificationRow()\">\n </cqa-button>\n </div>\n <div class=\"cqa-api-edit-step-status-verification\">\n <div class=\"cqa-api-edit-step-status-verification-header\">\n <span class=\"cqa-api-edit-step-verification-label\">S.no</span>\n <span class=\"cqa-api-edit-step-verification-label\">Verification</span>\n <span class=\"cqa-api-edit-step-verification-label\">Expected Value</span>\n <span class=\"cqa-api-edit-step-verification-label cqa-api-edit-step-verification-label--empty\"\n aria-hidden=\"true\"></span>\n </div>\n <div *ngFor=\"let row of statusVerificationRows; let i = index; trackBy: trackByStatusVerification\"\n [formGroup]=\"row\" class=\"cqa-api-edit-step-status-verification-row\">\n <span class=\"cqa-api-edit-step-verification-sno\">{{ i + 1 }}</span>\n <cqa-dynamic-select [form]=\"row\" [config]=\"statusVerificationSelectConfig\"\n class=\"cqa-api-edit-step-status-verification-select cqa-w-full\">\n </cqa-dynamic-select>\n <cqa-custom-input [value]=\"row.get('expectedValue')?.value ?? ''\"\n (valueChange)=\"row.get('expectedValue')?.setValue($event)\" placeholder=\"Expected Value\"\n type=\"number\" [fullWidth]=\"true\" size=\"sm\" class=\"cqa-api-edit-step-verification-input\"\n ariaLabel=\"Expected Value\">\n </cqa-custom-input>\n <cqa-button type=\"button\" variant=\"text\" [customClass]=\"'cqa-api-edit-step-verification-delete'\"\n [tooltip]=\"'Remove row'\" (clicked)=\"removeStatusVerificationRow(i)\">\n <svg class=\"cqa-api-edit-step-verification-delete-icon\" width=\"16\" height=\"16\" viewBox=\"0 0 16 16\"\n fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\">\n <path\n d=\"M10.6663 6V12.6667H5.33301V6H10.6663ZM9.66634 2H6.33301L5.66634 2.66667H3.33301V4H12.6663V2.66667H10.333L9.66634 2ZM11.9997 4.66667H3.99967V12.6667C3.99967 13.4 4.59967 14 5.33301 14H10.6663C11.3997 14 11.9997 13.4 11.9997 12.6667V4.66667Z\"\n fill=\"#F9BFBF\" />\n </svg>\n </cqa-button>\n </div>\n </div>\n </div>\n </div>\n </div>\n </div>\n\n <!-- Response Preview -->\n <div #responsePreviewRef class=\"cqa-api-edit-step-response\">\n <h3 class=\"cqa-api-edit-step-response-title cqa-text-[14px] cqa-font-bold cqa-leading-[20px] cqa-text-[#111827] cqa-m-0 cqa-mb-[12px] cqa-shrink-0\">Response Preview</h3>\n <pre class=\"cqa-api-edit-step-response-content\">{{ responsePreview }}</pre>\n </div>\n\n <!-- Step actions: one row, full width. Step 1: Cancel + Next; Step 2: Back + Next; Step 3: Back + Create -->\n <div class=\"cqa-api-edit-step-actions\">\n <ng-container [ngSwitch]=\"currentStep\">\n <ng-container *ngSwitchCase=\"1\">\n <cqa-button type=\"button\" variant=\"outlined\" btnSize=\"lg\" text=\"Cancel\"\n customClass=\"cqa-api-edit-step-actions-btn-cancel\" (clicked)=\"onCancel()\">\n </cqa-button>\n <cqa-button type=\"button\" variant=\"filled\" btnSize=\"lg\" text=\"Next\"\n customClass=\"cqa-api-edit-step-actions-btn-next\" (clicked)=\"onNext()\">\n </cqa-button>\n </ng-container>\n <ng-container *ngSwitchCase=\"2\">\n <cqa-button type=\"button\" variant=\"outlined\" btnSize=\"lg\" text=\"Back\"\n customClass=\"cqa-api-edit-step-actions-btn-back\" (clicked)=\"onBack()\">\n </cqa-button>\n <cqa-button type=\"button\" variant=\"filled\" btnSize=\"lg\" text=\"Next\"\n customClass=\"cqa-api-edit-step-actions-btn-next\" (clicked)=\"onNext()\">\n </cqa-button>\n </ng-container>\n <ng-container *ngSwitchCase=\"3\">\n <cqa-button type=\"button\" variant=\"outlined\" btnSize=\"lg\" text=\"Back\"\n customClass=\"cqa-api-edit-step-actions-btn-back\" (clicked)=\"onBack()\">\n </cqa-button>\n <cqa-button *ngIf=\"!isCreatingStep\" type=\"button\" variant=\"filled\" btnSize=\"lg\" [text]=\"editMode ? 'Update' : 'Create'\"\n customClass=\"cqa-api-edit-step-actions-btn-create\" (clicked)=\"onCreate()\">\n </cqa-button>\n <!-- Match cqa-button flex: actions row styles > cqa-button { flex: 1 1 0 } \u2014 loader div must grow same width as Create -->\n <div *ngIf=\"isCreatingStep\" class=\"cqa-api-edit-step-actions-loader cqa-w-full cqa-min-h-[38px] cqa-rounded-[8px] cqa-flex cqa-items-center cqa-justify-center\">\n <cqa-badge label=\"Creating\u2026\" icon=\"autorenew\" [isLoading]=\"true\" [fullWidth]=\"true\" [centerContent]=\"true\"\n [inlineStyles]=\"'min-height: 38px; height: 38px; width: 100%; box-sizing: border-box; display: flex; align-items: center; justify-content: center;'\"\n variant=\"info\" size=\"medium\"></cqa-badge>\n </div>\n </ng-container>\n </ng-container>\n </div>\n</div>", components: [{ type: ButtonComponent, selector: "cqa-button", inputs: ["variant", "btnSize", "disabled", "loading", "icon", "iconPosition", "fullWidth", "iconColor", "type", "text", "customClass", "inlineStyles", "tooltip", "tooltipPosition"], outputs: ["clicked"] }, { type: CustomInputComponent, selector: "cqa-custom-input", inputs: ["inputId", "label", "type", "placeholder", "value", "disabled", "errors", "required", "ariaLabel", "size", "fullWidth", "maxLength", "showCharCount", "inputInlineStyle", "labelInlineStyle"], outputs: ["valueChange", "blurred", "focused", "enterPressed"] }, { type: DynamicSelectFieldComponent, selector: "cqa-dynamic-select", inputs: ["form", "config"], outputs: ["selectionChange", "selectClick", "searchChange", "loadMore", "addCustomValue"] }, { type: AutocompleteComponent, selector: "cqa-autocomplete", inputs: ["placeholder", "options", "value", "disabled", "showClear", "ariaLabel", "autoFocus", "size", "fullWidth", "compact"], outputs: ["valueChange", "optionSelect", "cleared"] }, { type: BadgeComponent, selector: "cqa-badge", inputs: ["type", "label", "icon", "iconLibrary", "variant", "size", "backgroundColor", "textColor", "borderColor", "iconBackgroundColor", "iconColor", "iconSize", "inlineStyles", "key", "value", "keyTextColor", "valueTextColor", "isLoading", "fullWidth", "centerContent", "title"] }, { type: CustomTextareaComponent, selector: "cqa-custom-textarea", inputs: ["label", "placeholder", "value", "enableMarkdown", "disabled", "errors", "required", "ariaLabel", "size", "fullWidth", "maxLength", "showCharCount", "rows", "cols", "resize", "textareaInlineStyle", "labelInlineStyle"], outputs: ["valueChange", "blurred", "focused"] }, { type: SegmentControlComponent, selector: "cqa-segment-control", inputs: ["segments", "value", "disabled", "containerBgColor", "fullWidth", "size"], outputs: ["valueChange"] }, { type: i1.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { type: AdvancedVariablesFormComponent, selector: "cqa-advanced-variables-form", inputs: ["advancedVariables", "advancedVariableForm"], outputs: ["variableBooleanChange", "variableValueChange"] }], directives: [{ type: i2.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { type: i2.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { type: i2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { type: i1$1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { type: i1$1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { type: i2.NgSwitch, selector: "[ngSwitch]", inputs: ["ngSwitch"] }, { type: i2.NgSwitchCase, selector: "[ngSwitchCase]", inputs: ["ngSwitchCase"] }, { type: i2.NgSwitchDefault, selector: "[ngSwitchDefault]" }], changeDetection: i0.ChangeDetectionStrategy.Default });
39404
+ ApiEditStepComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.4.0", ngImport: i0, type: ApiEditStepComponent, deps: [{ token: i1$1.FormBuilder }, { token: i0.ChangeDetectorRef }, { token: DialogService }], target: i0.ɵɵFactoryTarget.Component });
39405
+ ApiEditStepComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version: "13.4.0", type: ApiEditStepComponent, selector: "cqa-api-edit-step", inputs: { initialMethod: "initialMethod", initialEnvironment: "initialEnvironment", initialStep: "initialStep", initialUrl: "initialUrl", initialPayloadTab: "initialPayloadTab", initialPayloadType: "initialPayloadType", initialPayloadText: "initialPayloadText", initialSummary: "initialSummary", initialHeaders: "initialHeaders", initialResponsePreview: "initialResponsePreview", initialVariableName: "initialVariableName", initialResponseBodyVerifications: "initialResponseBodyVerifications", initialStatusVerifications: "initialStatusVerifications", initialActiveResponseVerificationTab: "initialActiveResponseVerificationTab", editMode: "editMode", isCreatingStep: "isCreatingStep", initialApiType: "initialApiType", initialGraphQL: "initialGraphQL", graphQLSchemaSource: "graphQLSchemaSource", graphQLOperations: "graphQLOperations", isLoadingGraphQLSchema: "isLoadingGraphQLSchema", httpMethodOptions: "httpMethodOptions", environmentOptions: "environmentOptions", hasMoreEnvironments: "hasMoreEnvironments", isLoadingEnvironments: "isLoadingEnvironments", urlOptions: "urlOptions", authTypeOptions: "authTypeOptions", initialAuthType: "initialAuthType", initialOAuth2: "initialOAuth2", formatOptions: "formatOptions", initialFormat: "initialFormat", headerNameOptions: "headerNameOptions", verificationOptions: "verificationOptions", verificationDataTypeOptions: "verificationDataTypeOptions", statusVerificationOptions: "statusVerificationOptions", advancedSettingsVariables: "advancedSettingsVariables" }, outputs: { importCurl: "importCurl", importCurlCancel: "importCurlCancel", sendRequest: "sendRequest", back: "back", cancel: "cancel", next: "next", create: "create", headersChange: "headersChange", environmentChange: "environmentChange", environmentSearch: "environmentSearch", environmentLoadMore: "environmentLoadMore", loadSchema: "loadSchema" }, host: { classAttribute: "cqa-ui-root" }, viewQueries: [{ propertyName: "payloadEditorWithLinesRef", first: true, predicate: ["payloadEditorWithLinesRef"], descendants: true }, { propertyName: "payloadTextareaRef", first: true, predicate: ["payloadTextareaRef"], descendants: true }, { propertyName: "responsePreviewRef", first: true, predicate: ["responsePreviewRef"], descendants: true }], usesOnChanges: true, ngImport: i0, template: "<div class=\"cqa-api-edit-step-container cqa-bg-[#ffffff] cqa-flex cqa-flex-col cqa-w-full cqa-h-full cqa-p-[16px] cqa-gap-[12px] cqa-overflow-y-auto cqa-overflow-x-hidden cqa-box-border\">\n\n <h2 class=\"cqa-api-edit-step-title cqa-font-inter cqa-text-[12px] cqa-font-semibold cqa-leading-[100%] cqa-tracking-normal cqa-text-[#000000] cqa-m-0\">\n {{ editMode ? 'Update API Test Step' : 'Create API Test Step' }}\n </h2>\n\n <div class=\"cqa-api-edit-step-indicator cqa-flex cqa-items-center cqa-gap-0 cqa-max-[767px]:cqa-flex-wrap cqa-max-[767px]:cqa-gap-2 cqa-max-[767px]:cqa-justify-start\"\n [class.cqa-justify-start]=\"stepLabels.length === 1\"\n [class.cqa-justify-between]=\"stepLabels.length !== 1\">\n <ng-container *ngFor=\"let step of stepLabels; let i = index\">\n <cqa-button type=\"button\" variant=\"text\"\n customClass=\"cqa-api-edit-step-indicator-item cqa-api-edit-step-indicator-item--clickable cqa-flex cqa-items-center cqa-gap-[10px] cqa-cursor-pointer cqa-border-none cqa-p-0 cqa-font-inherit cqa-text-inherit cqa-text-left !cqa-bg-transparent\"\n [attr.aria-label]=\"'Step ' + step.index + ': ' + step.label\" [attr.aria-pressed]=\"step.index === currentStep\"\n (clicked)=\"setStep(step.index)\">\n <span class=\"cqa-api-edit-step-indicator-circle cqa-w-8 cqa-h-8 cqa-rounded-full cqa-inline-flex cqa-items-center cqa-justify-center cqa-text-sm cqa-font-medium cqa-leading-none cqa-border-none cqa-shrink-0\"\n [ngClass]=\"step.index === currentStep ? 'cqa-bg-[#4F46E5] cqa-text-white' : 'cqa-bg-[#E0E0E0] cqa-text-[#666666]'\">\n {{ step.index }}\n </span>\n <span class=\"cqa-api-edit-step-indicator-label cqa-text-sm cqa-leading-[18px] cqa-font-normal cqa-max-[767px]:cqa-text-xs\"\n [ngClass]=\"step.index === currentStep ? 'cqa-text-[#4F46E5]' : 'cqa-text-[#666666]'\">\n {{ step.label }}\n </span>\n <div *ngIf=\"i < stepLabels.length - 1\" class=\"cqa-api-edit-step-indicator-line cqa-w-[96px] cqa-h-[2px] cqa-bg-[#E0E0E0] cqa-my-0 cqa-mx-[6px] cqa-shrink-0 cqa-self-center cqa-max-[767px]:cqa-w-[24px] cqa-max-[767px]:cqa-mx-1\" aria-hidden=\"true\"></div>\n </cqa-button>\n </ng-container>\n </div>\n\n <!-- Step content viewport: only the active step body is shown (*ngIf) -->\n <div class=\"cqa-api-edit-step-viewport cqa-w-full\">\n <!-- Step 1: Environment, request, body, response -->\n <div *ngIf=\"currentStep === 1\" class=\"cqa-api-edit-step-panel cqa-flex cqa-flex-col cqa-gap-6 cqa-box-border cqa-w-full\">\n <!-- API Type segment: REST API (default) | GraphQL | SOAP/WebSocket/gRPC (disabled) -->\n <div class=\"cqa-api-edit-step-api-type-row cqa-flex cqa-flex-col cqa-gap-1.5\">\n <label class=\"cqa-api-edit-step-api-type-label cqa-m-0 cqa-text-[14px] cqa-font-semibold cqa-leading-[100%] cqa-tracking-normal\">API Type</label>\n <cqa-segment-control\n [value]=\"activeApiType\"\n [segments]=\"apiTypeSegments\"\n (valueChange)=\"onApiTypeChange($event)\"\n class=\"cqa-api-edit-step-api-type-segment\">\n </cqa-segment-control>\n </div>\n\n <!-- Summary field: label on top, input below (above Environment) -->\n <div class=\"cqa-api-edit-step-summary-row cqa-flex cqa-flex-col cqa-gap-1.5 cqa-items-stretch\">\n <label class=\"cqa-api-edit-step-summary-label cqa-m-0 cqa-text-[14px] cqa-font-semibold cqa-leading-[100%] cqa-tracking-normal\">Summary</label>\n <cqa-custom-input [(value)]=\"summary\"\n [label]=\"''\" placeholder=\"Enter Request summary\" [fullWidth]=\"true\" size=\"md\"\n ariaLabel=\"Summary\">\n </cqa-custom-input>\n </div>\n <!-- Environment row: label on top, select below (aligned right on desktop) -->\n <div class=\"cqa-api-edit-step-environment-row cqa-flex cqa-justify-end cqa-gap-1.5 cqa-items-end cqa-max-[767px]:cqa-items-stretch\">\n <div *ngIf=\"activeApiType === 'graphql'\" class=\"cqa-api-edit-step-url-wrap cqa-flex-1 cqa-min-w-[200px] cqa-max-[767px]:cqa-min-w-0 cqa-max-[767px]:cqa-w-full\">\n <cqa-autocomplete\n [value]=\"url\"\n (valueChange)=\"url = $event\"\n [options]=\"urlOptions\"\n [placeholder]=\"urlPlaceholder\"\n ariaLabel=\"Enter URL or select Parameter\"\n [fullWidth]=\"true\"\n (optionSelect)=\"onUrlOptionSelect($event)\">\n </cqa-autocomplete>\n <div *ngIf=\"urlError\" class=\"cqa-text-red-600 cqa-text-sm cqa-mt-1\">\n {{ urlError }}\n </div>\n </div>\n\n <div class=\"cqa-flex cqa-flex-col cqa-gap-1.5 cqa-max-[767px]:cqa-items-stretch\">\n <label class=\"cqa-api-edit-step-environment-label cqa-m-0 cqa-text-[14px] cqa-font-semibold cqa-leading-[100%] cqa-tracking-normal\">Environments</label>\n <cqa-dynamic-select *ngIf=\"environmentForm\"\n [form]=\"environmentForm\"\n [config]=\"environmentSelectConfig\"\n class=\"cqa-api-edit-step-environment-select cqa-shrink-0 cqa-w-auto cqa-min-w-[315px] cqa-max-w-[315px] cqa-max-[767px]:cqa-min-w-0 cqa-max-[767px]:cqa-max-w-full cqa-max-[767px]:cqa-w-full [&_.mat-form-field-wrapper]:cqa-pb-0\"\n aria-label=\"Environment\"\n (selectionChange)=\"onEnvironmentSelectionChange($event)\">\n </cqa-dynamic-select>\n </div>\n </div>\n\n <!-- Request row: method, URL, buttons -->\n <div [ngClass]=\"{'cqa-items-start': activeApiType === 'rest', 'cqa-justify-between': activeApiType === 'graphql'}\" class=\"cqa-api-edit-step-request-row cqa-flex cqa-gap-3 cqa-flex-wrap cqa-max-[767px]:cqa-flex-col cqa-max-[767px]:cqa-items-stretch cqa-max-[767px]:cqa-gap-2.5\">\n <!-- REST mode shows the HTTP Method dropdown. GraphQL pins method to POST internally so the dropdown is hidden. -->\n <cqa-dynamic-select *ngIf=\"methodForm && activeApiType === 'rest'\" [form]=\"methodForm\" [config]=\"methodSelectConfig\"\n class=\"cqa-api-edit-step-method-select cqa-shrink-0 cqa-w-auto cqa-min-w-[152px] cqa-max-w-[152px] cqa-max-[767px]:cqa-min-w-0 cqa-max-[767px]:cqa-max-w-full cqa-max-[767px]:cqa-w-full\" aria-label=\"HTTP method\"\n (selectionChange)=\"onMethodSelectionChange($event)\">\n </cqa-dynamic-select>\n <div *ngIf=\"activeApiType === 'rest'\" class=\"cqa-api-edit-step-url-wrap cqa-flex-1 cqa-min-w-[200px] cqa-max-[767px]:cqa-min-w-0 cqa-max-[767px]:cqa-w-full\">\n <cqa-autocomplete\n [value]=\"url\"\n (valueChange)=\"url = $event\"\n [options]=\"urlOptions\"\n [placeholder]=\"urlPlaceholder\"\n ariaLabel=\"Enter URL or select Parameter\"\n [fullWidth]=\"true\"\n (optionSelect)=\"onUrlOptionSelect($event)\">\n </cqa-autocomplete>\n <div *ngIf=\"urlError\" class=\"cqa-text-red-600 cqa-text-sm cqa-mt-1\">\n {{ urlError }}\n </div>\n </div>\n\n <!-- Import API cURL: REST-only (the cURL parser produces REST shapes). -->\n <div *ngIf=\"activeApiType === 'rest'\" class=\"cqa-api-edit-step-import-curl-trigger cqa-contents cqa-max-[767px]:cqa-w-full cqa-max-[767px]:!cqa-flex\" (click)=\"openImportCurlPanel()\">\n <cqa-button type=\"button\" variant=\"outlined\" btnSize=\"lg\" text=\"Import API cURL\"\n customClass=\"cqa-api-edit-step-btn-outline !cqa-bg-white !cqa-border !cqa-border-solid !cqa-border-[#6B7280] cqa-text-[#414146] cqa-font-inter cqa-text-[14px] cqa-font-semibold cqa-leading-[100%] cqa-tracking-normal hover:!cqa-bg-[#F9FAFB] hover:!cqa-text-[#414146] cqa-max-[767px]:cqa-w-full cqa-mt-1\">\n </cqa-button>\n </div>\n <!--\n Load Schema: GraphQL-only. Uses the elevated cqa-button variant so the\n indigo surface + text + elevation shadow come from the design system \u2014\n customClass keeps only the typography + responsive sizing tweaks.\n Disabled / blocked styling is handled natively by the elevated variant\n (cqa-bg-primary-muted, muted text, no shadow), so we don't add an\n opacity override on top.\n -->\n <cqa-button *ngIf=\"activeApiType === 'graphql'\" type=\"button\" variant=\"elevated\" btnSize=\"lg\" text=\"Load Schema\"\n icon=\"schema\"\n customClass=\"cqa-font-inter cqa-text-[14px] cqa-font-semibold cqa-leading-[100%] cqa-tracking-normal cqa-max-[767px]:cqa-w-full cqa-mt-1\"\n [disabled]=\"!url || !url.trim() || isLoadingGraphQLSchema\"\n (clicked)=\"onLoadSchema()\">\n </cqa-button>\n <cqa-button variant=\"filled\" btnSize=\"lg\" text=\"Send Request\" (clicked)=\"onSendRequest()\"\n [disabled]=\"isSendRequestDisabled\"\n customClass=\"cqa-api-edit-step-btn-primary cqa-font-inter cqa-text-[14px] cqa-font-semibold cqa-leading-[100%] cqa-tracking-normal !cqa-bg-[#3F43EE] cqa-text-[#FBFCFF] !cqa-border-none cqa-py-2.5 cqa-px-6 cqa-gap-2 cqa-rounded-lg cqa-min-h-[37px] cqa-box-border hover:!cqa-bg-[#1B1FEB] cqa-max-[767px]:cqa-w-full cqa-mt-1\"\n [ngClass]=\"{'!cqa-opacity-50 !cqa-cursor-not-allowed': isSendRequestDisabled}\">\n </cqa-button>\n </div>\n\n\n\n <!-- Body content: header section (default) OR import cURL section -->\n <ng-container *ngIf=\"bodyView === 'import-curl'\">\n <div class=\"cqa-api-edit-step-import-curl-panel cqa-flex cqa-flex-col cqa-bg-white cqa-rounded-lg cqa-border cqa-border-solid cqa-border-[#E5E7EB] cqa-shadow-[0_1px_3px_rgba(0,0,0,0.08)] cqa-overflow-hidden\">\n <div class=\"cqa-api-edit-step-import-curl-header cqa-flex cqa-items-center cqa-justify-between cqa-py-3 cqa-px-5 cqa-border-b cqa-border-solid cqa-border-[#E2E8F0] cqa-bg-[#F9FAFB] cqa-rounded-t-lg cqa-max-[767px]:cqa-px-4\">\n <h3 class=\"cqa-api-edit-step-import-curl-title cqa-m-0 cqa-text-xs cqa-font-bold cqa-leading-[18px] cqa-text-[#374151]\">Import API cURL</h3>\n </div>\n <div class=\"cqa-api-edit-step-import-curl-separator cqa-h-px cqa-bg-[#E2E8F0] cqa-my-0 cqa-mx-5\"></div>\n <div class=\"cqa-api-edit-step-import-curl-note cqa-px-5 cqa-pt-3\">\n <cqa-badge label=\"cURL is supported in Bash format only.\" icon=\"info\" variant=\"info\" size=\"small\" [fullWidth]=\"true\"></cqa-badge>\n </div>\n <div class=\"cqa-api-edit-step-import-curl-body\">\n <cqa-custom-textarea [value]=\"importCurlControl.value\"\n (valueChange)=\"importCurlControl.setValue($event)\"\n placeholder=\"Paste your cURL command here (e.g., curl -X POST https://api.example.com/users)\"\n [fullWidth]=\"true\" [rows]=\"8\" resize=\"vertical\" size=\"md\"\n class=\"cqa-api-edit-step-import-curl-textarea\">\n </cqa-custom-textarea>\n </div>\n <div class=\"cqa-api-edit-step-import-curl-footer\">\n <cqa-button type=\"button\" variant=\"outlined\" btnSize=\"lg\" text=\"Cancel\"\n customClass=\"cqa-api-edit-step-import-curl-btn-cancel\" (clicked)=\"onCancelImportCurl()\"></cqa-button>\n <cqa-button type=\"button\" variant=\"filled\" btnSize=\"lg\" text=\"Import\"\n customClass=\"cqa-api-edit-step-import-curl-btn-import\" (clicked)=\"onImportCurlConfirm()\"></cqa-button>\n </div>\n </div>\n </ng-container>\n\n <!-- GraphQL operation picker \u2014 opened by the \"Load Schema\" button in GraphQL mode. -->\n <ng-container *ngIf=\"bodyView === 'graphql-schema'\">\n <cqa-graphql-operation-picker\n [schemaSource]=\"graphQLSchemaSource\"\n [operations]=\"graphQLOperations\"\n [introspected]=\"!!graphQLSchemaSource\"\n [isLoading]=\"isLoadingGraphQLSchema\"\n (back)=\"onGraphQLPickerBack()\"\n (change)=\"onGraphQLPickerBack()\"\n (reIntrospect)=\"onGraphQLPickerReIntrospect()\"\n (operationSelected)=\"onGraphQLPickerOperationSelected($event)\">\n </cqa-graphql-operation-picker>\n </ng-container>\n\n <ng-container *ngIf=\"bodyView === 'headers'\">\n <div class=\"cqa-api-edit-step-tabs-wrapper cqa-flex cqa-flex-col cqa-gap-3\">\n <div class=\"cqa-api-edit-step-tabs\">\n <cqa-button *ngFor=\"let tab of payloadTabs\" type=\"button\" variant=\"text\" [text]=\"tab.label\"\n [customClass]=\"'cqa-api-edit-step-tab' + (activePayloadTab === tab.value ? ' cqa-api-edit-step-tab--active' : '') + (isMethodWithoutBody && (tab.value === 'body' || tab.value === 'params') ? ' cqa-api-edit-step-tab--disabled' : '')\"\n [disabled]=\"isMethodWithoutBody && (tab.value === 'body' || tab.value === 'params')\"\n (clicked)=\"setPayloadTab(tab.value)\">\n </cqa-button>\n </div>\n\n <!-- Payload content (Authorization) -->\n <div *ngIf=\"activePayloadTab === 'authorization'\" class=\"cqa-api-edit-step-payload\"\n [attr.style]=\"'padding: 20px; min-height: 200px;'\">\n <div [attr.style]=\"'display: flex; flex-direction: column; gap: 20px;'\">\n <div [attr.style]=\"'display: flex; flex-direction: column; gap: 8px; width: fit-content;'\">\n <span [attr.style]=\"'font-size: 11px; font-weight: 600; letter-spacing: 0.02em; color: #6B7280; text-transform: uppercase; line-height: 1.25;'\">AUTH TYPE</span>\n <cqa-dynamic-select *ngIf=\"authTypeForm\" [form]=\"authTypeForm\" [config]=\"authTypeSelectConfig\"\n class=\"cqa-api-edit-step-auth-type-select\" aria-label=\"Auth type\"\n [attr.style]=\"'min-width: 160px;'\">\n </cqa-dynamic-select>\n </div>\n <!-- No Auth: centered message -->\n <div *ngIf=\"selectedAuthType === 'no-auth'\"\n [attr.style]=\"'flex: 1; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 12px; min-width: 0; text-align: center; border: 1px solid #E5E7EB; border-radius: 8px; padding-top: 16px; padding-bottom: 16px;'\">\n <div aria-hidden=\"true\"\n [attr.style]=\"'width: 48px; height: 48px; border-radius: 10px; background: #F3F4F6; display: flex; align-items: center; justify-content: center; flex-shrink: 0;'\">\n <svg width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\" style=\"display: block;\">\n <rect x=\"5\" y=\"11\" width=\"14\" height=\"2\" rx=\"1\" fill=\"#9CA3AF\"/>\n </svg>\n </div>\n <span [attr.style]=\"'font-size: 16px; font-weight: 600; color: #374151; line-height: 1.25;'\">No Auth</span>\n <div [attr.style]=\"'display: flex; align-items: center; justify-content: center; gap: 6px; flex-wrap: wrap;'\">\n <span [attr.style]=\"'font-size: 14px; font-weight: 400; color: #9CA3AF; line-height: 1.4;'\">This request does not use any authorization.</span>\n <span role=\"img\" aria-label=\"More information\" title=\"This request does not use any authorization.\"\n [attr.style]=\"'display: inline-flex; align-items: center; justify-content: center; width: 18px; height: 18px; color: #9CA3AF; flex-shrink: 0;'\">\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 16 16\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\" style=\"display: block;\">\n <circle cx=\"8\" cy=\"8\" r=\"7\" stroke=\"#9CA3AF\" stroke-width=\"1.5\" fill=\"none\"/>\n <path d=\"M8 7v4M8 5v.5\" stroke=\"#9CA3AF\" stroke-width=\"1.5\" stroke-linecap=\"round\"/>\n </svg>\n </span>\n </div>\n </div>\n <!-- Bearer Token: Token label + textarea -->\n <div *ngIf=\"selectedAuthType === 'bearer'\"\n [attr.style]=\"'flex: 1; display: flex; flex-direction: column; gap: 10px; min-width: 0;'\">\n <span [attr.style]=\"'font-size: 12px; font-weight: 600; color: #374151; line-height: 1.25;'\">Token</span>\n <cqa-custom-textarea [(value)]=\"bearerToken\"\n placeholder=\"Enter bearer token\"\n [fullWidth]=\"true\" [rows]=\"6\" resize=\"vertical\" size=\"md\"\n class=\"cqa-api-edit-step-auth-token-textarea\" ariaLabel=\"Bearer token\">\n </cqa-custom-textarea>\n </div>\n <!-- OAuth 2.0: Grant Type, Access Token URL, Client ID, Client Secret, Scope, Client Authentication -->\n <div *ngIf=\"selectedAuthType === 'oauth2' && oauth2Form\"\n class=\"cqa-api-edit-step-oauth2-grid\"\n [attr.style]=\"'display: grid; grid-template-columns: 1fr 1fr; gap: 16px 24px; width: 100%;'\">\n <div [attr.style]=\"'display: flex; flex-direction: column; gap: 6px; min-width: 0;'\">\n <span [attr.style]=\"'font-size: 12px; font-weight: 600; color: #374151; line-height: 1.25;'\">Grant Type</span>\n <cqa-dynamic-select *ngIf=\"oauth2Form\" [form]=\"oauth2Form\" [config]=\"oauth2GrantTypeSelectConfig\"\n class=\"cqa-api-edit-step-auth-oauth-select\" aria-label=\"OAuth grant type\"\n [attr.style]=\"'min-width: 0; width: 100%;'\">\n </cqa-dynamic-select>\n <span [attr.style]=\"'font-size: 11px; font-style: italic; color: #6B7280; line-height: 1.3;'\">OAuth grant type</span>\n </div>\n <div [attr.style]=\"'display: flex; flex-direction: column; gap: 6px; min-width: 0;'\">\n <span [attr.style]=\"'font-size: 12px; font-weight: 600; color: #374151; line-height: 1.25;'\">Access Token URL</span>\n <cqa-custom-input [value]=\"oauth2Form.get('accessTokenUrl')?.value\"\n (valueChange)=\"oauth2Form.get('accessTokenUrl')?.setValue($event)\"\n [label]=\"''\" placeholder=\"Enter OAuth token endpoint\" [fullWidth]=\"true\" size=\"md\"\n ariaLabel=\"Access Token URL\">\n </cqa-custom-input>\n <span [attr.style]=\"'font-size: 11px; font-style: italic; color: #6B7280; line-height: 1.3;'\">OAuth token endpoint</span>\n </div>\n <div [attr.style]=\"'display: flex; flex-direction: column; gap: 6px; min-width: 0;'\">\n <span [attr.style]=\"'font-size: 12px; font-weight: 600; color: #374151; line-height: 1.25;'\">Client ID</span>\n <cqa-custom-input [value]=\"oauth2Form.get('clientId')?.value\"\n (valueChange)=\"oauth2Form.get('clientId')?.setValue($event)\"\n [label]=\"''\" placeholder=\"Enter OAuth client identifier\" [fullWidth]=\"true\" size=\"md\"\n ariaLabel=\"Client ID\">\n </cqa-custom-input>\n <span [attr.style]=\"'font-size: 11px; font-style: italic; color: #6B7280; line-height: 1.3;'\">OAuth client identifier</span>\n </div>\n <div [attr.style]=\"'display: flex; flex-direction: column; gap: 6px; min-width: 0;'\">\n <span [attr.style]=\"'font-size: 12px; font-weight: 600; color: #374151; line-height: 1.25;'\">Client Secret</span>\n <cqa-custom-input [value]=\"oauth2Form.get('clientSecret')?.value\"\n (valueChange)=\"oauth2Form.get('clientSecret')?.setValue($event)\"\n type=\"password\" [label]=\"''\" placeholder=\"Enter OAuth client secret\" [fullWidth]=\"true\" size=\"md\"\n ariaLabel=\"Client Secret\">\n </cqa-custom-input>\n <span [attr.style]=\"'font-size: 11px; font-style: italic; color: #6B7280; line-height: 1.3;'\">OAuth client secret</span>\n </div>\n <div [attr.style]=\"'display: flex; flex-direction: column; gap: 6px; min-width: 0;'\">\n <span [attr.style]=\"'font-size: 12px; font-weight: 600; color: #374151; line-height: 1.25;'\">Scope</span>\n <cqa-custom-input [value]=\"oauth2Form.get('scope')?.value\"\n (valueChange)=\"oauth2Form.get('scope')?.setValue($event)\"\n [label]=\"''\" placeholder=\"Enter space-separated scopes\" [fullWidth]=\"true\" size=\"md\"\n ariaLabel=\"Scope\">\n </cqa-custom-input>\n <span [attr.style]=\"'font-size: 11px; font-style: italic; color: #6B7280; line-height: 1.3;'\">Space-separated scopes</span>\n </div>\n <div [attr.style]=\"'display: flex; flex-direction: column; gap: 6px; min-width: 0;'\">\n <span [attr.style]=\"'font-size: 12px; font-weight: 600; color: #374151; line-height: 1.25;'\">Client Authentication</span>\n <cqa-dynamic-select *ngIf=\"oauth2Form\" [form]=\"oauth2Form\" [config]=\"oauth2ClientAuthSelectConfig\"\n class=\"cqa-api-edit-step-auth-oauth-select\" aria-label=\"Client authentication method\"\n [attr.style]=\"'min-width: 0; width: 100%;'\">\n </cqa-dynamic-select>\n <span [attr.style]=\"'font-size: 11px; font-style: italic; color: #6B7280; line-height: 1.3;'\">Client authentication method</span>\n </div>\n </div>\n </div>\n </div>\n\n <!-- Payload content (Headers) -->\n <div *ngIf=\"activePayloadTab === 'headers'\" class=\"cqa-api-edit-step-payload\">\n <div class=\"cqa-api-edit-step-headers-grid\">\n <span class=\"cqa-api-edit-step-headers-label\">Header Name</span>\n <span class=\"cqa-api-edit-step-headers-label\">Header Value</span>\n <span class=\"cqa-api-edit-step-headers-label cqa-api-edit-step-headers-label--empty\"\n aria-hidden=\"true\"></span>\n </div>\n <div *ngFor=\"let row of headerRows; let i = index; trackBy: trackByHeader\" [formGroup]=\"row\"\n class=\"cqa-api-edit-step-header-row\">\n <cqa-dynamic-select [form]=\"row\" [config]=\"headerNameSelectConfig\"\n (addCustomValue)=\"onHeaderNameAddCustomValue($event, row)\"\n class=\"cqa-api-edit-step-header-type-select cqa-w-full\">\n </cqa-dynamic-select>\n <cqa-custom-input [value]=\"row.get('value')?.value ?? ''\"\n (valueChange)=\"row.get('value')?.setValue($event)\" placeholder=\"Value\" [fullWidth]=\"true\" size=\"sm\"\n class=\"cqa-api-edit-step-header-value-input\" ariaLabel=\"Header value\">\n </cqa-custom-input>\n <cqa-button type=\"button\" variant=\"text\" [customClass]=\"'cqa-api-edit-step-header-delete'\"\n [tooltip]=\"'Remove header'\" (clicked)=\"removeHeader(i)\">\n <svg class=\"cqa-api-edit-step-header-delete-icon\" width=\"16\" height=\"16\" viewBox=\"0 0 16 16\"\n fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\">\n <path\n d=\"M10.6663 6V12.6667H5.33301V6H10.6663ZM9.66634 2H6.33301L5.66634 2.66667H3.33301V4H12.6663V2.66667H10.333L9.66634 2ZM11.9997 4.66667H3.99967V12.6667C3.99967 13.4 4.59967 14 5.33301 14H10.6663C11.3997 14 11.9997 13.4 11.9997 12.6667V4.66667Z\"\n fill=\"#F9BFBF\" />\n </svg>\n </cqa-button>\n </div>\n <div class=\"cqa-api-edit-step-add-header-wrap\">\n <cqa-button type=\"button\" variant=\"text\" text=\"+ Add Header\"\n [customClass]=\"'cqa-api-edit-step-add-header-link'\" (clicked)=\"addHeader()\">\n </cqa-button>\n </div>\n </div>\n\n <!-- Payload content (Params): Key\u2013Value table with add/delete rows. REST-only \u2014 GraphQL POST has no query params. -->\n <div *ngIf=\"activePayloadTab === 'params' && activeApiType === 'rest'\" class=\"cqa-api-edit-step-payload\">\n <div class=\"cqa-api-edit-step-key-value-grid cqa-api-edit-step-key-value-header\">\n <span class=\"cqa-api-edit-step-key-value-label\">Key</span>\n <span class=\"cqa-api-edit-step-key-value-label\">Value</span>\n <span class=\"cqa-api-edit-step-key-value-label cqa-api-edit-step-key-value-label--empty\"\n aria-hidden=\"true\"></span>\n </div>\n <div *ngFor=\"let row of paramsRows; let i = index; trackBy: trackByParams\" [formGroup]=\"row\"\n class=\"cqa-api-edit-step-key-value-row\">\n <cqa-custom-input [value]=\"row.get('key')?.value ?? ''\"\n (valueChange)=\"row.get('key')?.setValue($event)\" placeholder=\"Key\" [fullWidth]=\"true\" size=\"sm\"\n class=\"cqa-api-edit-step-key-value-input\" ariaLabel=\"Key\">\n </cqa-custom-input>\n <cqa-custom-input [value]=\"row.get('value')?.value ?? ''\"\n (valueChange)=\"row.get('value')?.setValue($event)\" placeholder=\"Value\" [fullWidth]=\"true\" size=\"sm\"\n class=\"cqa-api-edit-step-key-value-input\" ariaLabel=\"Value\">\n </cqa-custom-input>\n <cqa-button type=\"button\" variant=\"text\" [customClass]=\"'cqa-api-edit-step-key-value-delete'\"\n [tooltip]=\"'Remove row'\" (clicked)=\"removeParamsRow(i)\">\n <svg class=\"cqa-api-edit-step-key-value-delete-icon\" width=\"16\" height=\"16\" viewBox=\"0 0 16 16\"\n fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\">\n <path\n d=\"M10.6663 6V12.6667H5.33301V6H10.6663ZM9.66634 2H6.33301L5.66634 2.66667H3.33301V4H12.6663V2.66667H10.333L9.66634 2ZM11.9997 4.66667H3.99967V12.6667C3.99967 13.4 4.59967 14 5.33301 14H10.6663C11.3997 14 11.9997 13.4 11.9997 12.6667V4.66667Z\"\n fill=\"#F9BFBF\" />\n </svg>\n </cqa-button>\n </div>\n <div class=\"cqa-api-edit-step-key-value-add-wrap\">\n <cqa-button type=\"button\" variant=\"filled\" btnSize=\"lg\" text=\"+ Add Row\"\n customClass=\"cqa-api-edit-step-key-value-add-btn\" (clicked)=\"addParamsRow()\">\n </cqa-button>\n </div>\n </div>\n\n <!-- Payload content (Body only): type, format, text area, Back/Next. REST-only \u2014 GraphQL uses the dedicated tab below. -->\n <div *ngIf=\"activePayloadTab === 'body' && activeApiType === 'rest'\"\n class=\"cqa-api-edit-step-payload cqa-api-edit-step-payload-editor\">\n <div class=\"cqa-api-edit-step-payload-type-row\">\n <span class=\"cqa-api-edit-step-payload-type-label\">Type</span>\n <div class=\"cqa-api-edit-step-payload-type-radios\">\n <cqa-segment-control [value]=\"payloadType\" [segments]=\"payloadTypeSegments\"\n (valueChange)=\"onPayloadTypeChange($event)\"\n class=\"cqa-api-edit-step-payload-type-segment\">\n </cqa-segment-control>\n </div>\n <div *ngIf=\"payloadType === 'raw'\" class=\"cqa-api-edit-step-payload-format-wrap\">\n <span class=\"cqa-api-edit-step-payload-format-label\">Format:</span>\n <cqa-dynamic-select *ngIf=\"payloadFormatForm\" [form]=\"payloadFormatForm\"\n [config]=\"payloadFormatSelectConfig\" class=\"cqa-api-edit-step-payload-format-select\"\n aria-label=\"Format\">\n </cqa-dynamic-select>\n </div>\n </div>\n <!-- Raw: text area with line numbers (Postman-style payload body) -->\n <div *ngIf=\"payloadType === 'raw'\" class=\"cqa-api-edit-step-payload-body\" #payloadEditorWithLinesRef>\n <div class=\"cqa-api-edit-step-payload-editor-with-lines\">\n <div class=\"cqa-api-edit-step-payload-line-numbers\" aria-hidden=\"true\">\n <span *ngFor=\"let n of payloadLineNumbers\" class=\"cqa-api-edit-step-payload-line-num\">\n <span *ngIf=\"payloadJsonError && payloadJsonError.line === n\"\n class=\"cqa-api-edit-step-payload-line-error-icon\" [title]=\"payloadJsonErrorTooltip\"\n aria-label=\"Parse error on this line\">\n <svg xmlns=\"http://www.w3.org/2000/svg\" width=\"14\" height=\"14\" viewBox=\"0 0 14 14\" fill=\"none\" aria-hidden=\"true\">\n <circle cx=\"7\" cy=\"7\" r=\"6\" fill=\"#EF4444\"/>\n <path d=\"M4 4l6 6M10 4l-6 6\" stroke=\"white\" stroke-width=\"1.5\" stroke-linecap=\"round\"/>\n </svg>\n </span>\n <span class=\"cqa-api-edit-step-payload-line-num-text\">{{ n }}</span>\n </span>\n </div>\n <div class=\"cqa-api-edit-step-payload-textarea-cell\">\n <div class=\"cqa-api-edit-step-payload-textarea cqa-w-full cqa-flex cqa-flex-col cqa-min-h-0\"\n [ngClass]=\"{'cqa-api-edit-step-payload-textarea--error': payloadJsonError}\">\n <textarea #payloadTextareaRef\n class=\"cqa-api-edit-step-payload-textarea-input cqa-w-full cqa-outline-none cqa-box-border\"\n [ngClass]=\"{'cqa-api-edit-step-payload-textarea-input--error': payloadJsonError}\"\n [value]=\"payloadText\"\n placeholder=\"\"\n [attr.aria-label]=\"'Payload'\"\n [attr.aria-invalid]=\"!!payloadJsonError\"\n (input)=\"onPayloadInput($event)\"\n (keydown)=\"onPayloadKeydown($event)\">\n </textarea>\n </div>\n </div>\n </div>\n <p *ngIf=\"payloadJsonError\" class=\"cqa-api-edit-step-payload-json-error-msg\">\n Invalid JSON format. Please check your syntax.\n </p>\n </div>\n\n <!-- x-www-form-urlencoded: Key\u2013Value rows, add/remove dynamically -->\n <div *ngIf=\"payloadType === 'x-www-form-urlencoded'\" class=\"cqa-api-edit-step-key-value\">\n <div class=\"cqa-api-edit-step-key-value-grid cqa-api-edit-step-key-value-header\">\n <span class=\"cqa-api-edit-step-key-value-label\">Key</span>\n <span class=\"cqa-api-edit-step-key-value-label\">Value</span>\n <span class=\"cqa-api-edit-step-key-value-label cqa-api-edit-step-key-value-label--empty\"\n aria-hidden=\"true\"></span>\n </div>\n <div *ngFor=\"let row of keyValueRows; let i = index; trackBy: trackByKeyValue\" [formGroup]=\"row\"\n class=\"cqa-api-edit-step-key-value-row\">\n <cqa-custom-input [value]=\"row.get('key')?.value ?? ''\"\n (valueChange)=\"row.get('key')?.setValue($event)\" placeholder=\"Key\" [fullWidth]=\"true\" size=\"sm\"\n class=\"cqa-api-edit-step-key-value-input\" ariaLabel=\"Key\">\n </cqa-custom-input>\n <cqa-custom-input [value]=\"row.get('value')?.value ?? ''\"\n (valueChange)=\"row.get('value')?.setValue($event)\" placeholder=\"Value\" [fullWidth]=\"true\" size=\"sm\"\n class=\"cqa-api-edit-step-key-value-input\" ariaLabel=\"Value\">\n </cqa-custom-input>\n <cqa-button type=\"button\" variant=\"text\" [customClass]=\"'cqa-api-edit-step-key-value-delete'\"\n [tooltip]=\"'Remove row'\" (clicked)=\"removeKeyValueRow(i)\">\n <svg class=\"cqa-api-edit-step-key-value-delete-icon\" width=\"16\" height=\"16\" viewBox=\"0 0 16 16\"\n fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\">\n <path\n d=\"M10.6663 6V12.6667H5.33301V6H10.6663ZM9.66634 2H6.33301L5.66634 2.66667H3.33301V4H12.6663V2.66667H10.333L9.66634 2ZM11.9997 4.66667H3.99967V12.6667C3.99967 13.4 4.59967 14 5.33301 14H10.6663C11.3997 14 11.9997 13.4 11.9997 12.6667V4.66667Z\"\n fill=\"#F9BFBF\" />\n </svg>\n </cqa-button>\n </div>\n <div class=\"cqa-api-edit-step-key-value-add-wrap\">\n <cqa-button type=\"button\" variant=\"filled\" btnSize=\"lg\" text=\"+ Add Row\"\n customClass=\"cqa-api-edit-step-key-value-add-btn\" (clicked)=\"addKeyValueRow()\">\n </cqa-button>\n </div>\n </div>\n\n <!-- Form Data: Key\u2013Type\u2013Value rows; Type is a dropdown (Text/File), add/remove dynamically -->\n <div *ngIf=\"payloadType === 'form-data'\" class=\"cqa-api-edit-step-key-type-value\">\n <div class=\"cqa-api-edit-step-key-type-value-header\">\n <span class=\"cqa-api-edit-step-key-type-value-label\">Key</span>\n <span class=\"cqa-api-edit-step-key-type-value-label\">Type</span>\n <span class=\"cqa-api-edit-step-key-type-value-label\">Value</span>\n <span class=\"cqa-api-edit-step-key-type-value-label cqa-api-edit-step-key-type-value-label--empty\"\n aria-hidden=\"true\"></span>\n </div>\n <div *ngFor=\"let row of keyTypeValueRows; let i = index; trackBy: trackByKeyTypeValue\" [formGroup]=\"row\"\n class=\"cqa-api-edit-step-key-type-value-row\">\n <cqa-custom-input [value]=\"row.get('key')?.value ?? ''\"\n (valueChange)=\"row.get('key')?.setValue($event)\" placeholder=\"Key\" [fullWidth]=\"true\" size=\"sm\"\n class=\"cqa-api-edit-step-key-type-value-input\" ariaLabel=\"Key\">\n </cqa-custom-input>\n <cqa-dynamic-select [form]=\"row\" [config]=\"keyTypeValueTypeSelectConfig\"\n class=\"cqa-api-edit-step-key-type-value-type-select cqa-w-full\">\n </cqa-dynamic-select>\n <cqa-custom-input [value]=\"row.get('value')?.value ?? ''\"\n (valueChange)=\"row.get('value')?.setValue($event)\" placeholder=\"Value\" [fullWidth]=\"true\" size=\"sm\"\n class=\"cqa-api-edit-step-key-type-value-input\" ariaLabel=\"Value\">\n </cqa-custom-input>\n <cqa-button type=\"button\" variant=\"text\" [customClass]=\"'cqa-api-edit-step-key-type-value-delete'\"\n [tooltip]=\"'Remove row'\" (clicked)=\"removeKeyTypeValueRow(i)\">\n <svg class=\"cqa-api-edit-step-key-type-value-delete-icon\" width=\"16\" height=\"16\" viewBox=\"0 0 16 16\"\n fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\">\n <path\n d=\"M10.6663 6V12.6667H5.33301V6H10.6663ZM9.66634 2H6.33301L5.66634 2.66667H3.33301V4H12.6663V2.66667H10.333L9.66634 2ZM11.9997 4.66667H3.99967V12.6667C3.99967 13.4 4.59967 14 5.33301 14H10.6663C11.3997 14 11.9997 13.4 11.9997 12.6667V4.66667Z\"\n fill=\"#F9BFBF\" />\n </svg>\n </cqa-button>\n </div>\n <div class=\"cqa-api-edit-step-key-type-value-add-wrap\">\n <cqa-button type=\"button\" variant=\"filled\" btnSize=\"lg\" text=\"+ Add Row\"\n customClass=\"cqa-api-edit-step-key-type-value-add-btn\" (clicked)=\"addKeyTypeValueRow()\">\n </cqa-button>\n </div>\n </div>\n </div>\n\n <!-- Payload content (GraphQL): Query + Variables + Operation Name. Active only in GraphQL mode. -->\n <div *ngIf=\"activePayloadTab === 'graphql' && activeApiType === 'graphql' && graphQLForm\"\n class=\"cqa-api-edit-step-payload-graphql cqa-api-edit-step-graphql-tab cqa-flex cqa-flex-col cqa-gap-4\">\n <!--\n Query / Variables use the design-system `cqa-custom-textarea` so\n styling, label, error rendering, and accessibility come from the\n primitive. We bind via [value] / (valueChange) instead of\n formControlName because cqa-custom-textarea isn't a CVA \u2014 calling\n form.get('\u2026').setValue still fires the existing valueChanges\n subscriptions (which clear inline errors and recompute the\n multi-operation warning).\n -->\n <cqa-custom-textarea\n label=\"Query\"\n [required]=\"true\"\n [value]=\"graphQLForm.get('query')?.value || ''\"\n (valueChange)=\"graphQLForm.get('query')?.setValue($event)\"\n placeholder=\"query GetX($id: ID!) {&#10; x(id: $id) { name }&#10;}\"\n ariaLabel=\"GraphQL query\"\n [errors]=\"graphQLQueryError ? [graphQLQueryError] : []\"\n [fullWidth]=\"true\"\n [rows]=\"4\"\n resize=\"vertical\"\n size=\"md\">\n </cqa-custom-textarea>\n <div class=\"cqa-flex cqa-gap-1.5\">\n <cqa-custom-textarea\n class=\"cqa-flex-1\"\n label=\"Variables (JSON)\"\n [value]=\"graphQLForm.get('variables')?.value || ''\"\n (valueChange)=\"graphQLForm.get('variables')?.setValue($event)\"\n placeholder='{ \"id\": \"abc\" }'\n ariaLabel=\"GraphQL variables\"\n [errors]=\"graphQLVariablesError ? [graphQLVariablesError] : []\"\n [fullWidth]=\"true\"\n [rows]=\"3\"\n resize=\"vertical\"\n size=\"md\">\n </cqa-custom-textarea>\n <!-- Operation Name: optional unless 2+ operations are declared. -->\n <div class=\"cqa-api-edit-step-graphql-field cqa-flex cqa-flex-col cqa-gap-1.5\">\n <label class=\"cqa-api-edit-step-graphql-label cqa-m-0 cqa-text-[14px] cqa-font-semibold cqa-text-[#374151]\">Operation Name</label>\n <cqa-custom-input\n [value]=\"graphQLForm.get('operationName')?.value || ''\"\n (valueChange)=\"graphQLForm.get('operationName')?.setValue($event)\"\n [label]=\"''\" placeholder=\"e.g. GetBooking\" [fullWidth]=\"true\" size=\"md\"\n ariaLabel=\"GraphQL operation name\">\n </cqa-custom-input>\n <p *ngIf=\"graphQLOperationWarning\" class=\"cqa-text-[#B45309] cqa-text-[12px] cqa-mt-1\">\n {{ graphQLOperationWarning }}\n </p>\n <div class=\"cqa-flex cqa-flex-row cqa-items-center cqa-gap-1.5 cqa-text-[12px] cqa-p-2\" style=\"border-radius: 8px; border: 1px solid #EDE9FE; background: #F5F3FF;\">\n <div>\n <mat-icon style=\"display:flex;font-size:14px;font-weight: 600;\" class=\"cqa-flex cqa-items-center cqa-justify-center cqa-text-[14px] cqa-text-[#7C3AED]\" >lightbulb</mat-icon>\n </div>\n <div class=\"cqa-text-[#7C3AED]\">\n Use <span style=\"background-color: white; color:black; border-radius: 4px; padding: 2px;\">var</span> in variables to bind from earlier steps.\n </div>\n </div>\n </div>\n </div>\n </div>\n </div>\n </ng-container>\n\n <!-- Advanced settings (step-level). Hidden while the GraphQL picker is open so it doesn't compete for focus with the operation list. -->\n <div *ngIf=\"bodyView !== 'graphql-schema'\">\n <button\n type=\"button\"\n class=\"cqa-flex cqa-items-center cqa-justify-between cqa-w-full cqa-text-left cqa-text-[12px] cqa-font-medium cqa-text-gray-700 cqa-py-2 cqa-border-b cqa-border-gray-200\"\n (click)=\"advancedExpanded = !advancedExpanded\">\n <span class=\"cqa-text-[10px]\">Advanced</span>\n <mat-icon class=\"cqa-text-lg cqa-transition-transform\" [class.cqa-rotate-180]=\"advancedExpanded\">\n expand_more\n </mat-icon>\n </button>\n <div *ngIf=\"advancedExpanded\" class=\"cqa-mt-2\">\n <cqa-advanced-variables-form\n [advancedVariables]=\"advancedSettingsVariables\"\n [advancedVariableForm]=\"advancedVariablesForm\"\n (variableBooleanChange)=\"onAdvancedVariableBooleanChange($event.name, $event.value)\"\n (variableValueChange)=\"onAdvancedVariableValueChange($event.name, $event.value)\">\n </cqa-advanced-variables-form>\n </div>\n </div>\n\n </div>\n <!-- Step 2: Variable Name: input, validation, Back / Next -->\n <div *ngIf=\"currentStep === 2\" class=\"cqa-api-edit-step-panel cqa-flex cqa-flex-col cqa-gap-6 cqa-box-border cqa-w-full\">\n <div class=\"cqa-api-edit-step-variable-section\">\n <div class=\"cqa-api-edit-step-variable-input-wrap\">\n <cqa-custom-input [(value)]=\"variableName\" [label]=\"''\" placeholder=\"Variable Name\" [fullWidth]=\"true\"\n size=\"md\" (valueChange)=\"onVariableNameChange()\" aria-label=\"Variable Name\">\n </cqa-custom-input>\n </div>\n <p *ngIf=\"variableNameError\" class=\"cqa-api-edit-step-variable-error\" role=\"alert\">{{ variableNameError }}</p>\n </div>\n </div>\n <!-- Step 3: Response Body / Status tabs -->\n <div *ngIf=\"currentStep === 3\" class=\"cqa-api-edit-step-panel cqa-flex cqa-flex-col cqa-gap-6 cqa-box-border cqa-w-full\">\n <div class=\"cqa-api-edit-step-tabs-wrapper cqa-flex cqa-flex-col cqa-gap-3\">\n <div class=\"cqa-api-edit-step-tabs\">\n <cqa-button *ngFor=\"let tab of responseVerificationTabs\" type=\"button\" variant=\"text\" [text]=\"tab.label\"\n [customClass]=\"'cqa-api-edit-step-tab' + (activeResponseVerificationTab === tab.value ? ' cqa-api-edit-step-tab--active' : '')\"\n (clicked)=\"setResponseVerificationTab(tab.value)\">\n </cqa-button>\n </div>\n\n <!-- Response Body tab content: verification grid -->\n <div *ngIf=\"activeResponseVerificationTab === 'response-body'\" class=\"cqa-api-edit-step-step3-content\">\n <div class=\"cqa-api-edit-step-verification-header-row cqa-flex cqa-flex-row cqa-justify-end cqa-items-center cqa-pt-0 cqa-pr-0 cqa-pb-2 cqa-pl-0\">\n <span></span>\n <cqa-button type=\"button\" variant=\"text\" text=\"Add Verification\"\n customClass=\"cqa-api-edit-step-verification-add-link\" (clicked)=\"addVerificationRow()\">\n </cqa-button>\n </div>\n <div class=\"cqa-api-edit-step-verification\">\n <div class=\"cqa-api-edit-step-verification-grid cqa-api-edit-step-verification-grid-header\">\n <span class=\"cqa-api-edit-step-verification-label\">S.no</span>\n <span class=\"cqa-api-edit-step-verification-label\">JSON Path</span>\n <span class=\"cqa-api-edit-step-verification-label\">Verification</span>\n <span class=\"cqa-api-edit-step-verification-label\">Data Type</span>\n <span class=\"cqa-api-edit-step-verification-label\">Expected Value</span>\n <span class=\"cqa-api-edit-step-verification-label cqa-api-edit-step-verification-label--empty\"\n aria-hidden=\"true\"></span>\n </div>\n <div *ngFor=\"let row of verificationRows; let i = index; trackBy: trackByVerification\" [formGroup]=\"row\"\n class=\"cqa-api-edit-step-verification-row\">\n <span class=\"cqa-api-edit-step-verification-sno\">{{ i + 1 }}</span>\n <cqa-custom-input [value]=\"row.get('jsonPath')?.value ?? ''\"\n (valueChange)=\"row.get('jsonPath')?.setValue($event)\" placeholder=\"Json Path\" [fullWidth]=\"true\"\n size=\"sm\" class=\"cqa-api-edit-step-verification-input\" ariaLabel=\"JSON Path\">\n </cqa-custom-input>\n <cqa-dynamic-select [form]=\"row\" [config]=\"verificationSelectConfig\"\n class=\"cqa-api-edit-step-verification-select cqa-w-full\">\n </cqa-dynamic-select>\n <cqa-dynamic-select [form]=\"row\" [config]=\"verificationDataTypeSelectConfig\"\n class=\"cqa-api-edit-step-verification-select cqa-w-full\"\n (selectionChange)=\"onVerificationDataTypeChange(row, $event)\">\n </cqa-dynamic-select>\n <!-- Expected Value: text for String/Array/Object, number for Number, dropdown for Boolean -->\n <ng-container [ngSwitch]=\"row.get('dataType')?.value\">\n <cqa-dynamic-select *ngSwitchCase=\"'boolean'\" [form]=\"row\"\n [config]=\"verificationExpectedValueBooleanSelectConfig\"\n class=\"cqa-api-edit-step-verification-select cqa-api-edit-step-verification-expected-select cqa-w-full\">\n </cqa-dynamic-select>\n <cqa-custom-input *ngSwitchCase=\"'number'\" [value]=\"row.get('expectedValue')?.value ?? ''\"\n (valueChange)=\"row.get('expectedValue')?.setValue($event)\" placeholder=\"Expected Value (number)\"\n type=\"number\" [fullWidth]=\"true\" size=\"sm\"\n class=\"cqa-api-edit-step-verification-input cqa-api-edit-step-verification-expected-number\"\n ariaLabel=\"Expected Value\">\n </cqa-custom-input>\n <cqa-custom-input *ngSwitchDefault [value]=\"row.get('expectedValue')?.value ?? ''\"\n (valueChange)=\"row.get('expectedValue')?.setValue($event)\"\n placeholder=\"Expected Value in String\" [fullWidth]=\"true\" size=\"sm\"\n class=\"cqa-api-edit-step-verification-input\" ariaLabel=\"Expected Value\">\n </cqa-custom-input>\n </ng-container>\n <cqa-button type=\"button\" variant=\"text\" [customClass]=\"'cqa-api-edit-step-verification-delete'\"\n [tooltip]=\"'Remove row'\" (clicked)=\"removeVerificationRow(i)\">\n <svg class=\"cqa-api-edit-step-verification-delete-icon\" width=\"16\" height=\"16\" viewBox=\"0 0 16 16\"\n fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\">\n <path\n d=\"M10.6663 6V12.6667H5.33301V6H10.6663ZM9.66634 2H6.33301L5.66634 2.66667H3.33301V4H12.6663V2.66667H10.333L9.66634 2ZM11.9997 4.66667H3.99967V12.6667C3.99967 13.4 4.59967 14 5.33301 14H10.6663C11.3997 14 11.9997 13.4 11.9997 12.6667V4.66667Z\"\n fill=\"#F9BFBF\" />\n </svg>\n </cqa-button>\n </div>\n </div>\n </div>\n\n <!-- Status tab content: S.no, Verification dropdown, Expected Value, add/remove rows -->\n <div *ngIf=\"activeResponseVerificationTab === 'status'\" class=\"cqa-api-edit-step-step3-content\">\n <div class=\"cqa-api-edit-step-verification-header-row cqa-flex cqa-flex-row cqa-justify-end cqa-items-center cqa-pt-0 cqa-pr-0 cqa-pb-2 cqa-pl-0\">\n <span></span>\n <cqa-button type=\"button\" variant=\"text\" text=\"Add Verification\"\n customClass=\"cqa-api-edit-step-verification-add-link\" (clicked)=\"addStatusVerificationRow()\">\n </cqa-button>\n </div>\n <div class=\"cqa-api-edit-step-status-verification\">\n <div class=\"cqa-api-edit-step-status-verification-header\">\n <span class=\"cqa-api-edit-step-verification-label\">S.no</span>\n <span class=\"cqa-api-edit-step-verification-label\">Verification</span>\n <span class=\"cqa-api-edit-step-verification-label\">Expected Value</span>\n <span class=\"cqa-api-edit-step-verification-label cqa-api-edit-step-verification-label--empty\"\n aria-hidden=\"true\"></span>\n </div>\n <div *ngFor=\"let row of statusVerificationRows; let i = index; trackBy: trackByStatusVerification\"\n [formGroup]=\"row\" class=\"cqa-api-edit-step-status-verification-row\">\n <span class=\"cqa-api-edit-step-verification-sno\">{{ i + 1 }}</span>\n <cqa-dynamic-select [form]=\"row\" [config]=\"statusVerificationSelectConfig\"\n class=\"cqa-api-edit-step-status-verification-select cqa-w-full\">\n </cqa-dynamic-select>\n <cqa-custom-input [value]=\"row.get('expectedValue')?.value ?? ''\"\n (valueChange)=\"row.get('expectedValue')?.setValue($event)\" placeholder=\"Expected Value\"\n type=\"number\" [fullWidth]=\"true\" size=\"sm\" class=\"cqa-api-edit-step-verification-input\"\n ariaLabel=\"Expected Value\">\n </cqa-custom-input>\n <cqa-button type=\"button\" variant=\"text\" [customClass]=\"'cqa-api-edit-step-verification-delete'\"\n [tooltip]=\"'Remove row'\" (clicked)=\"removeStatusVerificationRow(i)\">\n <svg class=\"cqa-api-edit-step-verification-delete-icon\" width=\"16\" height=\"16\" viewBox=\"0 0 16 16\"\n fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\">\n <path\n d=\"M10.6663 6V12.6667H5.33301V6H10.6663ZM9.66634 2H6.33301L5.66634 2.66667H3.33301V4H12.6663V2.66667H10.333L9.66634 2ZM11.9997 4.66667H3.99967V12.6667C3.99967 13.4 4.59967 14 5.33301 14H10.6663C11.3997 14 11.9997 13.4 11.9997 12.6667V4.66667Z\"\n fill=\"#F9BFBF\" />\n </svg>\n </cqa-button>\n </div>\n </div>\n </div>\n </div>\n </div>\n </div>\n\n <!-- Response Preview. Hidden while the GraphQL picker is open \u2014 the user is in \"pick an operation\" mode, not \"view last response\" mode, so showing a stale preview below the picker is just noise. -->\n <div *ngIf=\"bodyView !== 'graphql-schema'\" #responsePreviewRef class=\"cqa-api-edit-step-response\">\n <h3 class=\"cqa-api-edit-step-response-title cqa-text-[14px] cqa-font-bold cqa-leading-[20px] cqa-text-[#111827] cqa-m-0 cqa-mb-[12px] cqa-shrink-0\">Response Preview</h3>\n <pre class=\"cqa-api-edit-step-response-content\">{{ responsePreview }}</pre>\n </div>\n\n <!-- Step actions: one row, full width. Step 1: Cancel + Next; Step 2: Back + Next; Step 3: Back + Create -->\n <div *ngIf=\"bodyView !== 'graphql-schema'\" class=\"cqa-api-edit-step-actions\">\n <ng-container [ngSwitch]=\"currentStep\">\n <ng-container *ngSwitchCase=\"1\">\n <cqa-button type=\"button\" variant=\"outlined\" btnSize=\"lg\" text=\"Cancel\"\n customClass=\"cqa-api-edit-step-actions-btn-cancel\" (clicked)=\"onCancel()\">\n </cqa-button>\n <cqa-button type=\"button\" variant=\"filled\" btnSize=\"lg\" text=\"Next\"\n customClass=\"cqa-api-edit-step-actions-btn-next\" (clicked)=\"onNext()\">\n </cqa-button>\n </ng-container>\n <ng-container *ngSwitchCase=\"2\">\n <cqa-button type=\"button\" variant=\"outlined\" btnSize=\"lg\" text=\"Back\"\n customClass=\"cqa-api-edit-step-actions-btn-back\" (clicked)=\"onBack()\">\n </cqa-button>\n <cqa-button type=\"button\" variant=\"filled\" btnSize=\"lg\" text=\"Next\"\n customClass=\"cqa-api-edit-step-actions-btn-next\" (clicked)=\"onNext()\">\n </cqa-button>\n </ng-container>\n <ng-container *ngSwitchCase=\"3\">\n <cqa-button type=\"button\" variant=\"outlined\" btnSize=\"lg\" text=\"Back\"\n customClass=\"cqa-api-edit-step-actions-btn-back\" (clicked)=\"onBack()\">\n </cqa-button>\n <cqa-button *ngIf=\"!isCreatingStep\" type=\"button\" variant=\"filled\" btnSize=\"lg\" [text]=\"editMode ? 'Update' : 'Create'\"\n customClass=\"cqa-api-edit-step-actions-btn-create\" (clicked)=\"onCreate()\">\n </cqa-button>\n <!-- Match cqa-button flex: actions row styles > cqa-button { flex: 1 1 0 } \u2014 loader div must grow same width as Create -->\n <div *ngIf=\"isCreatingStep\" class=\"cqa-api-edit-step-actions-loader cqa-w-full cqa-min-h-[38px] cqa-rounded-[8px] cqa-flex cqa-items-center cqa-justify-center\">\n <cqa-badge label=\"Creating\u2026\" icon=\"autorenew\" [isLoading]=\"true\" [fullWidth]=\"true\" [centerContent]=\"true\"\n [inlineStyles]=\"'min-height: 38px; height: 38px; width: 100%; box-sizing: border-box; display: flex; align-items: center; justify-content: center;'\"\n variant=\"info\" size=\"medium\"></cqa-badge>\n </div>\n </ng-container>\n </ng-container>\n </div>\n</div>", components: [{ type: ButtonComponent, selector: "cqa-button", inputs: ["variant", "btnSize", "disabled", "loading", "icon", "iconPosition", "fullWidth", "iconColor", "type", "text", "customClass", "inlineStyles", "tooltip", "tooltipPosition"], outputs: ["clicked"] }, { type: SegmentControlComponent, selector: "cqa-segment-control", inputs: ["segments", "value", "disabled", "containerBgColor", "fullWidth", "size"], outputs: ["valueChange"] }, { type: CustomInputComponent, selector: "cqa-custom-input", inputs: ["inputId", "label", "type", "placeholder", "value", "disabled", "errors", "required", "ariaLabel", "size", "fullWidth", "maxLength", "showCharCount", "inputInlineStyle", "labelInlineStyle"], outputs: ["valueChange", "blurred", "focused", "enterPressed"] }, { type: AutocompleteComponent, selector: "cqa-autocomplete", inputs: ["placeholder", "options", "value", "disabled", "showClear", "ariaLabel", "autoFocus", "size", "fullWidth", "compact"], outputs: ["valueChange", "optionSelect", "cleared"] }, { type: DynamicSelectFieldComponent, selector: "cqa-dynamic-select", inputs: ["form", "config"], outputs: ["selectionChange", "selectClick", "searchChange", "loadMore", "addCustomValue"] }, { type: BadgeComponent, selector: "cqa-badge", inputs: ["type", "label", "icon", "iconLibrary", "variant", "size", "backgroundColor", "textColor", "borderColor", "iconBackgroundColor", "iconColor", "iconSize", "inlineStyles", "key", "value", "keyTextColor", "valueTextColor", "isLoading", "fullWidth", "centerContent", "title"] }, { type: CustomTextareaComponent, selector: "cqa-custom-textarea", inputs: ["label", "placeholder", "value", "enableMarkdown", "disabled", "errors", "required", "ariaLabel", "size", "fullWidth", "maxLength", "showCharCount", "rows", "cols", "resize", "textareaInlineStyle", "labelInlineStyle"], outputs: ["valueChange", "blurred", "focused"] }, { type: GraphQLOperationPickerComponent, selector: "cqa-graphql-operation-picker", inputs: ["schemaSource", "operations", "introspected", "isLoading"], outputs: ["back", "operationSelected", "reIntrospect", "change", "preview"] }, { type: i1.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { type: AdvancedVariablesFormComponent, selector: "cqa-advanced-variables-form", inputs: ["advancedVariables", "advancedVariableForm"], outputs: ["variableBooleanChange", "variableValueChange"] }], directives: [{ type: i2.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { type: i2.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { type: i2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { type: i1$1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { type: i1$1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { type: i2.NgSwitch, selector: "[ngSwitch]", inputs: ["ngSwitch"] }, { type: i2.NgSwitchCase, selector: "[ngSwitchCase]", inputs: ["ngSwitchCase"] }, { type: i2.NgSwitchDefault, selector: "[ngSwitchDefault]" }], changeDetection: i0.ChangeDetectionStrategy.Default });
38779
39406
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.4.0", ngImport: i0, type: ApiEditStepComponent, decorators: [{
38780
39407
  type: Component,
38781
- args: [{ selector: 'cqa-api-edit-step', host: { class: 'cqa-ui-root' }, changeDetection: ChangeDetectionStrategy.Default, template: "<div class=\"cqa-api-edit-step-container cqa-bg-[#ffffff] cqa-flex cqa-flex-col cqa-w-full cqa-h-full cqa-p-[16px] cqa-gap-[12px] cqa-overflow-y-auto cqa-overflow-x-hidden cqa-box-border\">\n\n <h2 class=\"cqa-api-edit-step-title cqa-font-inter cqa-text-[12px] cqa-font-semibold cqa-leading-[100%] cqa-tracking-normal cqa-text-[#000000] cqa-m-0\">\n {{ editMode ? 'Update API Test Step' : 'Create API Test Step' }}\n </h2>\n\n <div class=\"cqa-api-edit-step-indicator cqa-flex cqa-items-center cqa-gap-0 cqa-max-[767px]:cqa-flex-wrap cqa-max-[767px]:cqa-gap-2 cqa-max-[767px]:cqa-justify-start\"\n [class.cqa-justify-start]=\"stepLabels.length === 1\"\n [class.cqa-justify-between]=\"stepLabels.length !== 1\">\n <ng-container *ngFor=\"let step of stepLabels; let i = index\">\n <cqa-button type=\"button\" variant=\"text\"\n customClass=\"cqa-api-edit-step-indicator-item cqa-api-edit-step-indicator-item--clickable cqa-flex cqa-items-center cqa-gap-[10px] cqa-cursor-pointer cqa-border-none cqa-p-0 cqa-font-inherit cqa-text-inherit cqa-text-left !cqa-bg-transparent\"\n [attr.aria-label]=\"'Step ' + step.index + ': ' + step.label\" [attr.aria-pressed]=\"step.index === currentStep\"\n (clicked)=\"setStep(step.index)\">\n <span class=\"cqa-api-edit-step-indicator-circle cqa-w-8 cqa-h-8 cqa-rounded-full cqa-inline-flex cqa-items-center cqa-justify-center cqa-text-sm cqa-font-medium cqa-leading-none cqa-border-none cqa-shrink-0\"\n [ngClass]=\"step.index === currentStep ? 'cqa-bg-[#4F46E5] cqa-text-white' : 'cqa-bg-[#E0E0E0] cqa-text-[#666666]'\">\n {{ step.index }}\n </span>\n <span class=\"cqa-api-edit-step-indicator-label cqa-text-sm cqa-leading-[18px] cqa-font-normal cqa-max-[767px]:cqa-text-xs\"\n [ngClass]=\"step.index === currentStep ? 'cqa-text-[#4F46E5]' : 'cqa-text-[#666666]'\">\n {{ step.label }}\n </span>\n <div *ngIf=\"i < stepLabels.length - 1\" class=\"cqa-api-edit-step-indicator-line cqa-w-[96px] cqa-h-[2px] cqa-bg-[#E0E0E0] cqa-my-0 cqa-mx-[6px] cqa-shrink-0 cqa-self-center cqa-max-[767px]:cqa-w-[24px] cqa-max-[767px]:cqa-mx-1\" aria-hidden=\"true\"></div>\n </cqa-button>\n </ng-container>\n </div>\n\n <!-- Step content viewport: only the active step body is shown (*ngIf) -->\n <div class=\"cqa-api-edit-step-viewport cqa-w-full\">\n <!-- Step 1: Environment, request, body, response -->\n <div *ngIf=\"currentStep === 1\" class=\"cqa-api-edit-step-panel cqa-flex cqa-flex-col cqa-gap-6 cqa-box-border cqa-w-full\">\n <!-- Summary field: label on top, input below (above Environment) -->\n <div class=\"cqa-api-edit-step-summary-row cqa-flex cqa-flex-col cqa-gap-1.5 cqa-items-stretch\">\n <label class=\"cqa-api-edit-step-summary-label cqa-m-0 cqa-text-[14px] cqa-font-semibold cqa-leading-[100%] cqa-tracking-normal\">Summary</label>\n <cqa-custom-input [(value)]=\"summary\"\n [label]=\"''\" placeholder=\"Enter Request summary\" [fullWidth]=\"true\" size=\"md\"\n ariaLabel=\"Summary\">\n </cqa-custom-input>\n </div>\n <!-- Environment row: label on top, select below (aligned right on desktop) -->\n <div class=\"cqa-api-edit-step-environment-row cqa-flex cqa-flex-col cqa-gap-1.5 cqa-items-end cqa-max-[767px]:cqa-items-stretch\">\n <span class=\"cqa-flex cqa-flex-col cqa-gap-1.5 cqa-max-[767px]:cqa-items-stretch\">\n <label class=\"cqa-api-edit-step-environment-label cqa-m-0 cqa-text-[14px] cqa-font-semibold cqa-leading-[100%] cqa-tracking-normal\">Environments</label>\n <cqa-dynamic-select *ngIf=\"environmentForm\"\n [form]=\"environmentForm\"\n [config]=\"environmentSelectConfig\"\n class=\"cqa-api-edit-step-environment-select cqa-shrink-0 cqa-w-auto cqa-min-w-[315px] cqa-max-w-[315px] cqa-max-[767px]:cqa-min-w-0 cqa-max-[767px]:cqa-max-w-full cqa-max-[767px]:cqa-w-full [&_.mat-form-field-wrapper]:cqa-pb-0\"\n aria-label=\"Environment\"\n (selectionChange)=\"onEnvironmentSelectionChange($event)\">\n </cqa-dynamic-select>\n </span>\n </div>\n\n <!-- Request row: method, URL, buttons -->\n <div class=\"cqa-api-edit-step-request-row cqa-flex cqa-items-start cqa-gap-3 cqa-flex-wrap cqa-max-[767px]:cqa-flex-col cqa-max-[767px]:cqa-items-stretch cqa-max-[767px]:cqa-gap-2.5\">\n <cqa-dynamic-select *ngIf=\"methodForm\" [form]=\"methodForm\" [config]=\"methodSelectConfig\"\n class=\"cqa-api-edit-step-method-select cqa-shrink-0 cqa-w-auto cqa-min-w-[152px] cqa-max-w-[152px] cqa-max-[767px]:cqa-min-w-0 cqa-max-[767px]:cqa-max-w-full cqa-max-[767px]:cqa-w-full\" aria-label=\"HTTP method\"\n (selectionChange)=\"onMethodSelectionChange($event)\">\n </cqa-dynamic-select>\n <div class=\"cqa-api-edit-step-url-wrap cqa-flex-1 cqa-min-w-[200px] cqa-max-[767px]:cqa-min-w-0 cqa-max-[767px]:cqa-w-full\">\n <cqa-autocomplete\n [value]=\"url\"\n (valueChange)=\"url = $event\"\n [options]=\"urlOptions\"\n [placeholder]=\"urlPlaceholder\"\n ariaLabel=\"Enter URL or select Parameter\"\n [fullWidth]=\"true\"\n (optionSelect)=\"onUrlOptionSelect($event)\">\n </cqa-autocomplete>\n <div *ngIf=\"urlError\" class=\"cqa-text-red-600 cqa-text-sm cqa-mt-1\">\n {{ urlError }}\n </div>\n </div>\n <div class=\"cqa-api-edit-step-import-curl-trigger cqa-contents cqa-max-[767px]:cqa-w-full cqa-max-[767px]:!cqa-flex\" (click)=\"openImportCurlPanel()\">\n <cqa-button type=\"button\" variant=\"outlined\" btnSize=\"lg\" text=\"Import API cURL\"\n customClass=\"cqa-api-edit-step-btn-outline !cqa-bg-white !cqa-border !cqa-border-solid !cqa-border-[#6B7280] cqa-text-[#414146] cqa-font-inter cqa-text-[14px] cqa-font-semibold cqa-leading-[100%] cqa-tracking-normal hover:!cqa-bg-[#F9FAFB] hover:!cqa-text-[#414146] cqa-max-[767px]:cqa-w-full cqa-mt-1\">\n </cqa-button>\n </div>\n <cqa-button variant=\"filled\" btnSize=\"lg\" text=\"Send Request\" (clicked)=\"onSendRequest()\"\n [disabled]=\"!isUrlValid\"\n customClass=\"cqa-api-edit-step-btn-primary cqa-font-inter cqa-text-[14px] cqa-font-semibold cqa-leading-[100%] cqa-tracking-normal !cqa-bg-[#3F43EE] cqa-text-[#FBFCFF] !cqa-border-none cqa-py-2.5 cqa-px-6 cqa-gap-2 cqa-rounded-lg cqa-min-h-[37px] cqa-box-border hover:!cqa-bg-[#1B1FEB] cqa-max-[767px]:cqa-w-full cqa-mt-1\"\n [ngClass]=\"{'!cqa-opacity-50 !cqa-cursor-not-allowed': !isUrlValid}\">\n </cqa-button>\n </div>\n\n\n\n <!-- Body content: header section (default) OR import cURL section -->\n <ng-container *ngIf=\"bodyView === 'import-curl'\">\n <div class=\"cqa-api-edit-step-import-curl-panel cqa-flex cqa-flex-col cqa-bg-white cqa-rounded-lg cqa-border cqa-border-solid cqa-border-[#E5E7EB] cqa-shadow-[0_1px_3px_rgba(0,0,0,0.08)] cqa-overflow-hidden\">\n <div class=\"cqa-api-edit-step-import-curl-header cqa-flex cqa-items-center cqa-justify-between cqa-py-3 cqa-px-5 cqa-border-b cqa-border-solid cqa-border-[#E2E8F0] cqa-bg-[#F9FAFB] cqa-rounded-t-lg cqa-max-[767px]:cqa-px-4\">\n <h3 class=\"cqa-api-edit-step-import-curl-title cqa-m-0 cqa-text-xs cqa-font-bold cqa-leading-[18px] cqa-text-[#374151]\">Import API cURL</h3>\n </div>\n <div class=\"cqa-api-edit-step-import-curl-separator cqa-h-px cqa-bg-[#E2E8F0] cqa-my-0 cqa-mx-5\"></div>\n <div class=\"cqa-api-edit-step-import-curl-note cqa-px-5 cqa-pt-3\">\n <cqa-badge label=\"cURL is supported in Bash format only.\" icon=\"info\" variant=\"info\" size=\"small\" [fullWidth]=\"true\"></cqa-badge>\n </div>\n <div class=\"cqa-api-edit-step-import-curl-body\">\n <cqa-custom-textarea [value]=\"importCurlControl.value\"\n (valueChange)=\"importCurlControl.setValue($event)\"\n placeholder=\"Paste your cURL command here (e.g., curl -X POST https://api.example.com/users)\"\n [fullWidth]=\"true\" [rows]=\"8\" resize=\"vertical\" size=\"md\"\n class=\"cqa-api-edit-step-import-curl-textarea\">\n </cqa-custom-textarea>\n </div>\n <div class=\"cqa-api-edit-step-import-curl-footer\">\n <cqa-button type=\"button\" variant=\"outlined\" btnSize=\"lg\" text=\"Cancel\"\n customClass=\"cqa-api-edit-step-import-curl-btn-cancel\" (clicked)=\"onCancelImportCurl()\"></cqa-button>\n <cqa-button type=\"button\" variant=\"filled\" btnSize=\"lg\" text=\"Import\"\n customClass=\"cqa-api-edit-step-import-curl-btn-import\" (clicked)=\"onImportCurlConfirm()\"></cqa-button>\n </div>\n </div>\n </ng-container>\n\n <ng-container *ngIf=\"bodyView === 'headers'\">\n <div class=\"cqa-api-edit-step-tabs-wrapper cqa-flex cqa-flex-col cqa-gap-3\">\n <div class=\"cqa-api-edit-step-tabs\">\n <cqa-button *ngFor=\"let tab of payloadTabs\" type=\"button\" variant=\"text\" [text]=\"tab.label\"\n [customClass]=\"'cqa-api-edit-step-tab' + (activePayloadTab === tab.value ? ' cqa-api-edit-step-tab--active' : '') + (isMethodWithoutBody && (tab.value === 'body' || tab.value === 'params') ? ' cqa-api-edit-step-tab--disabled' : '')\"\n [disabled]=\"isMethodWithoutBody && (tab.value === 'body' || tab.value === 'params')\"\n (clicked)=\"setPayloadTab(tab.value)\">\n </cqa-button>\n </div>\n\n <!-- Payload content (Authorization) -->\n <div *ngIf=\"activePayloadTab === 'authorization'\" class=\"cqa-api-edit-step-payload\"\n [attr.style]=\"'padding: 20px; min-height: 200px;'\">\n <div [attr.style]=\"'display: flex; flex-direction: column; gap: 20px;'\">\n <div [attr.style]=\"'display: flex; flex-direction: column; gap: 8px; width: fit-content;'\">\n <span [attr.style]=\"'font-size: 11px; font-weight: 600; letter-spacing: 0.02em; color: #6B7280; text-transform: uppercase; line-height: 1.25;'\">AUTH TYPE</span>\n <cqa-dynamic-select *ngIf=\"authTypeForm\" [form]=\"authTypeForm\" [config]=\"authTypeSelectConfig\"\n class=\"cqa-api-edit-step-auth-type-select\" aria-label=\"Auth type\"\n [attr.style]=\"'min-width: 160px;'\">\n </cqa-dynamic-select>\n </div>\n <!-- No Auth: centered message -->\n <div *ngIf=\"selectedAuthType === 'no-auth'\"\n [attr.style]=\"'flex: 1; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 12px; min-width: 0; text-align: center; border: 1px solid #E5E7EB; border-radius: 8px; padding-top: 16px; padding-bottom: 16px;'\">\n <div aria-hidden=\"true\"\n [attr.style]=\"'width: 48px; height: 48px; border-radius: 10px; background: #F3F4F6; display: flex; align-items: center; justify-content: center; flex-shrink: 0;'\">\n <svg width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\" style=\"display: block;\">\n <rect x=\"5\" y=\"11\" width=\"14\" height=\"2\" rx=\"1\" fill=\"#9CA3AF\"/>\n </svg>\n </div>\n <span [attr.style]=\"'font-size: 16px; font-weight: 600; color: #374151; line-height: 1.25;'\">No Auth</span>\n <div [attr.style]=\"'display: flex; align-items: center; justify-content: center; gap: 6px; flex-wrap: wrap;'\">\n <span [attr.style]=\"'font-size: 14px; font-weight: 400; color: #9CA3AF; line-height: 1.4;'\">This request does not use any authorization.</span>\n <span role=\"img\" aria-label=\"More information\" title=\"This request does not use any authorization.\"\n [attr.style]=\"'display: inline-flex; align-items: center; justify-content: center; width: 18px; height: 18px; color: #9CA3AF; flex-shrink: 0;'\">\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 16 16\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\" style=\"display: block;\">\n <circle cx=\"8\" cy=\"8\" r=\"7\" stroke=\"#9CA3AF\" stroke-width=\"1.5\" fill=\"none\"/>\n <path d=\"M8 7v4M8 5v.5\" stroke=\"#9CA3AF\" stroke-width=\"1.5\" stroke-linecap=\"round\"/>\n </svg>\n </span>\n </div>\n </div>\n <!-- Bearer Token: Token label + textarea -->\n <div *ngIf=\"selectedAuthType === 'bearer'\"\n [attr.style]=\"'flex: 1; display: flex; flex-direction: column; gap: 10px; min-width: 0;'\">\n <span [attr.style]=\"'font-size: 12px; font-weight: 600; color: #374151; line-height: 1.25;'\">Token</span>\n <cqa-custom-textarea [(value)]=\"bearerToken\"\n placeholder=\"Enter bearer token\"\n [fullWidth]=\"true\" [rows]=\"6\" resize=\"vertical\" size=\"md\"\n class=\"cqa-api-edit-step-auth-token-textarea\" ariaLabel=\"Bearer token\">\n </cqa-custom-textarea>\n </div>\n <!-- OAuth 2.0: Grant Type, Access Token URL, Client ID, Client Secret, Scope, Client Authentication -->\n <div *ngIf=\"selectedAuthType === 'oauth2' && oauth2Form\"\n class=\"cqa-api-edit-step-oauth2-grid\"\n [attr.style]=\"'display: grid; grid-template-columns: 1fr 1fr; gap: 16px 24px; width: 100%;'\">\n <div [attr.style]=\"'display: flex; flex-direction: column; gap: 6px; min-width: 0;'\">\n <span [attr.style]=\"'font-size: 12px; font-weight: 600; color: #374151; line-height: 1.25;'\">Grant Type</span>\n <cqa-dynamic-select *ngIf=\"oauth2Form\" [form]=\"oauth2Form\" [config]=\"oauth2GrantTypeSelectConfig\"\n class=\"cqa-api-edit-step-auth-oauth-select\" aria-label=\"OAuth grant type\"\n [attr.style]=\"'min-width: 0; width: 100%;'\">\n </cqa-dynamic-select>\n <span [attr.style]=\"'font-size: 11px; font-style: italic; color: #6B7280; line-height: 1.3;'\">OAuth grant type</span>\n </div>\n <div [attr.style]=\"'display: flex; flex-direction: column; gap: 6px; min-width: 0;'\">\n <span [attr.style]=\"'font-size: 12px; font-weight: 600; color: #374151; line-height: 1.25;'\">Access Token URL</span>\n <cqa-custom-input [value]=\"oauth2Form.get('accessTokenUrl')?.value\"\n (valueChange)=\"oauth2Form.get('accessTokenUrl')?.setValue($event)\"\n [label]=\"''\" placeholder=\"Enter OAuth token endpoint\" [fullWidth]=\"true\" size=\"md\"\n ariaLabel=\"Access Token URL\">\n </cqa-custom-input>\n <span [attr.style]=\"'font-size: 11px; font-style: italic; color: #6B7280; line-height: 1.3;'\">OAuth token endpoint</span>\n </div>\n <div [attr.style]=\"'display: flex; flex-direction: column; gap: 6px; min-width: 0;'\">\n <span [attr.style]=\"'font-size: 12px; font-weight: 600; color: #374151; line-height: 1.25;'\">Client ID</span>\n <cqa-custom-input [value]=\"oauth2Form.get('clientId')?.value\"\n (valueChange)=\"oauth2Form.get('clientId')?.setValue($event)\"\n [label]=\"''\" placeholder=\"Enter OAuth client identifier\" [fullWidth]=\"true\" size=\"md\"\n ariaLabel=\"Client ID\">\n </cqa-custom-input>\n <span [attr.style]=\"'font-size: 11px; font-style: italic; color: #6B7280; line-height: 1.3;'\">OAuth client identifier</span>\n </div>\n <div [attr.style]=\"'display: flex; flex-direction: column; gap: 6px; min-width: 0;'\">\n <span [attr.style]=\"'font-size: 12px; font-weight: 600; color: #374151; line-height: 1.25;'\">Client Secret</span>\n <cqa-custom-input [value]=\"oauth2Form.get('clientSecret')?.value\"\n (valueChange)=\"oauth2Form.get('clientSecret')?.setValue($event)\"\n type=\"password\" [label]=\"''\" placeholder=\"Enter OAuth client secret\" [fullWidth]=\"true\" size=\"md\"\n ariaLabel=\"Client Secret\">\n </cqa-custom-input>\n <span [attr.style]=\"'font-size: 11px; font-style: italic; color: #6B7280; line-height: 1.3;'\">OAuth client secret</span>\n </div>\n <div [attr.style]=\"'display: flex; flex-direction: column; gap: 6px; min-width: 0;'\">\n <span [attr.style]=\"'font-size: 12px; font-weight: 600; color: #374151; line-height: 1.25;'\">Scope</span>\n <cqa-custom-input [value]=\"oauth2Form.get('scope')?.value\"\n (valueChange)=\"oauth2Form.get('scope')?.setValue($event)\"\n [label]=\"''\" placeholder=\"Enter space-separated scopes\" [fullWidth]=\"true\" size=\"md\"\n ariaLabel=\"Scope\">\n </cqa-custom-input>\n <span [attr.style]=\"'font-size: 11px; font-style: italic; color: #6B7280; line-height: 1.3;'\">Space-separated scopes</span>\n </div>\n <div [attr.style]=\"'display: flex; flex-direction: column; gap: 6px; min-width: 0;'\">\n <span [attr.style]=\"'font-size: 12px; font-weight: 600; color: #374151; line-height: 1.25;'\">Client Authentication</span>\n <cqa-dynamic-select *ngIf=\"oauth2Form\" [form]=\"oauth2Form\" [config]=\"oauth2ClientAuthSelectConfig\"\n class=\"cqa-api-edit-step-auth-oauth-select\" aria-label=\"Client authentication method\"\n [attr.style]=\"'min-width: 0; width: 100%;'\">\n </cqa-dynamic-select>\n <span [attr.style]=\"'font-size: 11px; font-style: italic; color: #6B7280; line-height: 1.3;'\">Client authentication method</span>\n </div>\n </div>\n </div>\n </div>\n\n <!-- Payload content (Headers) -->\n <div *ngIf=\"activePayloadTab === 'headers'\" class=\"cqa-api-edit-step-payload\">\n <div class=\"cqa-api-edit-step-headers-grid\">\n <span class=\"cqa-api-edit-step-headers-label\">Header Name</span>\n <span class=\"cqa-api-edit-step-headers-label\">Header Value</span>\n <span class=\"cqa-api-edit-step-headers-label cqa-api-edit-step-headers-label--empty\"\n aria-hidden=\"true\"></span>\n </div>\n <div *ngFor=\"let row of headerRows; let i = index; trackBy: trackByHeader\" [formGroup]=\"row\"\n class=\"cqa-api-edit-step-header-row\">\n <cqa-dynamic-select [form]=\"row\" [config]=\"headerNameSelectConfig\"\n (addCustomValue)=\"onHeaderNameAddCustomValue($event, row)\"\n class=\"cqa-api-edit-step-header-type-select cqa-w-full\">\n </cqa-dynamic-select>\n <cqa-custom-input [value]=\"row.get('value')?.value ?? ''\"\n (valueChange)=\"row.get('value')?.setValue($event)\" placeholder=\"Value\" [fullWidth]=\"true\" size=\"sm\"\n class=\"cqa-api-edit-step-header-value-input\" ariaLabel=\"Header value\">\n </cqa-custom-input>\n <cqa-button type=\"button\" variant=\"text\" [customClass]=\"'cqa-api-edit-step-header-delete'\"\n [tooltip]=\"'Remove header'\" (clicked)=\"removeHeader(i)\">\n <svg class=\"cqa-api-edit-step-header-delete-icon\" width=\"16\" height=\"16\" viewBox=\"0 0 16 16\"\n fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\">\n <path\n d=\"M10.6663 6V12.6667H5.33301V6H10.6663ZM9.66634 2H6.33301L5.66634 2.66667H3.33301V4H12.6663V2.66667H10.333L9.66634 2ZM11.9997 4.66667H3.99967V12.6667C3.99967 13.4 4.59967 14 5.33301 14H10.6663C11.3997 14 11.9997 13.4 11.9997 12.6667V4.66667Z\"\n fill=\"#F9BFBF\" />\n </svg>\n </cqa-button>\n </div>\n <div class=\"cqa-api-edit-step-add-header-wrap\">\n <cqa-button type=\"button\" variant=\"text\" text=\"+ Add Header\"\n [customClass]=\"'cqa-api-edit-step-add-header-link'\" (clicked)=\"addHeader()\">\n </cqa-button>\n </div>\n </div>\n\n <!-- Payload content (Params): Key\u2013Value table with add/delete rows -->\n <div *ngIf=\"activePayloadTab === 'params'\" class=\"cqa-api-edit-step-payload\">\n <div class=\"cqa-api-edit-step-key-value-grid cqa-api-edit-step-key-value-header\">\n <span class=\"cqa-api-edit-step-key-value-label\">Key</span>\n <span class=\"cqa-api-edit-step-key-value-label\">Value</span>\n <span class=\"cqa-api-edit-step-key-value-label cqa-api-edit-step-key-value-label--empty\"\n aria-hidden=\"true\"></span>\n </div>\n <div *ngFor=\"let row of paramsRows; let i = index; trackBy: trackByParams\" [formGroup]=\"row\"\n class=\"cqa-api-edit-step-key-value-row\">\n <cqa-custom-input [value]=\"row.get('key')?.value ?? ''\"\n (valueChange)=\"row.get('key')?.setValue($event)\" placeholder=\"Key\" [fullWidth]=\"true\" size=\"sm\"\n class=\"cqa-api-edit-step-key-value-input\" ariaLabel=\"Key\">\n </cqa-custom-input>\n <cqa-custom-input [value]=\"row.get('value')?.value ?? ''\"\n (valueChange)=\"row.get('value')?.setValue($event)\" placeholder=\"Value\" [fullWidth]=\"true\" size=\"sm\"\n class=\"cqa-api-edit-step-key-value-input\" ariaLabel=\"Value\">\n </cqa-custom-input>\n <cqa-button type=\"button\" variant=\"text\" [customClass]=\"'cqa-api-edit-step-key-value-delete'\"\n [tooltip]=\"'Remove row'\" (clicked)=\"removeParamsRow(i)\">\n <svg class=\"cqa-api-edit-step-key-value-delete-icon\" width=\"16\" height=\"16\" viewBox=\"0 0 16 16\"\n fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\">\n <path\n d=\"M10.6663 6V12.6667H5.33301V6H10.6663ZM9.66634 2H6.33301L5.66634 2.66667H3.33301V4H12.6663V2.66667H10.333L9.66634 2ZM11.9997 4.66667H3.99967V12.6667C3.99967 13.4 4.59967 14 5.33301 14H10.6663C11.3997 14 11.9997 13.4 11.9997 12.6667V4.66667Z\"\n fill=\"#F9BFBF\" />\n </svg>\n </cqa-button>\n </div>\n <div class=\"cqa-api-edit-step-key-value-add-wrap\">\n <cqa-button type=\"button\" variant=\"filled\" btnSize=\"lg\" text=\"+ Add Row\"\n customClass=\"cqa-api-edit-step-key-value-add-btn\" (clicked)=\"addParamsRow()\">\n </cqa-button>\n </div>\n </div>\n\n <!-- Payload content (Body only): type, format, text area, Back/Next -->\n <div *ngIf=\"activePayloadTab === 'body'\"\n class=\"cqa-api-edit-step-payload cqa-api-edit-step-payload-editor\">\n <div class=\"cqa-api-edit-step-payload-type-row\">\n <span class=\"cqa-api-edit-step-payload-type-label\">Type</span>\n <div class=\"cqa-api-edit-step-payload-type-radios\">\n <cqa-segment-control [value]=\"payloadType\" [segments]=\"payloadTypeSegments\"\n (valueChange)=\"onPayloadTypeChange($event)\"\n class=\"cqa-api-edit-step-payload-type-segment\">\n </cqa-segment-control>\n </div>\n <div *ngIf=\"payloadType === 'raw'\" class=\"cqa-api-edit-step-payload-format-wrap\">\n <span class=\"cqa-api-edit-step-payload-format-label\">Format:</span>\n <cqa-dynamic-select *ngIf=\"payloadFormatForm\" [form]=\"payloadFormatForm\"\n [config]=\"payloadFormatSelectConfig\" class=\"cqa-api-edit-step-payload-format-select\"\n aria-label=\"Format\">\n </cqa-dynamic-select>\n </div>\n </div>\n <!-- Raw: text area with line numbers (Postman-style payload body) -->\n <div *ngIf=\"payloadType === 'raw'\" class=\"cqa-api-edit-step-payload-body\" #payloadEditorWithLinesRef>\n <div class=\"cqa-api-edit-step-payload-editor-with-lines\">\n <div class=\"cqa-api-edit-step-payload-line-numbers\" aria-hidden=\"true\">\n <span *ngFor=\"let n of payloadLineNumbers\" class=\"cqa-api-edit-step-payload-line-num\">\n <span *ngIf=\"payloadJsonError && payloadJsonError.line === n\"\n class=\"cqa-api-edit-step-payload-line-error-icon\" [title]=\"payloadJsonErrorTooltip\"\n aria-label=\"Parse error on this line\">\n <svg xmlns=\"http://www.w3.org/2000/svg\" width=\"14\" height=\"14\" viewBox=\"0 0 14 14\" fill=\"none\" aria-hidden=\"true\">\n <circle cx=\"7\" cy=\"7\" r=\"6\" fill=\"#EF4444\"/>\n <path d=\"M4 4l6 6M10 4l-6 6\" stroke=\"white\" stroke-width=\"1.5\" stroke-linecap=\"round\"/>\n </svg>\n </span>\n <span class=\"cqa-api-edit-step-payload-line-num-text\">{{ n }}</span>\n </span>\n </div>\n <div class=\"cqa-api-edit-step-payload-textarea-cell\">\n <div class=\"cqa-api-edit-step-payload-textarea cqa-w-full cqa-flex cqa-flex-col cqa-min-h-0\"\n [ngClass]=\"{'cqa-api-edit-step-payload-textarea--error': payloadJsonError}\">\n <textarea #payloadTextareaRef\n class=\"cqa-api-edit-step-payload-textarea-input cqa-w-full cqa-outline-none cqa-box-border\"\n [ngClass]=\"{'cqa-api-edit-step-payload-textarea-input--error': payloadJsonError}\"\n [value]=\"payloadText\"\n placeholder=\"\"\n [attr.aria-label]=\"'Payload'\"\n [attr.aria-invalid]=\"!!payloadJsonError\"\n (input)=\"onPayloadInput($event)\"\n (keydown)=\"onPayloadKeydown($event)\">\n </textarea>\n </div>\n </div>\n </div>\n <p *ngIf=\"payloadJsonError\" class=\"cqa-api-edit-step-payload-json-error-msg\">\n Invalid JSON format. Please check your syntax.\n </p>\n </div>\n\n <!-- x-www-form-urlencoded: Key\u2013Value rows, add/remove dynamically -->\n <div *ngIf=\"payloadType === 'x-www-form-urlencoded'\" class=\"cqa-api-edit-step-key-value\">\n <div class=\"cqa-api-edit-step-key-value-grid cqa-api-edit-step-key-value-header\">\n <span class=\"cqa-api-edit-step-key-value-label\">Key</span>\n <span class=\"cqa-api-edit-step-key-value-label\">Value</span>\n <span class=\"cqa-api-edit-step-key-value-label cqa-api-edit-step-key-value-label--empty\"\n aria-hidden=\"true\"></span>\n </div>\n <div *ngFor=\"let row of keyValueRows; let i = index; trackBy: trackByKeyValue\" [formGroup]=\"row\"\n class=\"cqa-api-edit-step-key-value-row\">\n <cqa-custom-input [value]=\"row.get('key')?.value ?? ''\"\n (valueChange)=\"row.get('key')?.setValue($event)\" placeholder=\"Key\" [fullWidth]=\"true\" size=\"sm\"\n class=\"cqa-api-edit-step-key-value-input\" ariaLabel=\"Key\">\n </cqa-custom-input>\n <cqa-custom-input [value]=\"row.get('value')?.value ?? ''\"\n (valueChange)=\"row.get('value')?.setValue($event)\" placeholder=\"Value\" [fullWidth]=\"true\" size=\"sm\"\n class=\"cqa-api-edit-step-key-value-input\" ariaLabel=\"Value\">\n </cqa-custom-input>\n <cqa-button type=\"button\" variant=\"text\" [customClass]=\"'cqa-api-edit-step-key-value-delete'\"\n [tooltip]=\"'Remove row'\" (clicked)=\"removeKeyValueRow(i)\">\n <svg class=\"cqa-api-edit-step-key-value-delete-icon\" width=\"16\" height=\"16\" viewBox=\"0 0 16 16\"\n fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\">\n <path\n d=\"M10.6663 6V12.6667H5.33301V6H10.6663ZM9.66634 2H6.33301L5.66634 2.66667H3.33301V4H12.6663V2.66667H10.333L9.66634 2ZM11.9997 4.66667H3.99967V12.6667C3.99967 13.4 4.59967 14 5.33301 14H10.6663C11.3997 14 11.9997 13.4 11.9997 12.6667V4.66667Z\"\n fill=\"#F9BFBF\" />\n </svg>\n </cqa-button>\n </div>\n <div class=\"cqa-api-edit-step-key-value-add-wrap\">\n <cqa-button type=\"button\" variant=\"filled\" btnSize=\"lg\" text=\"+ Add Row\"\n customClass=\"cqa-api-edit-step-key-value-add-btn\" (clicked)=\"addKeyValueRow()\">\n </cqa-button>\n </div>\n </div>\n\n <!-- Form Data: Key\u2013Type\u2013Value rows; Type is a dropdown (Text/File), add/remove dynamically -->\n <div *ngIf=\"payloadType === 'form-data'\" class=\"cqa-api-edit-step-key-type-value\">\n <div class=\"cqa-api-edit-step-key-type-value-header\">\n <span class=\"cqa-api-edit-step-key-type-value-label\">Key</span>\n <span class=\"cqa-api-edit-step-key-type-value-label\">Type</span>\n <span class=\"cqa-api-edit-step-key-type-value-label\">Value</span>\n <span class=\"cqa-api-edit-step-key-type-value-label cqa-api-edit-step-key-type-value-label--empty\"\n aria-hidden=\"true\"></span>\n </div>\n <div *ngFor=\"let row of keyTypeValueRows; let i = index; trackBy: trackByKeyTypeValue\" [formGroup]=\"row\"\n class=\"cqa-api-edit-step-key-type-value-row\">\n <cqa-custom-input [value]=\"row.get('key')?.value ?? ''\"\n (valueChange)=\"row.get('key')?.setValue($event)\" placeholder=\"Key\" [fullWidth]=\"true\" size=\"sm\"\n class=\"cqa-api-edit-step-key-type-value-input\" ariaLabel=\"Key\">\n </cqa-custom-input>\n <cqa-dynamic-select [form]=\"row\" [config]=\"keyTypeValueTypeSelectConfig\"\n class=\"cqa-api-edit-step-key-type-value-type-select cqa-w-full\">\n </cqa-dynamic-select>\n <cqa-custom-input [value]=\"row.get('value')?.value ?? ''\"\n (valueChange)=\"row.get('value')?.setValue($event)\" placeholder=\"Value\" [fullWidth]=\"true\" size=\"sm\"\n class=\"cqa-api-edit-step-key-type-value-input\" ariaLabel=\"Value\">\n </cqa-custom-input>\n <cqa-button type=\"button\" variant=\"text\" [customClass]=\"'cqa-api-edit-step-key-type-value-delete'\"\n [tooltip]=\"'Remove row'\" (clicked)=\"removeKeyTypeValueRow(i)\">\n <svg class=\"cqa-api-edit-step-key-type-value-delete-icon\" width=\"16\" height=\"16\" viewBox=\"0 0 16 16\"\n fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\">\n <path\n d=\"M10.6663 6V12.6667H5.33301V6H10.6663ZM9.66634 2H6.33301L5.66634 2.66667H3.33301V4H12.6663V2.66667H10.333L9.66634 2ZM11.9997 4.66667H3.99967V12.6667C3.99967 13.4 4.59967 14 5.33301 14H10.6663C11.3997 14 11.9997 13.4 11.9997 12.6667V4.66667Z\"\n fill=\"#F9BFBF\" />\n </svg>\n </cqa-button>\n </div>\n <div class=\"cqa-api-edit-step-key-type-value-add-wrap\">\n <cqa-button type=\"button\" variant=\"filled\" btnSize=\"lg\" text=\"+ Add Row\"\n customClass=\"cqa-api-edit-step-key-type-value-add-btn\" (clicked)=\"addKeyTypeValueRow()\">\n </cqa-button>\n </div>\n </div>\n </div>\n </div>\n </ng-container>\n\n <!-- Advanced settings (step-level) -->\n <div class=\"cqa-mt-4\">\n <button\n type=\"button\"\n class=\"cqa-flex cqa-items-center cqa-justify-between cqa-w-full cqa-text-left cqa-text-[12px] cqa-font-medium cqa-text-gray-700 cqa-py-2 cqa-border-b cqa-border-gray-200\"\n (click)=\"advancedExpanded = !advancedExpanded\">\n <span class=\"cqa-text-[10px]\">Advanced</span>\n <mat-icon class=\"cqa-text-lg cqa-transition-transform\" [class.cqa-rotate-180]=\"advancedExpanded\">\n expand_more\n </mat-icon>\n </button>\n <div *ngIf=\"advancedExpanded\" class=\"cqa-mt-2\">\n <cqa-advanced-variables-form\n [advancedVariables]=\"advancedSettingsVariables\"\n [advancedVariableForm]=\"advancedVariablesForm\"\n (variableBooleanChange)=\"onAdvancedVariableBooleanChange($event.name, $event.value)\"\n (variableValueChange)=\"onAdvancedVariableValueChange($event.name, $event.value)\">\n </cqa-advanced-variables-form>\n </div>\n </div>\n\n </div>\n <!-- Step 2: Variable Name: input, validation, Back / Next -->\n <div *ngIf=\"currentStep === 2\" class=\"cqa-api-edit-step-panel cqa-flex cqa-flex-col cqa-gap-6 cqa-box-border cqa-w-full\">\n <div class=\"cqa-api-edit-step-variable-section\">\n <div class=\"cqa-api-edit-step-variable-input-wrap\">\n <cqa-custom-input [(value)]=\"variableName\" [label]=\"''\" placeholder=\"Variable Name\" [fullWidth]=\"true\"\n size=\"md\" (valueChange)=\"onVariableNameChange()\" aria-label=\"Variable Name\">\n </cqa-custom-input>\n </div>\n <p *ngIf=\"variableNameError\" class=\"cqa-api-edit-step-variable-error\" role=\"alert\">{{ variableNameError }}</p>\n </div>\n </div>\n <!-- Step 3: Response Body / Status tabs -->\n <div *ngIf=\"currentStep === 3\" class=\"cqa-api-edit-step-panel cqa-flex cqa-flex-col cqa-gap-6 cqa-box-border cqa-w-full\">\n <div class=\"cqa-api-edit-step-tabs-wrapper cqa-flex cqa-flex-col cqa-gap-3\">\n <div class=\"cqa-api-edit-step-tabs\">\n <cqa-button *ngFor=\"let tab of responseVerificationTabs\" type=\"button\" variant=\"text\" [text]=\"tab.label\"\n [customClass]=\"'cqa-api-edit-step-tab' + (activeResponseVerificationTab === tab.value ? ' cqa-api-edit-step-tab--active' : '')\"\n (clicked)=\"setResponseVerificationTab(tab.value)\">\n </cqa-button>\n </div>\n\n <!-- Response Body tab content: verification grid -->\n <div *ngIf=\"activeResponseVerificationTab === 'response-body'\" class=\"cqa-api-edit-step-step3-content\">\n <div class=\"cqa-api-edit-step-verification-header-row cqa-flex cqa-flex-row cqa-justify-end cqa-items-center cqa-pt-0 cqa-pr-0 cqa-pb-2 cqa-pl-0\">\n <span></span>\n <cqa-button type=\"button\" variant=\"text\" text=\"Add Verification\"\n customClass=\"cqa-api-edit-step-verification-add-link\" (clicked)=\"addVerificationRow()\">\n </cqa-button>\n </div>\n <div class=\"cqa-api-edit-step-verification\">\n <div class=\"cqa-api-edit-step-verification-grid cqa-api-edit-step-verification-grid-header\">\n <span class=\"cqa-api-edit-step-verification-label\">S.no</span>\n <span class=\"cqa-api-edit-step-verification-label\">JSON Path</span>\n <span class=\"cqa-api-edit-step-verification-label\">Verification</span>\n <span class=\"cqa-api-edit-step-verification-label\">Data Type</span>\n <span class=\"cqa-api-edit-step-verification-label\">Expected Value</span>\n <span class=\"cqa-api-edit-step-verification-label cqa-api-edit-step-verification-label--empty\"\n aria-hidden=\"true\"></span>\n </div>\n <div *ngFor=\"let row of verificationRows; let i = index; trackBy: trackByVerification\" [formGroup]=\"row\"\n class=\"cqa-api-edit-step-verification-row\">\n <span class=\"cqa-api-edit-step-verification-sno\">{{ i + 1 }}</span>\n <cqa-custom-input [value]=\"row.get('jsonPath')?.value ?? ''\"\n (valueChange)=\"row.get('jsonPath')?.setValue($event)\" placeholder=\"Json Path\" [fullWidth]=\"true\"\n size=\"sm\" class=\"cqa-api-edit-step-verification-input\" ariaLabel=\"JSON Path\">\n </cqa-custom-input>\n <cqa-dynamic-select [form]=\"row\" [config]=\"verificationSelectConfig\"\n class=\"cqa-api-edit-step-verification-select cqa-w-full\">\n </cqa-dynamic-select>\n <cqa-dynamic-select [form]=\"row\" [config]=\"verificationDataTypeSelectConfig\"\n class=\"cqa-api-edit-step-verification-select cqa-w-full\"\n (selectionChange)=\"onVerificationDataTypeChange(row, $event)\">\n </cqa-dynamic-select>\n <!-- Expected Value: text for String/Array/Object, number for Number, dropdown for Boolean -->\n <ng-container [ngSwitch]=\"row.get('dataType')?.value\">\n <cqa-dynamic-select *ngSwitchCase=\"'boolean'\" [form]=\"row\"\n [config]=\"verificationExpectedValueBooleanSelectConfig\"\n class=\"cqa-api-edit-step-verification-select cqa-api-edit-step-verification-expected-select cqa-w-full\">\n </cqa-dynamic-select>\n <cqa-custom-input *ngSwitchCase=\"'number'\" [value]=\"row.get('expectedValue')?.value ?? ''\"\n (valueChange)=\"row.get('expectedValue')?.setValue($event)\" placeholder=\"Expected Value (number)\"\n type=\"number\" [fullWidth]=\"true\" size=\"sm\"\n class=\"cqa-api-edit-step-verification-input cqa-api-edit-step-verification-expected-number\"\n ariaLabel=\"Expected Value\">\n </cqa-custom-input>\n <cqa-custom-input *ngSwitchDefault [value]=\"row.get('expectedValue')?.value ?? ''\"\n (valueChange)=\"row.get('expectedValue')?.setValue($event)\"\n placeholder=\"Expected Value in String\" [fullWidth]=\"true\" size=\"sm\"\n class=\"cqa-api-edit-step-verification-input\" ariaLabel=\"Expected Value\">\n </cqa-custom-input>\n </ng-container>\n <cqa-button type=\"button\" variant=\"text\" [customClass]=\"'cqa-api-edit-step-verification-delete'\"\n [tooltip]=\"'Remove row'\" (clicked)=\"removeVerificationRow(i)\">\n <svg class=\"cqa-api-edit-step-verification-delete-icon\" width=\"16\" height=\"16\" viewBox=\"0 0 16 16\"\n fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\">\n <path\n d=\"M10.6663 6V12.6667H5.33301V6H10.6663ZM9.66634 2H6.33301L5.66634 2.66667H3.33301V4H12.6663V2.66667H10.333L9.66634 2ZM11.9997 4.66667H3.99967V12.6667C3.99967 13.4 4.59967 14 5.33301 14H10.6663C11.3997 14 11.9997 13.4 11.9997 12.6667V4.66667Z\"\n fill=\"#F9BFBF\" />\n </svg>\n </cqa-button>\n </div>\n </div>\n </div>\n\n <!-- Status tab content: S.no, Verification dropdown, Expected Value, add/remove rows -->\n <div *ngIf=\"activeResponseVerificationTab === 'status'\" class=\"cqa-api-edit-step-step3-content\">\n <div class=\"cqa-api-edit-step-verification-header-row cqa-flex cqa-flex-row cqa-justify-end cqa-items-center cqa-pt-0 cqa-pr-0 cqa-pb-2 cqa-pl-0\">\n <span></span>\n <cqa-button type=\"button\" variant=\"text\" text=\"Add Verification\"\n customClass=\"cqa-api-edit-step-verification-add-link\" (clicked)=\"addStatusVerificationRow()\">\n </cqa-button>\n </div>\n <div class=\"cqa-api-edit-step-status-verification\">\n <div class=\"cqa-api-edit-step-status-verification-header\">\n <span class=\"cqa-api-edit-step-verification-label\">S.no</span>\n <span class=\"cqa-api-edit-step-verification-label\">Verification</span>\n <span class=\"cqa-api-edit-step-verification-label\">Expected Value</span>\n <span class=\"cqa-api-edit-step-verification-label cqa-api-edit-step-verification-label--empty\"\n aria-hidden=\"true\"></span>\n </div>\n <div *ngFor=\"let row of statusVerificationRows; let i = index; trackBy: trackByStatusVerification\"\n [formGroup]=\"row\" class=\"cqa-api-edit-step-status-verification-row\">\n <span class=\"cqa-api-edit-step-verification-sno\">{{ i + 1 }}</span>\n <cqa-dynamic-select [form]=\"row\" [config]=\"statusVerificationSelectConfig\"\n class=\"cqa-api-edit-step-status-verification-select cqa-w-full\">\n </cqa-dynamic-select>\n <cqa-custom-input [value]=\"row.get('expectedValue')?.value ?? ''\"\n (valueChange)=\"row.get('expectedValue')?.setValue($event)\" placeholder=\"Expected Value\"\n type=\"number\" [fullWidth]=\"true\" size=\"sm\" class=\"cqa-api-edit-step-verification-input\"\n ariaLabel=\"Expected Value\">\n </cqa-custom-input>\n <cqa-button type=\"button\" variant=\"text\" [customClass]=\"'cqa-api-edit-step-verification-delete'\"\n [tooltip]=\"'Remove row'\" (clicked)=\"removeStatusVerificationRow(i)\">\n <svg class=\"cqa-api-edit-step-verification-delete-icon\" width=\"16\" height=\"16\" viewBox=\"0 0 16 16\"\n fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\">\n <path\n d=\"M10.6663 6V12.6667H5.33301V6H10.6663ZM9.66634 2H6.33301L5.66634 2.66667H3.33301V4H12.6663V2.66667H10.333L9.66634 2ZM11.9997 4.66667H3.99967V12.6667C3.99967 13.4 4.59967 14 5.33301 14H10.6663C11.3997 14 11.9997 13.4 11.9997 12.6667V4.66667Z\"\n fill=\"#F9BFBF\" />\n </svg>\n </cqa-button>\n </div>\n </div>\n </div>\n </div>\n </div>\n </div>\n\n <!-- Response Preview -->\n <div #responsePreviewRef class=\"cqa-api-edit-step-response\">\n <h3 class=\"cqa-api-edit-step-response-title cqa-text-[14px] cqa-font-bold cqa-leading-[20px] cqa-text-[#111827] cqa-m-0 cqa-mb-[12px] cqa-shrink-0\">Response Preview</h3>\n <pre class=\"cqa-api-edit-step-response-content\">{{ responsePreview }}</pre>\n </div>\n\n <!-- Step actions: one row, full width. Step 1: Cancel + Next; Step 2: Back + Next; Step 3: Back + Create -->\n <div class=\"cqa-api-edit-step-actions\">\n <ng-container [ngSwitch]=\"currentStep\">\n <ng-container *ngSwitchCase=\"1\">\n <cqa-button type=\"button\" variant=\"outlined\" btnSize=\"lg\" text=\"Cancel\"\n customClass=\"cqa-api-edit-step-actions-btn-cancel\" (clicked)=\"onCancel()\">\n </cqa-button>\n <cqa-button type=\"button\" variant=\"filled\" btnSize=\"lg\" text=\"Next\"\n customClass=\"cqa-api-edit-step-actions-btn-next\" (clicked)=\"onNext()\">\n </cqa-button>\n </ng-container>\n <ng-container *ngSwitchCase=\"2\">\n <cqa-button type=\"button\" variant=\"outlined\" btnSize=\"lg\" text=\"Back\"\n customClass=\"cqa-api-edit-step-actions-btn-back\" (clicked)=\"onBack()\">\n </cqa-button>\n <cqa-button type=\"button\" variant=\"filled\" btnSize=\"lg\" text=\"Next\"\n customClass=\"cqa-api-edit-step-actions-btn-next\" (clicked)=\"onNext()\">\n </cqa-button>\n </ng-container>\n <ng-container *ngSwitchCase=\"3\">\n <cqa-button type=\"button\" variant=\"outlined\" btnSize=\"lg\" text=\"Back\"\n customClass=\"cqa-api-edit-step-actions-btn-back\" (clicked)=\"onBack()\">\n </cqa-button>\n <cqa-button *ngIf=\"!isCreatingStep\" type=\"button\" variant=\"filled\" btnSize=\"lg\" [text]=\"editMode ? 'Update' : 'Create'\"\n customClass=\"cqa-api-edit-step-actions-btn-create\" (clicked)=\"onCreate()\">\n </cqa-button>\n <!-- Match cqa-button flex: actions row styles > cqa-button { flex: 1 1 0 } \u2014 loader div must grow same width as Create -->\n <div *ngIf=\"isCreatingStep\" class=\"cqa-api-edit-step-actions-loader cqa-w-full cqa-min-h-[38px] cqa-rounded-[8px] cqa-flex cqa-items-center cqa-justify-center\">\n <cqa-badge label=\"Creating\u2026\" icon=\"autorenew\" [isLoading]=\"true\" [fullWidth]=\"true\" [centerContent]=\"true\"\n [inlineStyles]=\"'min-height: 38px; height: 38px; width: 100%; box-sizing: border-box; display: flex; align-items: center; justify-content: center;'\"\n variant=\"info\" size=\"medium\"></cqa-badge>\n </div>\n </ng-container>\n </ng-container>\n </div>\n</div>" }]
38782
- }], ctorParameters: function () { return [{ type: i1$1.FormBuilder }, { type: i0.ChangeDetectorRef }]; }, propDecorators: { initialMethod: [{
39408
+ args: [{ selector: 'cqa-api-edit-step', host: { class: 'cqa-ui-root' }, changeDetection: ChangeDetectionStrategy.Default, template: "<div class=\"cqa-api-edit-step-container cqa-bg-[#ffffff] cqa-flex cqa-flex-col cqa-w-full cqa-h-full cqa-p-[16px] cqa-gap-[12px] cqa-overflow-y-auto cqa-overflow-x-hidden cqa-box-border\">\n\n <h2 class=\"cqa-api-edit-step-title cqa-font-inter cqa-text-[12px] cqa-font-semibold cqa-leading-[100%] cqa-tracking-normal cqa-text-[#000000] cqa-m-0\">\n {{ editMode ? 'Update API Test Step' : 'Create API Test Step' }}\n </h2>\n\n <div class=\"cqa-api-edit-step-indicator cqa-flex cqa-items-center cqa-gap-0 cqa-max-[767px]:cqa-flex-wrap cqa-max-[767px]:cqa-gap-2 cqa-max-[767px]:cqa-justify-start\"\n [class.cqa-justify-start]=\"stepLabels.length === 1\"\n [class.cqa-justify-between]=\"stepLabels.length !== 1\">\n <ng-container *ngFor=\"let step of stepLabels; let i = index\">\n <cqa-button type=\"button\" variant=\"text\"\n customClass=\"cqa-api-edit-step-indicator-item cqa-api-edit-step-indicator-item--clickable cqa-flex cqa-items-center cqa-gap-[10px] cqa-cursor-pointer cqa-border-none cqa-p-0 cqa-font-inherit cqa-text-inherit cqa-text-left !cqa-bg-transparent\"\n [attr.aria-label]=\"'Step ' + step.index + ': ' + step.label\" [attr.aria-pressed]=\"step.index === currentStep\"\n (clicked)=\"setStep(step.index)\">\n <span class=\"cqa-api-edit-step-indicator-circle cqa-w-8 cqa-h-8 cqa-rounded-full cqa-inline-flex cqa-items-center cqa-justify-center cqa-text-sm cqa-font-medium cqa-leading-none cqa-border-none cqa-shrink-0\"\n [ngClass]=\"step.index === currentStep ? 'cqa-bg-[#4F46E5] cqa-text-white' : 'cqa-bg-[#E0E0E0] cqa-text-[#666666]'\">\n {{ step.index }}\n </span>\n <span class=\"cqa-api-edit-step-indicator-label cqa-text-sm cqa-leading-[18px] cqa-font-normal cqa-max-[767px]:cqa-text-xs\"\n [ngClass]=\"step.index === currentStep ? 'cqa-text-[#4F46E5]' : 'cqa-text-[#666666]'\">\n {{ step.label }}\n </span>\n <div *ngIf=\"i < stepLabels.length - 1\" class=\"cqa-api-edit-step-indicator-line cqa-w-[96px] cqa-h-[2px] cqa-bg-[#E0E0E0] cqa-my-0 cqa-mx-[6px] cqa-shrink-0 cqa-self-center cqa-max-[767px]:cqa-w-[24px] cqa-max-[767px]:cqa-mx-1\" aria-hidden=\"true\"></div>\n </cqa-button>\n </ng-container>\n </div>\n\n <!-- Step content viewport: only the active step body is shown (*ngIf) -->\n <div class=\"cqa-api-edit-step-viewport cqa-w-full\">\n <!-- Step 1: Environment, request, body, response -->\n <div *ngIf=\"currentStep === 1\" class=\"cqa-api-edit-step-panel cqa-flex cqa-flex-col cqa-gap-6 cqa-box-border cqa-w-full\">\n <!-- API Type segment: REST API (default) | GraphQL | SOAP/WebSocket/gRPC (disabled) -->\n <div class=\"cqa-api-edit-step-api-type-row cqa-flex cqa-flex-col cqa-gap-1.5\">\n <label class=\"cqa-api-edit-step-api-type-label cqa-m-0 cqa-text-[14px] cqa-font-semibold cqa-leading-[100%] cqa-tracking-normal\">API Type</label>\n <cqa-segment-control\n [value]=\"activeApiType\"\n [segments]=\"apiTypeSegments\"\n (valueChange)=\"onApiTypeChange($event)\"\n class=\"cqa-api-edit-step-api-type-segment\">\n </cqa-segment-control>\n </div>\n\n <!-- Summary field: label on top, input below (above Environment) -->\n <div class=\"cqa-api-edit-step-summary-row cqa-flex cqa-flex-col cqa-gap-1.5 cqa-items-stretch\">\n <label class=\"cqa-api-edit-step-summary-label cqa-m-0 cqa-text-[14px] cqa-font-semibold cqa-leading-[100%] cqa-tracking-normal\">Summary</label>\n <cqa-custom-input [(value)]=\"summary\"\n [label]=\"''\" placeholder=\"Enter Request summary\" [fullWidth]=\"true\" size=\"md\"\n ariaLabel=\"Summary\">\n </cqa-custom-input>\n </div>\n <!-- Environment row: label on top, select below (aligned right on desktop) -->\n <div class=\"cqa-api-edit-step-environment-row cqa-flex cqa-justify-end cqa-gap-1.5 cqa-items-end cqa-max-[767px]:cqa-items-stretch\">\n <div *ngIf=\"activeApiType === 'graphql'\" class=\"cqa-api-edit-step-url-wrap cqa-flex-1 cqa-min-w-[200px] cqa-max-[767px]:cqa-min-w-0 cqa-max-[767px]:cqa-w-full\">\n <cqa-autocomplete\n [value]=\"url\"\n (valueChange)=\"url = $event\"\n [options]=\"urlOptions\"\n [placeholder]=\"urlPlaceholder\"\n ariaLabel=\"Enter URL or select Parameter\"\n [fullWidth]=\"true\"\n (optionSelect)=\"onUrlOptionSelect($event)\">\n </cqa-autocomplete>\n <div *ngIf=\"urlError\" class=\"cqa-text-red-600 cqa-text-sm cqa-mt-1\">\n {{ urlError }}\n </div>\n </div>\n\n <div class=\"cqa-flex cqa-flex-col cqa-gap-1.5 cqa-max-[767px]:cqa-items-stretch\">\n <label class=\"cqa-api-edit-step-environment-label cqa-m-0 cqa-text-[14px] cqa-font-semibold cqa-leading-[100%] cqa-tracking-normal\">Environments</label>\n <cqa-dynamic-select *ngIf=\"environmentForm\"\n [form]=\"environmentForm\"\n [config]=\"environmentSelectConfig\"\n class=\"cqa-api-edit-step-environment-select cqa-shrink-0 cqa-w-auto cqa-min-w-[315px] cqa-max-w-[315px] cqa-max-[767px]:cqa-min-w-0 cqa-max-[767px]:cqa-max-w-full cqa-max-[767px]:cqa-w-full [&_.mat-form-field-wrapper]:cqa-pb-0\"\n aria-label=\"Environment\"\n (selectionChange)=\"onEnvironmentSelectionChange($event)\">\n </cqa-dynamic-select>\n </div>\n </div>\n\n <!-- Request row: method, URL, buttons -->\n <div [ngClass]=\"{'cqa-items-start': activeApiType === 'rest', 'cqa-justify-between': activeApiType === 'graphql'}\" class=\"cqa-api-edit-step-request-row cqa-flex cqa-gap-3 cqa-flex-wrap cqa-max-[767px]:cqa-flex-col cqa-max-[767px]:cqa-items-stretch cqa-max-[767px]:cqa-gap-2.5\">\n <!-- REST mode shows the HTTP Method dropdown. GraphQL pins method to POST internally so the dropdown is hidden. -->\n <cqa-dynamic-select *ngIf=\"methodForm && activeApiType === 'rest'\" [form]=\"methodForm\" [config]=\"methodSelectConfig\"\n class=\"cqa-api-edit-step-method-select cqa-shrink-0 cqa-w-auto cqa-min-w-[152px] cqa-max-w-[152px] cqa-max-[767px]:cqa-min-w-0 cqa-max-[767px]:cqa-max-w-full cqa-max-[767px]:cqa-w-full\" aria-label=\"HTTP method\"\n (selectionChange)=\"onMethodSelectionChange($event)\">\n </cqa-dynamic-select>\n <div *ngIf=\"activeApiType === 'rest'\" class=\"cqa-api-edit-step-url-wrap cqa-flex-1 cqa-min-w-[200px] cqa-max-[767px]:cqa-min-w-0 cqa-max-[767px]:cqa-w-full\">\n <cqa-autocomplete\n [value]=\"url\"\n (valueChange)=\"url = $event\"\n [options]=\"urlOptions\"\n [placeholder]=\"urlPlaceholder\"\n ariaLabel=\"Enter URL or select Parameter\"\n [fullWidth]=\"true\"\n (optionSelect)=\"onUrlOptionSelect($event)\">\n </cqa-autocomplete>\n <div *ngIf=\"urlError\" class=\"cqa-text-red-600 cqa-text-sm cqa-mt-1\">\n {{ urlError }}\n </div>\n </div>\n\n <!-- Import API cURL: REST-only (the cURL parser produces REST shapes). -->\n <div *ngIf=\"activeApiType === 'rest'\" class=\"cqa-api-edit-step-import-curl-trigger cqa-contents cqa-max-[767px]:cqa-w-full cqa-max-[767px]:!cqa-flex\" (click)=\"openImportCurlPanel()\">\n <cqa-button type=\"button\" variant=\"outlined\" btnSize=\"lg\" text=\"Import API cURL\"\n customClass=\"cqa-api-edit-step-btn-outline !cqa-bg-white !cqa-border !cqa-border-solid !cqa-border-[#6B7280] cqa-text-[#414146] cqa-font-inter cqa-text-[14px] cqa-font-semibold cqa-leading-[100%] cqa-tracking-normal hover:!cqa-bg-[#F9FAFB] hover:!cqa-text-[#414146] cqa-max-[767px]:cqa-w-full cqa-mt-1\">\n </cqa-button>\n </div>\n <!--\n Load Schema: GraphQL-only. Uses the elevated cqa-button variant so the\n indigo surface + text + elevation shadow come from the design system \u2014\n customClass keeps only the typography + responsive sizing tweaks.\n Disabled / blocked styling is handled natively by the elevated variant\n (cqa-bg-primary-muted, muted text, no shadow), so we don't add an\n opacity override on top.\n -->\n <cqa-button *ngIf=\"activeApiType === 'graphql'\" type=\"button\" variant=\"elevated\" btnSize=\"lg\" text=\"Load Schema\"\n icon=\"schema\"\n customClass=\"cqa-font-inter cqa-text-[14px] cqa-font-semibold cqa-leading-[100%] cqa-tracking-normal cqa-max-[767px]:cqa-w-full cqa-mt-1\"\n [disabled]=\"!url || !url.trim() || isLoadingGraphQLSchema\"\n (clicked)=\"onLoadSchema()\">\n </cqa-button>\n <cqa-button variant=\"filled\" btnSize=\"lg\" text=\"Send Request\" (clicked)=\"onSendRequest()\"\n [disabled]=\"isSendRequestDisabled\"\n customClass=\"cqa-api-edit-step-btn-primary cqa-font-inter cqa-text-[14px] cqa-font-semibold cqa-leading-[100%] cqa-tracking-normal !cqa-bg-[#3F43EE] cqa-text-[#FBFCFF] !cqa-border-none cqa-py-2.5 cqa-px-6 cqa-gap-2 cqa-rounded-lg cqa-min-h-[37px] cqa-box-border hover:!cqa-bg-[#1B1FEB] cqa-max-[767px]:cqa-w-full cqa-mt-1\"\n [ngClass]=\"{'!cqa-opacity-50 !cqa-cursor-not-allowed': isSendRequestDisabled}\">\n </cqa-button>\n </div>\n\n\n\n <!-- Body content: header section (default) OR import cURL section -->\n <ng-container *ngIf=\"bodyView === 'import-curl'\">\n <div class=\"cqa-api-edit-step-import-curl-panel cqa-flex cqa-flex-col cqa-bg-white cqa-rounded-lg cqa-border cqa-border-solid cqa-border-[#E5E7EB] cqa-shadow-[0_1px_3px_rgba(0,0,0,0.08)] cqa-overflow-hidden\">\n <div class=\"cqa-api-edit-step-import-curl-header cqa-flex cqa-items-center cqa-justify-between cqa-py-3 cqa-px-5 cqa-border-b cqa-border-solid cqa-border-[#E2E8F0] cqa-bg-[#F9FAFB] cqa-rounded-t-lg cqa-max-[767px]:cqa-px-4\">\n <h3 class=\"cqa-api-edit-step-import-curl-title cqa-m-0 cqa-text-xs cqa-font-bold cqa-leading-[18px] cqa-text-[#374151]\">Import API cURL</h3>\n </div>\n <div class=\"cqa-api-edit-step-import-curl-separator cqa-h-px cqa-bg-[#E2E8F0] cqa-my-0 cqa-mx-5\"></div>\n <div class=\"cqa-api-edit-step-import-curl-note cqa-px-5 cqa-pt-3\">\n <cqa-badge label=\"cURL is supported in Bash format only.\" icon=\"info\" variant=\"info\" size=\"small\" [fullWidth]=\"true\"></cqa-badge>\n </div>\n <div class=\"cqa-api-edit-step-import-curl-body\">\n <cqa-custom-textarea [value]=\"importCurlControl.value\"\n (valueChange)=\"importCurlControl.setValue($event)\"\n placeholder=\"Paste your cURL command here (e.g., curl -X POST https://api.example.com/users)\"\n [fullWidth]=\"true\" [rows]=\"8\" resize=\"vertical\" size=\"md\"\n class=\"cqa-api-edit-step-import-curl-textarea\">\n </cqa-custom-textarea>\n </div>\n <div class=\"cqa-api-edit-step-import-curl-footer\">\n <cqa-button type=\"button\" variant=\"outlined\" btnSize=\"lg\" text=\"Cancel\"\n customClass=\"cqa-api-edit-step-import-curl-btn-cancel\" (clicked)=\"onCancelImportCurl()\"></cqa-button>\n <cqa-button type=\"button\" variant=\"filled\" btnSize=\"lg\" text=\"Import\"\n customClass=\"cqa-api-edit-step-import-curl-btn-import\" (clicked)=\"onImportCurlConfirm()\"></cqa-button>\n </div>\n </div>\n </ng-container>\n\n <!-- GraphQL operation picker \u2014 opened by the \"Load Schema\" button in GraphQL mode. -->\n <ng-container *ngIf=\"bodyView === 'graphql-schema'\">\n <cqa-graphql-operation-picker\n [schemaSource]=\"graphQLSchemaSource\"\n [operations]=\"graphQLOperations\"\n [introspected]=\"!!graphQLSchemaSource\"\n [isLoading]=\"isLoadingGraphQLSchema\"\n (back)=\"onGraphQLPickerBack()\"\n (change)=\"onGraphQLPickerBack()\"\n (reIntrospect)=\"onGraphQLPickerReIntrospect()\"\n (operationSelected)=\"onGraphQLPickerOperationSelected($event)\">\n </cqa-graphql-operation-picker>\n </ng-container>\n\n <ng-container *ngIf=\"bodyView === 'headers'\">\n <div class=\"cqa-api-edit-step-tabs-wrapper cqa-flex cqa-flex-col cqa-gap-3\">\n <div class=\"cqa-api-edit-step-tabs\">\n <cqa-button *ngFor=\"let tab of payloadTabs\" type=\"button\" variant=\"text\" [text]=\"tab.label\"\n [customClass]=\"'cqa-api-edit-step-tab' + (activePayloadTab === tab.value ? ' cqa-api-edit-step-tab--active' : '') + (isMethodWithoutBody && (tab.value === 'body' || tab.value === 'params') ? ' cqa-api-edit-step-tab--disabled' : '')\"\n [disabled]=\"isMethodWithoutBody && (tab.value === 'body' || tab.value === 'params')\"\n (clicked)=\"setPayloadTab(tab.value)\">\n </cqa-button>\n </div>\n\n <!-- Payload content (Authorization) -->\n <div *ngIf=\"activePayloadTab === 'authorization'\" class=\"cqa-api-edit-step-payload\"\n [attr.style]=\"'padding: 20px; min-height: 200px;'\">\n <div [attr.style]=\"'display: flex; flex-direction: column; gap: 20px;'\">\n <div [attr.style]=\"'display: flex; flex-direction: column; gap: 8px; width: fit-content;'\">\n <span [attr.style]=\"'font-size: 11px; font-weight: 600; letter-spacing: 0.02em; color: #6B7280; text-transform: uppercase; line-height: 1.25;'\">AUTH TYPE</span>\n <cqa-dynamic-select *ngIf=\"authTypeForm\" [form]=\"authTypeForm\" [config]=\"authTypeSelectConfig\"\n class=\"cqa-api-edit-step-auth-type-select\" aria-label=\"Auth type\"\n [attr.style]=\"'min-width: 160px;'\">\n </cqa-dynamic-select>\n </div>\n <!-- No Auth: centered message -->\n <div *ngIf=\"selectedAuthType === 'no-auth'\"\n [attr.style]=\"'flex: 1; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 12px; min-width: 0; text-align: center; border: 1px solid #E5E7EB; border-radius: 8px; padding-top: 16px; padding-bottom: 16px;'\">\n <div aria-hidden=\"true\"\n [attr.style]=\"'width: 48px; height: 48px; border-radius: 10px; background: #F3F4F6; display: flex; align-items: center; justify-content: center; flex-shrink: 0;'\">\n <svg width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\" style=\"display: block;\">\n <rect x=\"5\" y=\"11\" width=\"14\" height=\"2\" rx=\"1\" fill=\"#9CA3AF\"/>\n </svg>\n </div>\n <span [attr.style]=\"'font-size: 16px; font-weight: 600; color: #374151; line-height: 1.25;'\">No Auth</span>\n <div [attr.style]=\"'display: flex; align-items: center; justify-content: center; gap: 6px; flex-wrap: wrap;'\">\n <span [attr.style]=\"'font-size: 14px; font-weight: 400; color: #9CA3AF; line-height: 1.4;'\">This request does not use any authorization.</span>\n <span role=\"img\" aria-label=\"More information\" title=\"This request does not use any authorization.\"\n [attr.style]=\"'display: inline-flex; align-items: center; justify-content: center; width: 18px; height: 18px; color: #9CA3AF; flex-shrink: 0;'\">\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 16 16\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\" style=\"display: block;\">\n <circle cx=\"8\" cy=\"8\" r=\"7\" stroke=\"#9CA3AF\" stroke-width=\"1.5\" fill=\"none\"/>\n <path d=\"M8 7v4M8 5v.5\" stroke=\"#9CA3AF\" stroke-width=\"1.5\" stroke-linecap=\"round\"/>\n </svg>\n </span>\n </div>\n </div>\n <!-- Bearer Token: Token label + textarea -->\n <div *ngIf=\"selectedAuthType === 'bearer'\"\n [attr.style]=\"'flex: 1; display: flex; flex-direction: column; gap: 10px; min-width: 0;'\">\n <span [attr.style]=\"'font-size: 12px; font-weight: 600; color: #374151; line-height: 1.25;'\">Token</span>\n <cqa-custom-textarea [(value)]=\"bearerToken\"\n placeholder=\"Enter bearer token\"\n [fullWidth]=\"true\" [rows]=\"6\" resize=\"vertical\" size=\"md\"\n class=\"cqa-api-edit-step-auth-token-textarea\" ariaLabel=\"Bearer token\">\n </cqa-custom-textarea>\n </div>\n <!-- OAuth 2.0: Grant Type, Access Token URL, Client ID, Client Secret, Scope, Client Authentication -->\n <div *ngIf=\"selectedAuthType === 'oauth2' && oauth2Form\"\n class=\"cqa-api-edit-step-oauth2-grid\"\n [attr.style]=\"'display: grid; grid-template-columns: 1fr 1fr; gap: 16px 24px; width: 100%;'\">\n <div [attr.style]=\"'display: flex; flex-direction: column; gap: 6px; min-width: 0;'\">\n <span [attr.style]=\"'font-size: 12px; font-weight: 600; color: #374151; line-height: 1.25;'\">Grant Type</span>\n <cqa-dynamic-select *ngIf=\"oauth2Form\" [form]=\"oauth2Form\" [config]=\"oauth2GrantTypeSelectConfig\"\n class=\"cqa-api-edit-step-auth-oauth-select\" aria-label=\"OAuth grant type\"\n [attr.style]=\"'min-width: 0; width: 100%;'\">\n </cqa-dynamic-select>\n <span [attr.style]=\"'font-size: 11px; font-style: italic; color: #6B7280; line-height: 1.3;'\">OAuth grant type</span>\n </div>\n <div [attr.style]=\"'display: flex; flex-direction: column; gap: 6px; min-width: 0;'\">\n <span [attr.style]=\"'font-size: 12px; font-weight: 600; color: #374151; line-height: 1.25;'\">Access Token URL</span>\n <cqa-custom-input [value]=\"oauth2Form.get('accessTokenUrl')?.value\"\n (valueChange)=\"oauth2Form.get('accessTokenUrl')?.setValue($event)\"\n [label]=\"''\" placeholder=\"Enter OAuth token endpoint\" [fullWidth]=\"true\" size=\"md\"\n ariaLabel=\"Access Token URL\">\n </cqa-custom-input>\n <span [attr.style]=\"'font-size: 11px; font-style: italic; color: #6B7280; line-height: 1.3;'\">OAuth token endpoint</span>\n </div>\n <div [attr.style]=\"'display: flex; flex-direction: column; gap: 6px; min-width: 0;'\">\n <span [attr.style]=\"'font-size: 12px; font-weight: 600; color: #374151; line-height: 1.25;'\">Client ID</span>\n <cqa-custom-input [value]=\"oauth2Form.get('clientId')?.value\"\n (valueChange)=\"oauth2Form.get('clientId')?.setValue($event)\"\n [label]=\"''\" placeholder=\"Enter OAuth client identifier\" [fullWidth]=\"true\" size=\"md\"\n ariaLabel=\"Client ID\">\n </cqa-custom-input>\n <span [attr.style]=\"'font-size: 11px; font-style: italic; color: #6B7280; line-height: 1.3;'\">OAuth client identifier</span>\n </div>\n <div [attr.style]=\"'display: flex; flex-direction: column; gap: 6px; min-width: 0;'\">\n <span [attr.style]=\"'font-size: 12px; font-weight: 600; color: #374151; line-height: 1.25;'\">Client Secret</span>\n <cqa-custom-input [value]=\"oauth2Form.get('clientSecret')?.value\"\n (valueChange)=\"oauth2Form.get('clientSecret')?.setValue($event)\"\n type=\"password\" [label]=\"''\" placeholder=\"Enter OAuth client secret\" [fullWidth]=\"true\" size=\"md\"\n ariaLabel=\"Client Secret\">\n </cqa-custom-input>\n <span [attr.style]=\"'font-size: 11px; font-style: italic; color: #6B7280; line-height: 1.3;'\">OAuth client secret</span>\n </div>\n <div [attr.style]=\"'display: flex; flex-direction: column; gap: 6px; min-width: 0;'\">\n <span [attr.style]=\"'font-size: 12px; font-weight: 600; color: #374151; line-height: 1.25;'\">Scope</span>\n <cqa-custom-input [value]=\"oauth2Form.get('scope')?.value\"\n (valueChange)=\"oauth2Form.get('scope')?.setValue($event)\"\n [label]=\"''\" placeholder=\"Enter space-separated scopes\" [fullWidth]=\"true\" size=\"md\"\n ariaLabel=\"Scope\">\n </cqa-custom-input>\n <span [attr.style]=\"'font-size: 11px; font-style: italic; color: #6B7280; line-height: 1.3;'\">Space-separated scopes</span>\n </div>\n <div [attr.style]=\"'display: flex; flex-direction: column; gap: 6px; min-width: 0;'\">\n <span [attr.style]=\"'font-size: 12px; font-weight: 600; color: #374151; line-height: 1.25;'\">Client Authentication</span>\n <cqa-dynamic-select *ngIf=\"oauth2Form\" [form]=\"oauth2Form\" [config]=\"oauth2ClientAuthSelectConfig\"\n class=\"cqa-api-edit-step-auth-oauth-select\" aria-label=\"Client authentication method\"\n [attr.style]=\"'min-width: 0; width: 100%;'\">\n </cqa-dynamic-select>\n <span [attr.style]=\"'font-size: 11px; font-style: italic; color: #6B7280; line-height: 1.3;'\">Client authentication method</span>\n </div>\n </div>\n </div>\n </div>\n\n <!-- Payload content (Headers) -->\n <div *ngIf=\"activePayloadTab === 'headers'\" class=\"cqa-api-edit-step-payload\">\n <div class=\"cqa-api-edit-step-headers-grid\">\n <span class=\"cqa-api-edit-step-headers-label\">Header Name</span>\n <span class=\"cqa-api-edit-step-headers-label\">Header Value</span>\n <span class=\"cqa-api-edit-step-headers-label cqa-api-edit-step-headers-label--empty\"\n aria-hidden=\"true\"></span>\n </div>\n <div *ngFor=\"let row of headerRows; let i = index; trackBy: trackByHeader\" [formGroup]=\"row\"\n class=\"cqa-api-edit-step-header-row\">\n <cqa-dynamic-select [form]=\"row\" [config]=\"headerNameSelectConfig\"\n (addCustomValue)=\"onHeaderNameAddCustomValue($event, row)\"\n class=\"cqa-api-edit-step-header-type-select cqa-w-full\">\n </cqa-dynamic-select>\n <cqa-custom-input [value]=\"row.get('value')?.value ?? ''\"\n (valueChange)=\"row.get('value')?.setValue($event)\" placeholder=\"Value\" [fullWidth]=\"true\" size=\"sm\"\n class=\"cqa-api-edit-step-header-value-input\" ariaLabel=\"Header value\">\n </cqa-custom-input>\n <cqa-button type=\"button\" variant=\"text\" [customClass]=\"'cqa-api-edit-step-header-delete'\"\n [tooltip]=\"'Remove header'\" (clicked)=\"removeHeader(i)\">\n <svg class=\"cqa-api-edit-step-header-delete-icon\" width=\"16\" height=\"16\" viewBox=\"0 0 16 16\"\n fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\">\n <path\n d=\"M10.6663 6V12.6667H5.33301V6H10.6663ZM9.66634 2H6.33301L5.66634 2.66667H3.33301V4H12.6663V2.66667H10.333L9.66634 2ZM11.9997 4.66667H3.99967V12.6667C3.99967 13.4 4.59967 14 5.33301 14H10.6663C11.3997 14 11.9997 13.4 11.9997 12.6667V4.66667Z\"\n fill=\"#F9BFBF\" />\n </svg>\n </cqa-button>\n </div>\n <div class=\"cqa-api-edit-step-add-header-wrap\">\n <cqa-button type=\"button\" variant=\"text\" text=\"+ Add Header\"\n [customClass]=\"'cqa-api-edit-step-add-header-link'\" (clicked)=\"addHeader()\">\n </cqa-button>\n </div>\n </div>\n\n <!-- Payload content (Params): Key\u2013Value table with add/delete rows. REST-only \u2014 GraphQL POST has no query params. -->\n <div *ngIf=\"activePayloadTab === 'params' && activeApiType === 'rest'\" class=\"cqa-api-edit-step-payload\">\n <div class=\"cqa-api-edit-step-key-value-grid cqa-api-edit-step-key-value-header\">\n <span class=\"cqa-api-edit-step-key-value-label\">Key</span>\n <span class=\"cqa-api-edit-step-key-value-label\">Value</span>\n <span class=\"cqa-api-edit-step-key-value-label cqa-api-edit-step-key-value-label--empty\"\n aria-hidden=\"true\"></span>\n </div>\n <div *ngFor=\"let row of paramsRows; let i = index; trackBy: trackByParams\" [formGroup]=\"row\"\n class=\"cqa-api-edit-step-key-value-row\">\n <cqa-custom-input [value]=\"row.get('key')?.value ?? ''\"\n (valueChange)=\"row.get('key')?.setValue($event)\" placeholder=\"Key\" [fullWidth]=\"true\" size=\"sm\"\n class=\"cqa-api-edit-step-key-value-input\" ariaLabel=\"Key\">\n </cqa-custom-input>\n <cqa-custom-input [value]=\"row.get('value')?.value ?? ''\"\n (valueChange)=\"row.get('value')?.setValue($event)\" placeholder=\"Value\" [fullWidth]=\"true\" size=\"sm\"\n class=\"cqa-api-edit-step-key-value-input\" ariaLabel=\"Value\">\n </cqa-custom-input>\n <cqa-button type=\"button\" variant=\"text\" [customClass]=\"'cqa-api-edit-step-key-value-delete'\"\n [tooltip]=\"'Remove row'\" (clicked)=\"removeParamsRow(i)\">\n <svg class=\"cqa-api-edit-step-key-value-delete-icon\" width=\"16\" height=\"16\" viewBox=\"0 0 16 16\"\n fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\">\n <path\n d=\"M10.6663 6V12.6667H5.33301V6H10.6663ZM9.66634 2H6.33301L5.66634 2.66667H3.33301V4H12.6663V2.66667H10.333L9.66634 2ZM11.9997 4.66667H3.99967V12.6667C3.99967 13.4 4.59967 14 5.33301 14H10.6663C11.3997 14 11.9997 13.4 11.9997 12.6667V4.66667Z\"\n fill=\"#F9BFBF\" />\n </svg>\n </cqa-button>\n </div>\n <div class=\"cqa-api-edit-step-key-value-add-wrap\">\n <cqa-button type=\"button\" variant=\"filled\" btnSize=\"lg\" text=\"+ Add Row\"\n customClass=\"cqa-api-edit-step-key-value-add-btn\" (clicked)=\"addParamsRow()\">\n </cqa-button>\n </div>\n </div>\n\n <!-- Payload content (Body only): type, format, text area, Back/Next. REST-only \u2014 GraphQL uses the dedicated tab below. -->\n <div *ngIf=\"activePayloadTab === 'body' && activeApiType === 'rest'\"\n class=\"cqa-api-edit-step-payload cqa-api-edit-step-payload-editor\">\n <div class=\"cqa-api-edit-step-payload-type-row\">\n <span class=\"cqa-api-edit-step-payload-type-label\">Type</span>\n <div class=\"cqa-api-edit-step-payload-type-radios\">\n <cqa-segment-control [value]=\"payloadType\" [segments]=\"payloadTypeSegments\"\n (valueChange)=\"onPayloadTypeChange($event)\"\n class=\"cqa-api-edit-step-payload-type-segment\">\n </cqa-segment-control>\n </div>\n <div *ngIf=\"payloadType === 'raw'\" class=\"cqa-api-edit-step-payload-format-wrap\">\n <span class=\"cqa-api-edit-step-payload-format-label\">Format:</span>\n <cqa-dynamic-select *ngIf=\"payloadFormatForm\" [form]=\"payloadFormatForm\"\n [config]=\"payloadFormatSelectConfig\" class=\"cqa-api-edit-step-payload-format-select\"\n aria-label=\"Format\">\n </cqa-dynamic-select>\n </div>\n </div>\n <!-- Raw: text area with line numbers (Postman-style payload body) -->\n <div *ngIf=\"payloadType === 'raw'\" class=\"cqa-api-edit-step-payload-body\" #payloadEditorWithLinesRef>\n <div class=\"cqa-api-edit-step-payload-editor-with-lines\">\n <div class=\"cqa-api-edit-step-payload-line-numbers\" aria-hidden=\"true\">\n <span *ngFor=\"let n of payloadLineNumbers\" class=\"cqa-api-edit-step-payload-line-num\">\n <span *ngIf=\"payloadJsonError && payloadJsonError.line === n\"\n class=\"cqa-api-edit-step-payload-line-error-icon\" [title]=\"payloadJsonErrorTooltip\"\n aria-label=\"Parse error on this line\">\n <svg xmlns=\"http://www.w3.org/2000/svg\" width=\"14\" height=\"14\" viewBox=\"0 0 14 14\" fill=\"none\" aria-hidden=\"true\">\n <circle cx=\"7\" cy=\"7\" r=\"6\" fill=\"#EF4444\"/>\n <path d=\"M4 4l6 6M10 4l-6 6\" stroke=\"white\" stroke-width=\"1.5\" stroke-linecap=\"round\"/>\n </svg>\n </span>\n <span class=\"cqa-api-edit-step-payload-line-num-text\">{{ n }}</span>\n </span>\n </div>\n <div class=\"cqa-api-edit-step-payload-textarea-cell\">\n <div class=\"cqa-api-edit-step-payload-textarea cqa-w-full cqa-flex cqa-flex-col cqa-min-h-0\"\n [ngClass]=\"{'cqa-api-edit-step-payload-textarea--error': payloadJsonError}\">\n <textarea #payloadTextareaRef\n class=\"cqa-api-edit-step-payload-textarea-input cqa-w-full cqa-outline-none cqa-box-border\"\n [ngClass]=\"{'cqa-api-edit-step-payload-textarea-input--error': payloadJsonError}\"\n [value]=\"payloadText\"\n placeholder=\"\"\n [attr.aria-label]=\"'Payload'\"\n [attr.aria-invalid]=\"!!payloadJsonError\"\n (input)=\"onPayloadInput($event)\"\n (keydown)=\"onPayloadKeydown($event)\">\n </textarea>\n </div>\n </div>\n </div>\n <p *ngIf=\"payloadJsonError\" class=\"cqa-api-edit-step-payload-json-error-msg\">\n Invalid JSON format. Please check your syntax.\n </p>\n </div>\n\n <!-- x-www-form-urlencoded: Key\u2013Value rows, add/remove dynamically -->\n <div *ngIf=\"payloadType === 'x-www-form-urlencoded'\" class=\"cqa-api-edit-step-key-value\">\n <div class=\"cqa-api-edit-step-key-value-grid cqa-api-edit-step-key-value-header\">\n <span class=\"cqa-api-edit-step-key-value-label\">Key</span>\n <span class=\"cqa-api-edit-step-key-value-label\">Value</span>\n <span class=\"cqa-api-edit-step-key-value-label cqa-api-edit-step-key-value-label--empty\"\n aria-hidden=\"true\"></span>\n </div>\n <div *ngFor=\"let row of keyValueRows; let i = index; trackBy: trackByKeyValue\" [formGroup]=\"row\"\n class=\"cqa-api-edit-step-key-value-row\">\n <cqa-custom-input [value]=\"row.get('key')?.value ?? ''\"\n (valueChange)=\"row.get('key')?.setValue($event)\" placeholder=\"Key\" [fullWidth]=\"true\" size=\"sm\"\n class=\"cqa-api-edit-step-key-value-input\" ariaLabel=\"Key\">\n </cqa-custom-input>\n <cqa-custom-input [value]=\"row.get('value')?.value ?? ''\"\n (valueChange)=\"row.get('value')?.setValue($event)\" placeholder=\"Value\" [fullWidth]=\"true\" size=\"sm\"\n class=\"cqa-api-edit-step-key-value-input\" ariaLabel=\"Value\">\n </cqa-custom-input>\n <cqa-button type=\"button\" variant=\"text\" [customClass]=\"'cqa-api-edit-step-key-value-delete'\"\n [tooltip]=\"'Remove row'\" (clicked)=\"removeKeyValueRow(i)\">\n <svg class=\"cqa-api-edit-step-key-value-delete-icon\" width=\"16\" height=\"16\" viewBox=\"0 0 16 16\"\n fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\">\n <path\n d=\"M10.6663 6V12.6667H5.33301V6H10.6663ZM9.66634 2H6.33301L5.66634 2.66667H3.33301V4H12.6663V2.66667H10.333L9.66634 2ZM11.9997 4.66667H3.99967V12.6667C3.99967 13.4 4.59967 14 5.33301 14H10.6663C11.3997 14 11.9997 13.4 11.9997 12.6667V4.66667Z\"\n fill=\"#F9BFBF\" />\n </svg>\n </cqa-button>\n </div>\n <div class=\"cqa-api-edit-step-key-value-add-wrap\">\n <cqa-button type=\"button\" variant=\"filled\" btnSize=\"lg\" text=\"+ Add Row\"\n customClass=\"cqa-api-edit-step-key-value-add-btn\" (clicked)=\"addKeyValueRow()\">\n </cqa-button>\n </div>\n </div>\n\n <!-- Form Data: Key\u2013Type\u2013Value rows; Type is a dropdown (Text/File), add/remove dynamically -->\n <div *ngIf=\"payloadType === 'form-data'\" class=\"cqa-api-edit-step-key-type-value\">\n <div class=\"cqa-api-edit-step-key-type-value-header\">\n <span class=\"cqa-api-edit-step-key-type-value-label\">Key</span>\n <span class=\"cqa-api-edit-step-key-type-value-label\">Type</span>\n <span class=\"cqa-api-edit-step-key-type-value-label\">Value</span>\n <span class=\"cqa-api-edit-step-key-type-value-label cqa-api-edit-step-key-type-value-label--empty\"\n aria-hidden=\"true\"></span>\n </div>\n <div *ngFor=\"let row of keyTypeValueRows; let i = index; trackBy: trackByKeyTypeValue\" [formGroup]=\"row\"\n class=\"cqa-api-edit-step-key-type-value-row\">\n <cqa-custom-input [value]=\"row.get('key')?.value ?? ''\"\n (valueChange)=\"row.get('key')?.setValue($event)\" placeholder=\"Key\" [fullWidth]=\"true\" size=\"sm\"\n class=\"cqa-api-edit-step-key-type-value-input\" ariaLabel=\"Key\">\n </cqa-custom-input>\n <cqa-dynamic-select [form]=\"row\" [config]=\"keyTypeValueTypeSelectConfig\"\n class=\"cqa-api-edit-step-key-type-value-type-select cqa-w-full\">\n </cqa-dynamic-select>\n <cqa-custom-input [value]=\"row.get('value')?.value ?? ''\"\n (valueChange)=\"row.get('value')?.setValue($event)\" placeholder=\"Value\" [fullWidth]=\"true\" size=\"sm\"\n class=\"cqa-api-edit-step-key-type-value-input\" ariaLabel=\"Value\">\n </cqa-custom-input>\n <cqa-button type=\"button\" variant=\"text\" [customClass]=\"'cqa-api-edit-step-key-type-value-delete'\"\n [tooltip]=\"'Remove row'\" (clicked)=\"removeKeyTypeValueRow(i)\">\n <svg class=\"cqa-api-edit-step-key-type-value-delete-icon\" width=\"16\" height=\"16\" viewBox=\"0 0 16 16\"\n fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\">\n <path\n d=\"M10.6663 6V12.6667H5.33301V6H10.6663ZM9.66634 2H6.33301L5.66634 2.66667H3.33301V4H12.6663V2.66667H10.333L9.66634 2ZM11.9997 4.66667H3.99967V12.6667C3.99967 13.4 4.59967 14 5.33301 14H10.6663C11.3997 14 11.9997 13.4 11.9997 12.6667V4.66667Z\"\n fill=\"#F9BFBF\" />\n </svg>\n </cqa-button>\n </div>\n <div class=\"cqa-api-edit-step-key-type-value-add-wrap\">\n <cqa-button type=\"button\" variant=\"filled\" btnSize=\"lg\" text=\"+ Add Row\"\n customClass=\"cqa-api-edit-step-key-type-value-add-btn\" (clicked)=\"addKeyTypeValueRow()\">\n </cqa-button>\n </div>\n </div>\n </div>\n\n <!-- Payload content (GraphQL): Query + Variables + Operation Name. Active only in GraphQL mode. -->\n <div *ngIf=\"activePayloadTab === 'graphql' && activeApiType === 'graphql' && graphQLForm\"\n class=\"cqa-api-edit-step-payload-graphql cqa-api-edit-step-graphql-tab cqa-flex cqa-flex-col cqa-gap-4\">\n <!--\n Query / Variables use the design-system `cqa-custom-textarea` so\n styling, label, error rendering, and accessibility come from the\n primitive. We bind via [value] / (valueChange) instead of\n formControlName because cqa-custom-textarea isn't a CVA \u2014 calling\n form.get('\u2026').setValue still fires the existing valueChanges\n subscriptions (which clear inline errors and recompute the\n multi-operation warning).\n -->\n <cqa-custom-textarea\n label=\"Query\"\n [required]=\"true\"\n [value]=\"graphQLForm.get('query')?.value || ''\"\n (valueChange)=\"graphQLForm.get('query')?.setValue($event)\"\n placeholder=\"query GetX($id: ID!) {&#10; x(id: $id) { name }&#10;}\"\n ariaLabel=\"GraphQL query\"\n [errors]=\"graphQLQueryError ? [graphQLQueryError] : []\"\n [fullWidth]=\"true\"\n [rows]=\"4\"\n resize=\"vertical\"\n size=\"md\">\n </cqa-custom-textarea>\n <div class=\"cqa-flex cqa-gap-1.5\">\n <cqa-custom-textarea\n class=\"cqa-flex-1\"\n label=\"Variables (JSON)\"\n [value]=\"graphQLForm.get('variables')?.value || ''\"\n (valueChange)=\"graphQLForm.get('variables')?.setValue($event)\"\n placeholder='{ \"id\": \"abc\" }'\n ariaLabel=\"GraphQL variables\"\n [errors]=\"graphQLVariablesError ? [graphQLVariablesError] : []\"\n [fullWidth]=\"true\"\n [rows]=\"3\"\n resize=\"vertical\"\n size=\"md\">\n </cqa-custom-textarea>\n <!-- Operation Name: optional unless 2+ operations are declared. -->\n <div class=\"cqa-api-edit-step-graphql-field cqa-flex cqa-flex-col cqa-gap-1.5\">\n <label class=\"cqa-api-edit-step-graphql-label cqa-m-0 cqa-text-[14px] cqa-font-semibold cqa-text-[#374151]\">Operation Name</label>\n <cqa-custom-input\n [value]=\"graphQLForm.get('operationName')?.value || ''\"\n (valueChange)=\"graphQLForm.get('operationName')?.setValue($event)\"\n [label]=\"''\" placeholder=\"e.g. GetBooking\" [fullWidth]=\"true\" size=\"md\"\n ariaLabel=\"GraphQL operation name\">\n </cqa-custom-input>\n <p *ngIf=\"graphQLOperationWarning\" class=\"cqa-text-[#B45309] cqa-text-[12px] cqa-mt-1\">\n {{ graphQLOperationWarning }}\n </p>\n <div class=\"cqa-flex cqa-flex-row cqa-items-center cqa-gap-1.5 cqa-text-[12px] cqa-p-2\" style=\"border-radius: 8px; border: 1px solid #EDE9FE; background: #F5F3FF;\">\n <div>\n <mat-icon style=\"display:flex;font-size:14px;font-weight: 600;\" class=\"cqa-flex cqa-items-center cqa-justify-center cqa-text-[14px] cqa-text-[#7C3AED]\" >lightbulb</mat-icon>\n </div>\n <div class=\"cqa-text-[#7C3AED]\">\n Use <span style=\"background-color: white; color:black; border-radius: 4px; padding: 2px;\">var</span> in variables to bind from earlier steps.\n </div>\n </div>\n </div>\n </div>\n </div>\n </div>\n </ng-container>\n\n <!-- Advanced settings (step-level). Hidden while the GraphQL picker is open so it doesn't compete for focus with the operation list. -->\n <div *ngIf=\"bodyView !== 'graphql-schema'\">\n <button\n type=\"button\"\n class=\"cqa-flex cqa-items-center cqa-justify-between cqa-w-full cqa-text-left cqa-text-[12px] cqa-font-medium cqa-text-gray-700 cqa-py-2 cqa-border-b cqa-border-gray-200\"\n (click)=\"advancedExpanded = !advancedExpanded\">\n <span class=\"cqa-text-[10px]\">Advanced</span>\n <mat-icon class=\"cqa-text-lg cqa-transition-transform\" [class.cqa-rotate-180]=\"advancedExpanded\">\n expand_more\n </mat-icon>\n </button>\n <div *ngIf=\"advancedExpanded\" class=\"cqa-mt-2\">\n <cqa-advanced-variables-form\n [advancedVariables]=\"advancedSettingsVariables\"\n [advancedVariableForm]=\"advancedVariablesForm\"\n (variableBooleanChange)=\"onAdvancedVariableBooleanChange($event.name, $event.value)\"\n (variableValueChange)=\"onAdvancedVariableValueChange($event.name, $event.value)\">\n </cqa-advanced-variables-form>\n </div>\n </div>\n\n </div>\n <!-- Step 2: Variable Name: input, validation, Back / Next -->\n <div *ngIf=\"currentStep === 2\" class=\"cqa-api-edit-step-panel cqa-flex cqa-flex-col cqa-gap-6 cqa-box-border cqa-w-full\">\n <div class=\"cqa-api-edit-step-variable-section\">\n <div class=\"cqa-api-edit-step-variable-input-wrap\">\n <cqa-custom-input [(value)]=\"variableName\" [label]=\"''\" placeholder=\"Variable Name\" [fullWidth]=\"true\"\n size=\"md\" (valueChange)=\"onVariableNameChange()\" aria-label=\"Variable Name\">\n </cqa-custom-input>\n </div>\n <p *ngIf=\"variableNameError\" class=\"cqa-api-edit-step-variable-error\" role=\"alert\">{{ variableNameError }}</p>\n </div>\n </div>\n <!-- Step 3: Response Body / Status tabs -->\n <div *ngIf=\"currentStep === 3\" class=\"cqa-api-edit-step-panel cqa-flex cqa-flex-col cqa-gap-6 cqa-box-border cqa-w-full\">\n <div class=\"cqa-api-edit-step-tabs-wrapper cqa-flex cqa-flex-col cqa-gap-3\">\n <div class=\"cqa-api-edit-step-tabs\">\n <cqa-button *ngFor=\"let tab of responseVerificationTabs\" type=\"button\" variant=\"text\" [text]=\"tab.label\"\n [customClass]=\"'cqa-api-edit-step-tab' + (activeResponseVerificationTab === tab.value ? ' cqa-api-edit-step-tab--active' : '')\"\n (clicked)=\"setResponseVerificationTab(tab.value)\">\n </cqa-button>\n </div>\n\n <!-- Response Body tab content: verification grid -->\n <div *ngIf=\"activeResponseVerificationTab === 'response-body'\" class=\"cqa-api-edit-step-step3-content\">\n <div class=\"cqa-api-edit-step-verification-header-row cqa-flex cqa-flex-row cqa-justify-end cqa-items-center cqa-pt-0 cqa-pr-0 cqa-pb-2 cqa-pl-0\">\n <span></span>\n <cqa-button type=\"button\" variant=\"text\" text=\"Add Verification\"\n customClass=\"cqa-api-edit-step-verification-add-link\" (clicked)=\"addVerificationRow()\">\n </cqa-button>\n </div>\n <div class=\"cqa-api-edit-step-verification\">\n <div class=\"cqa-api-edit-step-verification-grid cqa-api-edit-step-verification-grid-header\">\n <span class=\"cqa-api-edit-step-verification-label\">S.no</span>\n <span class=\"cqa-api-edit-step-verification-label\">JSON Path</span>\n <span class=\"cqa-api-edit-step-verification-label\">Verification</span>\n <span class=\"cqa-api-edit-step-verification-label\">Data Type</span>\n <span class=\"cqa-api-edit-step-verification-label\">Expected Value</span>\n <span class=\"cqa-api-edit-step-verification-label cqa-api-edit-step-verification-label--empty\"\n aria-hidden=\"true\"></span>\n </div>\n <div *ngFor=\"let row of verificationRows; let i = index; trackBy: trackByVerification\" [formGroup]=\"row\"\n class=\"cqa-api-edit-step-verification-row\">\n <span class=\"cqa-api-edit-step-verification-sno\">{{ i + 1 }}</span>\n <cqa-custom-input [value]=\"row.get('jsonPath')?.value ?? ''\"\n (valueChange)=\"row.get('jsonPath')?.setValue($event)\" placeholder=\"Json Path\" [fullWidth]=\"true\"\n size=\"sm\" class=\"cqa-api-edit-step-verification-input\" ariaLabel=\"JSON Path\">\n </cqa-custom-input>\n <cqa-dynamic-select [form]=\"row\" [config]=\"verificationSelectConfig\"\n class=\"cqa-api-edit-step-verification-select cqa-w-full\">\n </cqa-dynamic-select>\n <cqa-dynamic-select [form]=\"row\" [config]=\"verificationDataTypeSelectConfig\"\n class=\"cqa-api-edit-step-verification-select cqa-w-full\"\n (selectionChange)=\"onVerificationDataTypeChange(row, $event)\">\n </cqa-dynamic-select>\n <!-- Expected Value: text for String/Array/Object, number for Number, dropdown for Boolean -->\n <ng-container [ngSwitch]=\"row.get('dataType')?.value\">\n <cqa-dynamic-select *ngSwitchCase=\"'boolean'\" [form]=\"row\"\n [config]=\"verificationExpectedValueBooleanSelectConfig\"\n class=\"cqa-api-edit-step-verification-select cqa-api-edit-step-verification-expected-select cqa-w-full\">\n </cqa-dynamic-select>\n <cqa-custom-input *ngSwitchCase=\"'number'\" [value]=\"row.get('expectedValue')?.value ?? ''\"\n (valueChange)=\"row.get('expectedValue')?.setValue($event)\" placeholder=\"Expected Value (number)\"\n type=\"number\" [fullWidth]=\"true\" size=\"sm\"\n class=\"cqa-api-edit-step-verification-input cqa-api-edit-step-verification-expected-number\"\n ariaLabel=\"Expected Value\">\n </cqa-custom-input>\n <cqa-custom-input *ngSwitchDefault [value]=\"row.get('expectedValue')?.value ?? ''\"\n (valueChange)=\"row.get('expectedValue')?.setValue($event)\"\n placeholder=\"Expected Value in String\" [fullWidth]=\"true\" size=\"sm\"\n class=\"cqa-api-edit-step-verification-input\" ariaLabel=\"Expected Value\">\n </cqa-custom-input>\n </ng-container>\n <cqa-button type=\"button\" variant=\"text\" [customClass]=\"'cqa-api-edit-step-verification-delete'\"\n [tooltip]=\"'Remove row'\" (clicked)=\"removeVerificationRow(i)\">\n <svg class=\"cqa-api-edit-step-verification-delete-icon\" width=\"16\" height=\"16\" viewBox=\"0 0 16 16\"\n fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\">\n <path\n d=\"M10.6663 6V12.6667H5.33301V6H10.6663ZM9.66634 2H6.33301L5.66634 2.66667H3.33301V4H12.6663V2.66667H10.333L9.66634 2ZM11.9997 4.66667H3.99967V12.6667C3.99967 13.4 4.59967 14 5.33301 14H10.6663C11.3997 14 11.9997 13.4 11.9997 12.6667V4.66667Z\"\n fill=\"#F9BFBF\" />\n </svg>\n </cqa-button>\n </div>\n </div>\n </div>\n\n <!-- Status tab content: S.no, Verification dropdown, Expected Value, add/remove rows -->\n <div *ngIf=\"activeResponseVerificationTab === 'status'\" class=\"cqa-api-edit-step-step3-content\">\n <div class=\"cqa-api-edit-step-verification-header-row cqa-flex cqa-flex-row cqa-justify-end cqa-items-center cqa-pt-0 cqa-pr-0 cqa-pb-2 cqa-pl-0\">\n <span></span>\n <cqa-button type=\"button\" variant=\"text\" text=\"Add Verification\"\n customClass=\"cqa-api-edit-step-verification-add-link\" (clicked)=\"addStatusVerificationRow()\">\n </cqa-button>\n </div>\n <div class=\"cqa-api-edit-step-status-verification\">\n <div class=\"cqa-api-edit-step-status-verification-header\">\n <span class=\"cqa-api-edit-step-verification-label\">S.no</span>\n <span class=\"cqa-api-edit-step-verification-label\">Verification</span>\n <span class=\"cqa-api-edit-step-verification-label\">Expected Value</span>\n <span class=\"cqa-api-edit-step-verification-label cqa-api-edit-step-verification-label--empty\"\n aria-hidden=\"true\"></span>\n </div>\n <div *ngFor=\"let row of statusVerificationRows; let i = index; trackBy: trackByStatusVerification\"\n [formGroup]=\"row\" class=\"cqa-api-edit-step-status-verification-row\">\n <span class=\"cqa-api-edit-step-verification-sno\">{{ i + 1 }}</span>\n <cqa-dynamic-select [form]=\"row\" [config]=\"statusVerificationSelectConfig\"\n class=\"cqa-api-edit-step-status-verification-select cqa-w-full\">\n </cqa-dynamic-select>\n <cqa-custom-input [value]=\"row.get('expectedValue')?.value ?? ''\"\n (valueChange)=\"row.get('expectedValue')?.setValue($event)\" placeholder=\"Expected Value\"\n type=\"number\" [fullWidth]=\"true\" size=\"sm\" class=\"cqa-api-edit-step-verification-input\"\n ariaLabel=\"Expected Value\">\n </cqa-custom-input>\n <cqa-button type=\"button\" variant=\"text\" [customClass]=\"'cqa-api-edit-step-verification-delete'\"\n [tooltip]=\"'Remove row'\" (clicked)=\"removeStatusVerificationRow(i)\">\n <svg class=\"cqa-api-edit-step-verification-delete-icon\" width=\"16\" height=\"16\" viewBox=\"0 0 16 16\"\n fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\">\n <path\n d=\"M10.6663 6V12.6667H5.33301V6H10.6663ZM9.66634 2H6.33301L5.66634 2.66667H3.33301V4H12.6663V2.66667H10.333L9.66634 2ZM11.9997 4.66667H3.99967V12.6667C3.99967 13.4 4.59967 14 5.33301 14H10.6663C11.3997 14 11.9997 13.4 11.9997 12.6667V4.66667Z\"\n fill=\"#F9BFBF\" />\n </svg>\n </cqa-button>\n </div>\n </div>\n </div>\n </div>\n </div>\n </div>\n\n <!-- Response Preview. Hidden while the GraphQL picker is open \u2014 the user is in \"pick an operation\" mode, not \"view last response\" mode, so showing a stale preview below the picker is just noise. -->\n <div *ngIf=\"bodyView !== 'graphql-schema'\" #responsePreviewRef class=\"cqa-api-edit-step-response\">\n <h3 class=\"cqa-api-edit-step-response-title cqa-text-[14px] cqa-font-bold cqa-leading-[20px] cqa-text-[#111827] cqa-m-0 cqa-mb-[12px] cqa-shrink-0\">Response Preview</h3>\n <pre class=\"cqa-api-edit-step-response-content\">{{ responsePreview }}</pre>\n </div>\n\n <!-- Step actions: one row, full width. Step 1: Cancel + Next; Step 2: Back + Next; Step 3: Back + Create -->\n <div *ngIf=\"bodyView !== 'graphql-schema'\" class=\"cqa-api-edit-step-actions\">\n <ng-container [ngSwitch]=\"currentStep\">\n <ng-container *ngSwitchCase=\"1\">\n <cqa-button type=\"button\" variant=\"outlined\" btnSize=\"lg\" text=\"Cancel\"\n customClass=\"cqa-api-edit-step-actions-btn-cancel\" (clicked)=\"onCancel()\">\n </cqa-button>\n <cqa-button type=\"button\" variant=\"filled\" btnSize=\"lg\" text=\"Next\"\n customClass=\"cqa-api-edit-step-actions-btn-next\" (clicked)=\"onNext()\">\n </cqa-button>\n </ng-container>\n <ng-container *ngSwitchCase=\"2\">\n <cqa-button type=\"button\" variant=\"outlined\" btnSize=\"lg\" text=\"Back\"\n customClass=\"cqa-api-edit-step-actions-btn-back\" (clicked)=\"onBack()\">\n </cqa-button>\n <cqa-button type=\"button\" variant=\"filled\" btnSize=\"lg\" text=\"Next\"\n customClass=\"cqa-api-edit-step-actions-btn-next\" (clicked)=\"onNext()\">\n </cqa-button>\n </ng-container>\n <ng-container *ngSwitchCase=\"3\">\n <cqa-button type=\"button\" variant=\"outlined\" btnSize=\"lg\" text=\"Back\"\n customClass=\"cqa-api-edit-step-actions-btn-back\" (clicked)=\"onBack()\">\n </cqa-button>\n <cqa-button *ngIf=\"!isCreatingStep\" type=\"button\" variant=\"filled\" btnSize=\"lg\" [text]=\"editMode ? 'Update' : 'Create'\"\n customClass=\"cqa-api-edit-step-actions-btn-create\" (clicked)=\"onCreate()\">\n </cqa-button>\n <!-- Match cqa-button flex: actions row styles > cqa-button { flex: 1 1 0 } \u2014 loader div must grow same width as Create -->\n <div *ngIf=\"isCreatingStep\" class=\"cqa-api-edit-step-actions-loader cqa-w-full cqa-min-h-[38px] cqa-rounded-[8px] cqa-flex cqa-items-center cqa-justify-center\">\n <cqa-badge label=\"Creating\u2026\" icon=\"autorenew\" [isLoading]=\"true\" [fullWidth]=\"true\" [centerContent]=\"true\"\n [inlineStyles]=\"'min-height: 38px; height: 38px; width: 100%; box-sizing: border-box; display: flex; align-items: center; justify-content: center;'\"\n variant=\"info\" size=\"medium\"></cqa-badge>\n </div>\n </ng-container>\n </ng-container>\n </div>\n</div>" }]
39409
+ }], ctorParameters: function () { return [{ type: i1$1.FormBuilder }, { type: i0.ChangeDetectorRef }, { type: DialogService }]; }, propDecorators: { initialMethod: [{
38783
39410
  type: Input
38784
39411
  }], initialEnvironment: [{
38785
39412
  type: Input
@@ -38811,6 +39438,16 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.4.0", ngImpor
38811
39438
  type: Input
38812
39439
  }], isCreatingStep: [{
38813
39440
  type: Input
39441
+ }], initialApiType: [{
39442
+ type: Input
39443
+ }], initialGraphQL: [{
39444
+ type: Input
39445
+ }], graphQLSchemaSource: [{
39446
+ type: Input
39447
+ }], graphQLOperations: [{
39448
+ type: Input
39449
+ }], isLoadingGraphQLSchema: [{
39450
+ type: Input
38814
39451
  }], importCurl: [{
38815
39452
  type: Output
38816
39453
  }], importCurlCancel: [{
@@ -38843,10 +39480,14 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.4.0", ngImpor
38843
39480
  type: Output
38844
39481
  }], environmentLoadMore: [{
38845
39482
  type: Output
39483
+ }], loadSchema: [{
39484
+ type: Output
38846
39485
  }], authTypeOptions: [{
38847
39486
  type: Input
38848
39487
  }], initialAuthType: [{
38849
39488
  type: Input
39489
+ }], initialOAuth2: [{
39490
+ type: Input
38850
39491
  }], formatOptions: [{
38851
39492
  type: Input
38852
39493
  }], initialFormat: [{
@@ -53840,6 +54481,7 @@ UiKitModule.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "12.0.0", version: "1
53840
54481
  DeleteStepsComponent,
53841
54482
  RunExecutionAlertComponent,
53842
54483
  ApiEditStepComponent,
54484
+ GraphQLOperationPickerComponent,
53843
54485
  LiveConversationComponent,
53844
54486
  StepBuilderActionComponent,
53845
54487
  StepBuilderLoopComponent,
@@ -54036,6 +54678,7 @@ UiKitModule.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "12.0.0", version: "1
54036
54678
  DeleteStepsComponent,
54037
54679
  RunExecutionAlertComponent,
54038
54680
  ApiEditStepComponent,
54681
+ GraphQLOperationPickerComponent,
54039
54682
  LiveConversationComponent,
54040
54683
  StepBuilderActionComponent,
54041
54684
  StepBuilderLoopComponent,
@@ -54279,6 +54922,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.4.0", ngImpor
54279
54922
  DeleteStepsComponent,
54280
54923
  RunExecutionAlertComponent,
54281
54924
  ApiEditStepComponent,
54925
+ GraphQLOperationPickerComponent,
54282
54926
  LiveConversationComponent,
54283
54927
  StepBuilderActionComponent,
54284
54928
  StepBuilderLoopComponent,
@@ -54481,6 +55125,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.4.0", ngImpor
54481
55125
  DeleteStepsComponent,
54482
55126
  RunExecutionAlertComponent,
54483
55127
  ApiEditStepComponent,
55128
+ GraphQLOperationPickerComponent,
54484
55129
  LiveConversationComponent,
54485
55130
  StepBuilderActionComponent,
54486
55131
  StepBuilderLoopComponent,
@@ -55234,5 +55879,5 @@ function buildTestCaseDetailsFromApi(data, options) {
55234
55879
  * Generated bundle index. Do not edit.
55235
55880
  */
55236
55881
 
55237
- export { ADVANCED_SUBFIELDS_BY_TYPE, ADVANCED_TOGGLE_KEYS, AIActionStepComponent, AIAgentStepComponent, ALL_FILTER_VALUE, API_EDIT_STEP_LABELS, ActionMenuButtonComponent, AddPrerequisiteCasesSectionComponent, AdvancedVariablesFormComponent, AiDebugAlertComponent, AiLogsWithReasoningComponent, AiPromptCardComponent, AiReasoningComponent, ApiEditStepComponent, ApiMockingCardComponent, ApiStepComponent, AssignEnvironmentsDialogComponent, AuditLogDrawerComponent, AuditLogDrawerService, AuditLogEntryCardComponent, AutocompleteComponent, BadgeComponent, BasicStepComponent, BreakpointsModalComponent, ButtonComponent, CUSTOM_EDIT_STEP_DATA, CUSTOM_EDIT_STEP_EDIT_IN_DEPTH, CUSTOM_EDIT_STEP_REF, CUSTOM_ELEMENT_POPUP_REF, CaptureVideoDialogComponent, ChangeHistoryComponent, ChartCardComponent, CodeEditorComponent, ColumnVisibilityComponent, CompareRunsComponent, ConditionBranchEditorComponent, ConditionDebugStepComponent, ConditionStepComponent, ConfigurationCardComponent, ConsoleAlertComponent, CoverageModuleCardComponent, CreateStepGroupComponent, CustomEditStepComponent, CustomEditStepRef, CustomEditStepService, CustomInputComponent, CustomTextareaComponent, CustomToggleComponent, DEFAULT_FOLDER_COLOR, DEFAULT_METADATA_COLOR, DEFAULT_MODULAR_CONFIG, DEFAULT_MODULAR_LABELS, DEFAULT_PRIORITY_COLOR_CONFIG, DEFAULT_REORDER_LABELS, DEFAULT_STATUS_COLOR_CONFIG, DEFAULT_TEST_SUITE_LABELS, DIALOG_DATA, DIALOG_REF, DashboardHeaderComponent, DaterangepickerComponent, DaterangepickerDirective, DbQueryExecutionItemComponent, DbVerificationStepComponent, DeleteFolderDialogComponent, DeleteStepsComponent, DetailDrawerComponent, DetailDrawerTabComponent, DetailDrawerTabContentDirective, DetailSidePanelComponent, DialogComponent, DialogRef, DialogService, DocumentVerificationStepComponent, DropdownButtonComponent, DynamicCellContainerDirective, DynamicCellTemplateDirective, DynamicFilterComponent, DynamicHeaderTemplateDirective, DynamicSelectFieldComponent, DynamicTableComponent, ELEMENT_POPUP_DATA, ELEMENT_POPUP_EDIT_IN_DEPTH, EMPTY_STATE_IMAGES, EMPTY_STATE_PRESETS, ENVIRONMENT_ACCENT_COLORS, ElementFormComponent, ElementListComponent, ElementPopupComponent, ElementPopupRef, ElementPopupService, EmptyStateComponent, ErrorModalComponent, ExecutionResultModalComponent, ExportCodeModalComponent, FOLDER_COLUMN_FIELD_ID, FOLDER_DRAG_MIME, FOLDER_NAME_MAX_LENGTH, FailedStepCardComponent, FailedStepComponent, FailedTestCasesCardComponent, FileDownloadStepComponent, FileUploadComponent, FolderDragDirective, FolderDropDirective, FolderSidebarComponent, FullTableLoaderComponent, HeatErrorMapCellComponent, InsightCardComponent, ItemListComponent, IterationsLoopComponent, JumpToStepModalComponent, LiveConversationComponent, LiveExecutionStepComponent, LoopStepComponent, MONACO_LANGUAGE_MAP, MainStepCollapseComponent, ManageColumnsDialogComponent, MetricsCardComponent, MixedVariableInputComponent, ModularTableTemplateComponent, MoveTestSuiteDialogComponent, MoveToFolderDialogComponent, NamePromptModalComponent, NetworkRequestComponent, NewDbConfigDialogComponent, NewEnvironmentDialogComponent, NewEnvironmentVariableDialogComponent, NewFolderDialogComponent, NewGlobalVariableDialogComponent, NewTestDataProfileDialogComponent, NewVersionHistoryDetailComponent, OtherButtonComponent, PRIORITY_COLORS, PaginationComponent, PermissionToggleComponent, ProgressIndicatorComponent, ProgressTextCardComponent, QuestionnaireListComponent, RESULT_COLORS, ROW_DRAG_MIME, RadioCardGroupComponent, RecordingBannerComponent, ReviewRecordedStepsModalComponent, RowDragDirective, RunExecutionAlertComponent, RunHistoryCardComponent, STATUS_COLORS, STEP_DETAILS_DRAWER_DATA, STEP_DETAILS_DRAWER_REF, STEP_DETAILS_FIELDS_BY_TYPE, STEP_DETAILS_FIELD_META, STEP_DETAILS_MODAL_DATA, STEP_DETAILS_MODAL_REF, SearchBarComponent, SegmentControlComponent, SelectedFiltersComponent, SelfHealAnalysisComponent, SessionChangesModalComponent, SessionRestorationDialogComponent, SimulatorComponent, StepBuilderActionComponent, StepBuilderAiAgentComponent, StepBuilderConditionComponent, StepBuilderCustomCodeComponent, StepBuilderDatabaseComponent, StepBuilderDocumentComponent, StepBuilderDocumentGenerationTemplateStepComponent, StepBuilderGroupComponent, StepBuilderLoopComponent, StepBuilderRecordStepComponent, StepDetailsDrawerComponent, StepDetailsDrawerRef, StepDetailsDrawerService, StepDetailsModalComponent, StepDetailsModalRef, StepDetailsModalService, StepGroupComponent, StepProgressCardComponent, StepRendererComponent, StepStatusCardComponent, StepTypes, StepperComponent, SubStepsConfirmationDialogComponent, TEST_CASE_DETAILS_FIELD_MAP, TEST_CASE_DETAILS_SELECT_KEYS, TEST_DATA_MODAL_DATA, TEST_DATA_MODAL_EDIT_IN_DEPTH, TEST_DATA_MODAL_REF, TableActionToolbarComponent, TableDataLoaderComponent, TableTemplateComponent, TailwindOverlayContainer, TemplateVariablesFormComponent, TestCaseAiAgentStepComponent, TestCaseAiVerifyStepComponent, TestCaseApiStepComponent, TestCaseConditionStepComponent, TestCaseCustomCodeStepComponent, TestCaseDatabaseStepComponent, TestCaseDetailsComponent, TestCaseDetailsEditComponent, TestCaseDetailsRendererComponent, TestCaseLinkCellComponent, TestCaseLoopStepComponent, TestCaseNormalStepComponent, TestCaseRestoreSessionStepComponent, TestCaseScreenshotStepComponent, TestCaseScrollStepComponent, TestCaseStepGroupComponent, TestCaseUploadStepComponent, TestCaseVerifyUrlStepComponent, TestDataModalComponent, TestDataModalRef, TestDataModalService, TestDistributionCardComponent, UiKitModule, UpdatedFailedStepComponent, VersionHistoryCompareComponent, VersionHistoryDetailComponent, VersionHistoryListComponent, VersionHistoryRestoreConfirmComponent, ViewCompareButtonComponent, ViewMoreFailedStepButtonComponent, ViewportSelectorComponent, VisualComparisonComponent, VisualDifferenceModalComponent, WorkspaceSelectorComponent, buildTestCaseDetailsFromApi, getDynamicFieldsFromLegacyConfig, getEmptyStatePreset, getMetadataColor, getMetadataValueStyle, getStepDetailsStepType, humanizeVariableKey, isAiAgentStepConfig, isAiVerifyStepConfig, isApiStepConfig, isConditionStepConfig, isCustomCodeStepConfig, isDatabaseStepConfig, isLoopStepConfig, isNormalStepConfig, isRestoreSessionStepConfig, isScreenshotStepConfig, isScrollStepConfig, isStepGroupConfig, isUploadStepConfig, isVerifyUrlStepConfig, mapApiVariablesToDynamicFields };
55882
+ export { ADVANCED_SUBFIELDS_BY_TYPE, ADVANCED_TOGGLE_KEYS, AIActionStepComponent, AIAgentStepComponent, ALL_FILTER_VALUE, API_EDIT_STEP_LABELS, ActionMenuButtonComponent, AddPrerequisiteCasesSectionComponent, AdvancedVariablesFormComponent, AiDebugAlertComponent, AiLogsWithReasoningComponent, AiPromptCardComponent, AiReasoningComponent, ApiEditStepComponent, ApiMockingCardComponent, ApiStepComponent, AssignEnvironmentsDialogComponent, AuditLogDrawerComponent, AuditLogDrawerService, AuditLogEntryCardComponent, AutocompleteComponent, BadgeComponent, BasicStepComponent, BreakpointsModalComponent, ButtonComponent, CUSTOM_EDIT_STEP_DATA, CUSTOM_EDIT_STEP_EDIT_IN_DEPTH, CUSTOM_EDIT_STEP_REF, CUSTOM_ELEMENT_POPUP_REF, CaptureVideoDialogComponent, ChangeHistoryComponent, ChartCardComponent, CodeEditorComponent, ColumnVisibilityComponent, CompareRunsComponent, ConditionBranchEditorComponent, ConditionDebugStepComponent, ConditionStepComponent, ConfigurationCardComponent, ConsoleAlertComponent, CoverageModuleCardComponent, CreateStepGroupComponent, CustomEditStepComponent, CustomEditStepRef, CustomEditStepService, CustomInputComponent, CustomTextareaComponent, CustomToggleComponent, DEFAULT_FOLDER_COLOR, DEFAULT_METADATA_COLOR, DEFAULT_MODULAR_CONFIG, DEFAULT_MODULAR_LABELS, DEFAULT_PRIORITY_COLOR_CONFIG, DEFAULT_REORDER_LABELS, DEFAULT_STATUS_COLOR_CONFIG, DEFAULT_TEST_SUITE_LABELS, DIALOG_DATA, DIALOG_REF, DashboardHeaderComponent, DaterangepickerComponent, DaterangepickerDirective, DbQueryExecutionItemComponent, DbVerificationStepComponent, DeleteFolderDialogComponent, DeleteStepsComponent, DetailDrawerComponent, DetailDrawerTabComponent, DetailDrawerTabContentDirective, DetailSidePanelComponent, DialogComponent, DialogRef, DialogService, DocumentVerificationStepComponent, DropdownButtonComponent, DynamicCellContainerDirective, DynamicCellTemplateDirective, DynamicFilterComponent, DynamicHeaderTemplateDirective, DynamicSelectFieldComponent, DynamicTableComponent, ELEMENT_POPUP_DATA, ELEMENT_POPUP_EDIT_IN_DEPTH, EMPTY_STATE_IMAGES, EMPTY_STATE_PRESETS, ENVIRONMENT_ACCENT_COLORS, ElementFormComponent, ElementListComponent, ElementPopupComponent, ElementPopupRef, ElementPopupService, EmptyStateComponent, ErrorModalComponent, ExecutionResultModalComponent, ExportCodeModalComponent, FOLDER_COLUMN_FIELD_ID, FOLDER_DRAG_MIME, FOLDER_NAME_MAX_LENGTH, FailedStepCardComponent, FailedStepComponent, FailedTestCasesCardComponent, FileDownloadStepComponent, FileUploadComponent, FolderDragDirective, FolderDropDirective, FolderSidebarComponent, FullTableLoaderComponent, GraphQLOperationPickerComponent, HeatErrorMapCellComponent, InsightCardComponent, ItemListComponent, IterationsLoopComponent, JumpToStepModalComponent, LiveConversationComponent, LiveExecutionStepComponent, LoopStepComponent, MONACO_LANGUAGE_MAP, MainStepCollapseComponent, ManageColumnsDialogComponent, MetricsCardComponent, MixedVariableInputComponent, ModularTableTemplateComponent, MoveTestSuiteDialogComponent, MoveToFolderDialogComponent, NamePromptModalComponent, NetworkRequestComponent, NewDbConfigDialogComponent, NewEnvironmentDialogComponent, NewEnvironmentVariableDialogComponent, NewFolderDialogComponent, NewGlobalVariableDialogComponent, NewTestDataProfileDialogComponent, NewVersionHistoryDetailComponent, OtherButtonComponent, PRIORITY_COLORS, PaginationComponent, PermissionToggleComponent, ProgressIndicatorComponent, ProgressTextCardComponent, QuestionnaireListComponent, RESULT_COLORS, ROW_DRAG_MIME, RadioCardGroupComponent, RecordingBannerComponent, ReviewRecordedStepsModalComponent, RowDragDirective, RunExecutionAlertComponent, RunHistoryCardComponent, STATUS_COLORS, STEP_DETAILS_DRAWER_DATA, STEP_DETAILS_DRAWER_REF, STEP_DETAILS_FIELDS_BY_TYPE, STEP_DETAILS_FIELD_META, STEP_DETAILS_MODAL_DATA, STEP_DETAILS_MODAL_REF, SearchBarComponent, SegmentControlComponent, SelectedFiltersComponent, SelfHealAnalysisComponent, SessionChangesModalComponent, SessionRestorationDialogComponent, SimulatorComponent, StepBuilderActionComponent, StepBuilderAiAgentComponent, StepBuilderConditionComponent, StepBuilderCustomCodeComponent, StepBuilderDatabaseComponent, StepBuilderDocumentComponent, StepBuilderDocumentGenerationTemplateStepComponent, StepBuilderGroupComponent, StepBuilderLoopComponent, StepBuilderRecordStepComponent, StepDetailsDrawerComponent, StepDetailsDrawerRef, StepDetailsDrawerService, StepDetailsModalComponent, StepDetailsModalRef, StepDetailsModalService, StepGroupComponent, StepProgressCardComponent, StepRendererComponent, StepStatusCardComponent, StepTypes, StepperComponent, SubStepsConfirmationDialogComponent, TEST_CASE_DETAILS_FIELD_MAP, TEST_CASE_DETAILS_SELECT_KEYS, TEST_DATA_MODAL_DATA, TEST_DATA_MODAL_EDIT_IN_DEPTH, TEST_DATA_MODAL_REF, TableActionToolbarComponent, TableDataLoaderComponent, TableTemplateComponent, TailwindOverlayContainer, TemplateVariablesFormComponent, TestCaseAiAgentStepComponent, TestCaseAiVerifyStepComponent, TestCaseApiStepComponent, TestCaseConditionStepComponent, TestCaseCustomCodeStepComponent, TestCaseDatabaseStepComponent, TestCaseDetailsComponent, TestCaseDetailsEditComponent, TestCaseDetailsRendererComponent, TestCaseLinkCellComponent, TestCaseLoopStepComponent, TestCaseNormalStepComponent, TestCaseRestoreSessionStepComponent, TestCaseScreenshotStepComponent, TestCaseScrollStepComponent, TestCaseStepGroupComponent, TestCaseUploadStepComponent, TestCaseVerifyUrlStepComponent, TestDataModalComponent, TestDataModalRef, TestDataModalService, TestDistributionCardComponent, UiKitModule, UpdatedFailedStepComponent, VersionHistoryCompareComponent, VersionHistoryDetailComponent, VersionHistoryListComponent, VersionHistoryRestoreConfirmComponent, ViewCompareButtonComponent, ViewMoreFailedStepButtonComponent, ViewportSelectorComponent, VisualComparisonComponent, VisualDifferenceModalComponent, WorkspaceSelectorComponent, buildTestCaseDetailsFromApi, getDynamicFieldsFromLegacyConfig, getEmptyStatePreset, getMetadataColor, getMetadataValueStyle, getStepDetailsStepType, humanizeVariableKey, isAiAgentStepConfig, isAiVerifyStepConfig, isApiStepConfig, isConditionStepConfig, isCustomCodeStepConfig, isDatabaseStepConfig, isLoopStepConfig, isNormalStepConfig, isRestoreSessionStepConfig, isScreenshotStepConfig, isScrollStepConfig, isStepGroupConfig, isUploadStepConfig, isVerifyUrlStepConfig, mapApiVariablesToDynamicFields };
55238
55883
  //# sourceMappingURL=cqa-lib-cqa-ui.mjs.map