cloud-ide-shared 1.0.32 → 1.0.34

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,13 +1,16 @@
1
1
  import * as i0 from '@angular/core';
2
- import { Component, InjectionToken, inject, Injectable, input, output, signal } from '@angular/core';
2
+ import { Component, InjectionToken, inject, Injectable, input, output, signal, DestroyRef, computed, effect } from '@angular/core';
3
3
  import { Router } from '@angular/router';
4
4
  import { Observable, BehaviorSubject, throwError } from 'rxjs';
5
- import { CideEleFileManagerService, CideEleButtonComponent, CideIconComponent, CideSpinnerComponent } from 'cloud-ide-element';
6
- import { cidePath, hostManagerRoutesUrl, commonRoutesUrl, generateStringFromObject, coreRoutesUrl } from 'cloud-ide-lms-model';
5
+ import { CideEleFileManagerService, CideEleButtonComponent, CideIconComponent, CideSpinnerComponent, TooltipDirective, NotificationService, CideSelectComponent } from 'cloud-ide-element';
6
+ import { cidePath, hostManagerRoutesUrl, commonRoutesUrl, generateStringFromObject, coreRoutesUrl, AcaClassPrgBranchListPayload, academicsRoutesUrl, MPrgTrmSection } from 'cloud-ide-lms-model';
7
7
  import { HttpClient } from '@angular/common/http';
8
8
  import { tap, catchError } from 'rxjs/operators';
9
9
  import * as i1 from '@angular/common';
10
- import { CommonModule } from '@angular/common';
10
+ import { CommonModule, NgClass } from '@angular/common';
11
+ import * as i2 from '@angular/forms';
12
+ import { Validators, ReactiveFormsModule } from '@angular/forms';
13
+ import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
11
14
 
12
15
  class CloudIdeShared {
13
16
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.7", ngImport: i0, type: CloudIdeShared, deps: [], target: i0.ɵɵFactoryTarget.Component });
@@ -158,6 +161,22 @@ const NATIONALITY_SERVICE_TOKEN = new InjectionToken('CideCoreNationalityService
158
161
  */
159
162
  const CLASS_PROGRAM_MASTER_SERVICE_TOKEN = new InjectionToken('CideCoreClassProgramMasterService');
160
163
 
164
+ /**
165
+ * Injection token for Program Term Section Service (CideLytProgramTermSectionService)
166
+ * Used to provide and inject program term section service implementations
167
+ * This enables dependency injection with interface-based contracts
168
+ *
169
+ * @example
170
+ * ```typescript
171
+ * // In app.config.ts
172
+ * { provide: PROGRAM_TERM_SECTION_SERVICE_TOKEN, useExisting: CideLytProgramTermSectionService }
173
+ *
174
+ * // In component
175
+ * private programTermSectionService = inject(PROGRAM_TERM_SECTION_SERVICE_TOKEN) as IProgramTermSectionService;
176
+ * ```
177
+ */
178
+ const PROGRAM_TERM_SECTION_SERVICE_TOKEN = new InjectionToken('CideLytProgramTermSectionService');
179
+
161
180
  /**
162
181
  * Shared Object ID Generation Service
163
182
  *
@@ -722,6 +741,37 @@ const FINANCIAL_YEAR_SERVICE_TOKEN = new InjectionToken('CideAccFinancialYearSer
722
741
  */
723
742
  const ACADEMIC_YEAR_SERVICE_TOKEN = new InjectionToken('CideLytAcademicYearService');
724
743
 
744
+ /**
745
+ * Injection token for Fee Structure Service (CideFeeFeeStructureService)
746
+ * Used to provide and inject fee structure service implementations
747
+ * This enables dependency injection with interface-based contracts
748
+ *
749
+ * @example
750
+ * ```typescript
751
+ * // In app.config.ts
752
+ * { provide: FEE_STRUCTURE_SERVICE_TOKEN, useExisting: CideFeeFeeStructureService }
753
+ *
754
+ * // In component
755
+ * private feeStructureService = inject(FEE_STRUCTURE_SERVICE_TOKEN);
756
+ * ```
757
+ */
758
+ const FEE_STRUCTURE_SERVICE_TOKEN = new InjectionToken('CideFeeFeeStructureService');
759
+ /**
760
+ * Injection token for Fee Payment Service (CideFeeFeePaymentService)
761
+ * Used to provide and inject fee payment service implementations
762
+ * This enables dependency injection with interface-based contracts
763
+ *
764
+ * @example
765
+ * ```typescript
766
+ * // In app.config.ts
767
+ * { provide: FEE_PAYMENT_SERVICE_TOKEN, useExisting: CideFeeFeePaymentService }
768
+ *
769
+ * // In component
770
+ * private feePaymentService = inject(FEE_PAYMENT_SERVICE_TOKEN);
771
+ * ```
772
+ */
773
+ const FEE_PAYMENT_SERVICE_TOKEN = new InjectionToken('CideFeeFeePaymentService');
774
+
725
775
  class CideSharedOrgStructureComponent {
726
776
  // Input parameters for configuration
727
777
  allowSwitching = input(true, ...(ngDevMode ? [{ debugName: "allowSwitching" }] : [])); // Allow entity switching (default: true)
@@ -870,7 +920,7 @@ class CideSharedOrgStructureComponent {
870
920
  });
871
921
  }
872
922
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.7", ngImport: i0, type: CideSharedOrgStructureComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
873
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.1.7", type: CideSharedOrgStructureComponent, isStandalone: true, selector: "cide-shared-org-structure", inputs: { allowSwitching: { classPropertyName: "allowSwitching", publicName: "allowSwitching", isSignal: true, isRequired: false, transformFunction: null }, showActions: { classPropertyName: "showActions", publicName: "showActions", isSignal: true, isRequired: false, transformFunction: null }, mode: { classPropertyName: "mode", publicName: "mode", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { entityClick: "entityClick", entitySelect: "entitySelect", entityView: "entityView" }, ngImport: i0, template: "<!-- Organization Structure Component -->\r\n<div class=\"tw-flex-1 tw-overflow-hidden\">\r\n @if (loading()) {\r\n <div class=\"tw-flex tw-items-center tw-justify-center tw-py-12\">\r\n <cide-ele-spinner size=\"md\"></cide-ele-spinner>\r\n <span class=\"tw-ml-3 tw-text-gray-600 tw-text-sm\">Loading organization structure...</span>\r\n </div>\r\n } @else if (error()) {\r\n <div class=\"tw-text-center tw-py-12\">\r\n <div class=\"tw-w-16 tw-h-16 tw-bg-red-100 tw-rounded-full tw-flex tw-items-center tw-justify-center tw-mx-auto tw-mb-4\">\r\n <cide-ele-icon class=\"tw-w-8 tw-h-8 tw-text-red-500\">error</cide-ele-icon>\r\n </div>\r\n <h3 class=\"tw-text-lg tw-font-medium tw-text-gray-900 tw-mb-2\">Error Loading Structure</h3>\r\n <p class=\"tw-text-gray-600 tw-mb-4\">{{ error() }}</p>\r\n <button cideEleButton variant=\"primary\" size=\"sm\" (click)=\"loadOrgStructure()\">\r\n <cide-ele-icon>refresh</cide-ele-icon>\r\n Try Again\r\n </button>\r\n </div>\r\n } @else if (orgStructure().length === 0) {\r\n <div class=\"tw-text-center tw-py-12\">\r\n <div class=\"tw-w-16 tw-h-16 tw-bg-gray-100 tw-rounded-full tw-flex tw-items-center tw-justify-center tw-mx-auto tw-mb-4\">\r\n <cide-ele-icon class=\"tw-w-8 tw-h-8 tw-text-gray-400\">account_tree</cide-ele-icon>\r\n </div>\r\n <h3 class=\"tw-text-lg tw-font-medium tw-text-gray-900 tw-mb-2\">No Entities Found</h3>\r\n <p class=\"tw-text-gray-600 tw-mb-4\">No entities are available to display in the organization structure.</p>\r\n @if (mode() === 'view') {\r\n <button cideEleButton variant=\"primary\" size=\"sm\" (click)=\"router.navigate(['/control-panel/entity-list'])\">\r\n <cide-ele-icon>add</cide-ele-icon>\r\n Create First Entity\r\n </button>\r\n }\r\n </div>\r\n } @else {\r\n <!-- Organization Chart Container -->\r\n <div class=\"tw-relative tw-min-h-screen tw-bg-gray-50 tw-py-8 tw-overflow-x-auto org-chart-container\">\r\n <!-- Background Grid Pattern -->\r\n <div class=\"tw-absolute tw-inset-0 tw-opacity-30\" style=\"background-image: radial-gradient(circle, #e5e7eb 1px, transparent 1px); background-size: 20px 20px;\"></div>\r\n \r\n <!-- Chart Content -->\r\n <div class=\"tw-relative tw-z-10 tw-min-w-max tw-px-4 org-chart-content\">\r\n @for (rootNode of orgStructure(); track rootNode._id) {\r\n <div class=\"tw-flex tw-flex-col tw-items-center tw-mb-12\">\r\n <!-- Render node recursively with unlimited depth -->\r\n <ng-container [ngTemplateOutlet]=\"entityNodeTemplate\" [ngTemplateOutletContext]=\"{ $implicit: rootNode, level: 0 }\"></ng-container>\r\n </div>\r\n }\r\n </div>\r\n </div>\r\n }\r\n</div>\r\n\r\n<!-- Entity Node Template (Recursive) -->\r\n<ng-template #entityNodeTemplate let-node let-level=\"level\">\r\n <div class=\"tw-flex tw-flex-col tw-items-center\">\r\n \r\n <!-- Entity Card -->\r\n <div class=\"tw-bg-white tw-shadow-lg tw-rounded-lg tw-p-4 tw-w-64 tw-max-w-72 tw-border-t-4 tw-transition-all tw-cursor-pointer hover:tw-shadow-xl hover:tw-scale-105 entity-card\"\r\n [class.level-0]=\"level === 0\"\r\n [class.level-1]=\"level === 1\"\r\n [class.level-2]=\"level === 2\"\r\n [class.level-3]=\"level === 3\"\r\n [class.level-4]=\"level === 4\"\r\n [class.level-5]=\"level === 5\"\r\n (click)=\"viewEntity(node._id)\">\r\n \r\n <!-- Card Content -->\r\n <div class=\"tw-flex tw-items-center tw-space-x-3\">\r\n <!-- Avatar -->\r\n <div class=\"tw-w-12 tw-h-12 tw-bg-purple-100 tw-rounded-full tw-flex tw-items-center tw-justify-center tw-flex-shrink-0\">\r\n <cide-ele-icon class=\"tw-w-6 tw-h-6 tw-text-purple-600\">person</cide-ele-icon>\r\n </div>\r\n \r\n <!-- Entity Info -->\r\n <div class=\"tw-flex-1 tw-min-w-0\">\r\n <h3 class=\"tw-text-sm tw-font-semibold tw-text-gray-900 tw-whitespace-nowrap tw-overflow-hidden tw-text-ellipsis\">{{ node.syen_name }}</h3>\r\n <p class=\"tw-text-xs tw-text-gray-600 tw-mt-1\">{{ node.syen_entity_code }}</p>\r\n <p class=\"tw-text-xs tw-text-gray-500 tw-mt-1\">{{ node.syen_entity_type_sygms }}</p>\r\n </div>\r\n </div>\r\n \r\n <!-- Action Icons -->\r\n <div class=\"tw-flex tw-items-center tw-justify-between tw-mt-3\">\r\n <div class=\"tw-flex tw-items-center tw-space-x-2\">\r\n <!-- Phone Icon -->\r\n <button class=\"tw-w-6 tw-h-6 tw-rounded-full tw-bg-gray-100 tw-flex tw-items-center tw-justify-center tw-text-gray-600 hover:tw-bg-gray-200 tw-transition-colors\">\r\n <cide-ele-icon class=\"tw-text-xs\">phone</cide-ele-icon>\r\n </button>\r\n \r\n <!-- Email Icon -->\r\n <button class=\"tw-w-6 tw-h-6 tw-rounded-full tw-bg-gray-100 tw-flex tw-items-center tw-justify-center tw-text-gray-600 hover:tw-bg-gray-200 tw-transition-colors\">\r\n <cide-ele-icon class=\"tw-text-xs\">email</cide-ele-icon>\r\n </button>\r\n \r\n <!-- LinkedIn Icon -->\r\n <button class=\"tw-w-6 tw-h-6 tw-rounded-full tw-bg-blue-100 tw-flex tw-items-center tw-justify-center tw-text-blue-600 hover:tw-bg-blue-200 tw-transition-colors\">\r\n <cide-ele-icon class=\"tw-text-xs\">attach_money</cide-ele-icon>\r\n </button>\r\n \r\n <!-- Document Icon -->\r\n <button class=\"tw-w-6 tw-h-6 tw-rounded-full tw-bg-gray-100 tw-flex tw-items-center tw-justify-center tw-text-gray-600 hover:tw-bg-gray-200 tw-transition-colors\">\r\n <cide-ele-icon class=\"tw-text-xs\">description</cide-ele-icon>\r\n </button>\r\n \r\n <!-- More Options Icon -->\r\n <button class=\"tw-w-6 tw-h-6 tw-rounded-full tw-bg-gray-100 tw-flex tw-items-center tw-justify-center tw-text-gray-600 hover:tw-bg-gray-200 tw-transition-colors\">\r\n <cide-ele-icon class=\"tw-text-xs\">more_horiz</cide-ele-icon>\r\n </button>\r\n </div>\r\n \r\n <!-- Status Badge -->\r\n <div class=\"tw-flex-shrink-0\">\r\n @if (node.syen_isactive) {\r\n <div class=\"tw-inline-flex tw-items-center tw-px-1.5 tw-py-0.5 tw-rounded-full tw-text-xs tw-font-medium tw-bg-green-100 tw-text-green-800\">\r\n <div class=\"tw-w-1.5 tw-h-1.5 tw-bg-green-500 tw-rounded-full tw-mr-1\"></div>\r\n Active\r\n </div>\r\n } @else {\r\n <div class=\"tw-inline-flex tw-items-center tw-px-1.5 tw-py-0.5 tw-rounded-full tw-text-xs tw-font-medium tw-bg-red-100 tw-text-red-800\">\r\n <div class=\"tw-w-1.5 tw-h-1.5 tw-bg-red-500 tw-rounded-full tw-mr-1\"></div>\r\n Inactive\r\n </div>\r\n }\r\n </div>\r\n </div>\r\n </div>\r\n \r\n <!-- Children Entities (Recursive) -->\r\n @if (node.children && node.children.length > 0) {\r\n <!-- Flexible Connection Lines -->\r\n <div class=\"tw-relative tw-flex tw-flex-col tw-items-center tw-mt-4\">\r\n <!-- Connection dot -->\r\n <div class=\"tw-absolute tw-left-1/2 tw-transform tw--translate-x-1/2\" style=\"top: -8px;\">\r\n <div class=\"tw-w-2 tw-h-2 tw-bg-purple-600 tw-rounded-full tw-border tw-border-white\"></div>\r\n </div>\r\n \r\n <!-- Flexible SVG Lines -->\r\n <div class=\"tw-relative tw-w-full tw-flex tw-justify-center flexible-connection-lines\">\r\n @if (node.children && node.children.length === 1) {\r\n <!-- Single child - simple vertical line -->\r\n <svg class=\"tw-absolute tw-left-1/2\" style=\"transform: translateX(-50%); width: 2px; height: 40px; top: 0;\">\r\n <line x1=\"1\" y1=\"0\" x2=\"1\" y2=\"40\" stroke=\"#9333ea\" stroke-width=\"2\" stroke-linecap=\"round\"/>\r\n </svg>\r\n }\r\n @else if (node.children && node.children.length > 1) {\r\n <!-- Multiple children - flexible design -->\r\n <svg class=\"tw-absolute tw-left-1/2\" style=\"transform: translateX(-50%); width: 100%; height: 40px; top: 0;\">\r\n <!-- Vertical line from parent -->\r\n <line x1=\"50%\" y1=\"0\" x2=\"50%\" y2=\"15\" stroke=\"#9333ea\" stroke-width=\"2\" stroke-linecap=\"round\"/>\r\n \r\n <!-- Horizontal line spanning children -->\r\n <line x1=\"20%\" y1=\"15\" x2=\"80%\" y2=\"15\" stroke=\"#9333ea\" stroke-width=\"2\" stroke-linecap=\"round\"/>\r\n \r\n <!-- Vertical lines to children -->\r\n @for (child of node.children; track child._id; let i = $index) {\r\n <line [attr.x1]=\"(20 + (60 * i) / (node.children.length - 1)) + '%'\" \r\n y1=\"15\" \r\n [attr.x2]=\"(20 + (60 * i) / (node.children.length - 1)) + '%'\" \r\n y2=\"35\" \r\n stroke=\"#9333ea\" \r\n stroke-width=\"2\" \r\n stroke-linecap=\"round\"/>\r\n <!-- Connection dot -->\r\n <circle [attr.cx]=\"(20 + (60 * i) / (node.children.length - 1)) + '%'\" cy=\"35\" r=\"2.5\" fill=\"#9333ea\" stroke=\"#fff\" stroke-width=\"1\"/>\r\n }\r\n </svg>\r\n }\r\n </div>\r\n \r\n <!-- Children Container - Flexible Layout -->\r\n <div class=\"tw-flex tw-flex-wrap tw-justify-center tw-gap-4 tw-w-full tw-max-w-6xl tw-px-4 children-container\">\r\n @for (child of node.children; track child._id) {\r\n <div class=\"tw-flex tw-flex-col tw-items-center tw-flex-shrink-0\">\r\n <!-- Child node -->\r\n <ng-container [ngTemplateOutlet]=\"entityNodeTemplate\" [ngTemplateOutletContext]=\"{ $implicit: child, level: level + 1 }\"></ng-container>\r\n </div>\r\n }\r\n </div>\r\n </div>\r\n }\r\n </div>\r\n</ng-template>\r\n", styles: [".org-chart-container{overflow-x:auto;overflow-y:visible;min-height:100vh}.org-chart-content{min-width:max-content;padding:2rem 1rem}.flexible-connection-lines{position:relative;width:100%;height:40px}.flexible-connection-lines svg{position:absolute;left:50%;transform:translate(-50%);width:100%;height:100%;top:0}.children-container{display:flex;flex-wrap:wrap;justify-content:center;gap:1rem;width:100%;max-width:80rem;padding:0 1rem}.children-container>div{flex-shrink:0;display:flex;flex-direction:column;align-items:center}.entity-card{width:16rem;max-width:18rem;transition:all .2s ease-in-out}.entity-card:hover{transform:translateY(-2px) scale(1.02);box-shadow:0 20px 25px -5px #0000001a,0 10px 10px -5px #0000000a}@media (max-width: 1400px){.children-container{max-width:70rem;gap:.875rem}.entity-card{width:15rem;max-width:17rem}}@media (max-width: 1200px){.children-container{max-width:60rem;gap:.75rem}.entity-card{width:14rem;max-width:16rem}}@media (max-width: 992px){.children-container{max-width:50rem;gap:.625rem}.entity-card{width:13rem;max-width:15rem}}@media (max-width: 768px){.children-container{max-width:40rem;gap:.5rem}.entity-card{width:12rem;max-width:14rem}.org-chart-content{padding:1.5rem .75rem}}@media (max-width: 576px){.children-container{max-width:30rem;gap:.375rem}.entity-card{width:11rem;max-width:13rem}.org-chart-content{padding:1rem .5rem}}.tw-overflow-y-auto::-webkit-scrollbar{width:6px}.tw-overflow-y-auto::-webkit-scrollbar-track{background:#f1f5f9;border-radius:3px}.tw-overflow-y-auto::-webkit-scrollbar-thumb{background:#cbd5e1;border-radius:3px}.tw-overflow-y-auto::-webkit-scrollbar-thumb:hover{background:#94a3b8}.tw-transition-shadow{transition:box-shadow .2s ease-in-out}.tw-shadow-lg:hover{box-shadow:0 20px 25px -5px #0000001a,0 10px 10px -5px #0000000a;transform:translateY(-2px);transition:all .2s ease-in-out}.tw-bg-purple-300,.tw-bg-purple-400{transition:all .3s ease-in-out}.connection-line-compact{display:block;overflow:visible}.connection-line-compact path,.connection-line-compact line{stroke-linecap:round;stroke-linejoin:round;transition:stroke-width .2s ease}.connection-line-compact:hover path,.connection-line-compact:hover line{stroke-width:3}.connection-line-compact circle{transition:all .2s ease}.connection-line-compact circle:hover{filter:drop-shadow(0 2px 4px rgba(147,51,234,.4))}.level-0{border-color:#fb923c!important}.level-1{border-color:#60a5fa!important}.level-2{border-color:#a78bfa!important}.level-3{border-color:#4ade80!important}.level-4{border-color:#818cf8!important}.level-5{border-color:#f472b6!important}@media (max-width: 1200px){.tw-max-w-7xl{max-width:100%}.tw-gap-6{gap:1rem}.tw-min-w-64{min-width:14rem}.tw-max-w-72{max-width:16rem}}@media (max-width: 768px){.tw-min-w-64{min-width:12rem}.tw-max-w-72{max-width:14rem}.tw-max-w-7xl{max-width:100%}.tw-gap-6{gap:.75rem}.tw-p-4{padding:.75rem}.tw-mb-4{margin-bottom:.75rem}.tw-space-x-3{gap:.5rem}.tw-space-x-2{gap:.375rem}.tw-w-12{width:2.5rem}.tw-h-12{height:2.5rem}}@media (max-width: 480px){.tw-min-w-72{min-width:12rem}.tw-max-w-80{max-width:14rem}.tw-p-4{padding:.5rem}.tw-space-x-3{gap:.375rem}.tw-space-x-2{gap:.25rem}.tw-mb-3{margin-bottom:.5rem}.tw-w-12{width:2rem}.tw-h-12{height:2rem}.tw-w-8{width:1.5rem}.tw-h-8{height:1.5rem}}@media print{.tw-bg-gray-50{background:#fff!important}.tw-shadow-lg{box-shadow:none!important;border:1px solid #e5e7eb!important}.tw-bg-purple-300{background:#6b7280!important}.tw-border-purple-300{border-color:#6b7280!important}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "component", type: CideEleButtonComponent, selector: "button[cideEleButton], a[cideEleButton]", inputs: ["label", "variant", "size", "type", "shape", "elevation", "disabled", "id", "loading", "fullWidth", "leftIcon", "rightIcon", "customClass", "tooltip", "ariaLabel", "testId", "routerLink", "routerExtras", "preventDoubleClick", "animated"], outputs: ["btnClick", "doubleClick"] }, { kind: "component", type: CideIconComponent, selector: "cide-ele-icon", inputs: ["size", "type", "toolTip"] }, { kind: "component", type: CideSpinnerComponent, selector: "cide-ele-spinner", inputs: ["size", "type"] }] });
923
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.1.7", type: CideSharedOrgStructureComponent, isStandalone: true, selector: "cide-shared-org-structure", inputs: { allowSwitching: { classPropertyName: "allowSwitching", publicName: "allowSwitching", isSignal: true, isRequired: false, transformFunction: null }, showActions: { classPropertyName: "showActions", publicName: "showActions", isSignal: true, isRequired: false, transformFunction: null }, mode: { classPropertyName: "mode", publicName: "mode", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { entityClick: "entityClick", entitySelect: "entitySelect", entityView: "entityView" }, ngImport: i0, template: "<!-- Organization Structure Component -->\n<div class=\"tw-flex-1 tw-overflow-hidden\">\n @if (loading()) {\n <div class=\"tw-flex tw-items-center tw-justify-center tw-py-12\">\n <cide-ele-spinner size=\"md\"></cide-ele-spinner>\n <span class=\"tw-ml-3 tw-text-gray-600 tw-text-sm\">Loading organization structure...</span>\n </div>\n } @else if (error()) {\n <div class=\"tw-text-center tw-py-12\">\n <div class=\"tw-w-16 tw-h-16 tw-bg-red-100 tw-rounded-full tw-flex tw-items-center tw-justify-center tw-mx-auto tw-mb-4\">\n <cide-ele-icon class=\"tw-w-8 tw-h-8 tw-text-red-500\">error</cide-ele-icon>\n </div>\n <h3 class=\"tw-text-lg tw-font-medium tw-text-gray-900 tw-mb-2\">Error Loading Structure</h3>\n <p class=\"tw-text-gray-600 tw-mb-4\">{{ error() }}</p>\n <button cideEleButton variant=\"primary\" size=\"sm\" (click)=\"loadOrgStructure()\">\n <cide-ele-icon>refresh</cide-ele-icon>\n Try Again\n </button>\n </div>\n } @else if (orgStructure().length === 0) {\n <div class=\"tw-text-center tw-py-12\">\n <div class=\"tw-w-16 tw-h-16 tw-bg-gray-100 tw-rounded-full tw-flex tw-items-center tw-justify-center tw-mx-auto tw-mb-4\">\n <cide-ele-icon class=\"tw-w-8 tw-h-8 tw-text-gray-400\">account_tree</cide-ele-icon>\n </div>\n <h3 class=\"tw-text-lg tw-font-medium tw-text-gray-900 tw-mb-2\">No Entities Found</h3>\n <p class=\"tw-text-gray-600 tw-mb-4\">No entities are available to display in the organization structure.</p>\n @if (mode() === 'view') {\n <button cideEleButton variant=\"primary\" size=\"sm\" (click)=\"router.navigate(['/control-panel/entity-list'])\">\n <cide-ele-icon>add</cide-ele-icon>\n Create First Entity\n </button>\n }\n </div>\n } @else {\n <!-- Organization Chart Container -->\n <div class=\"tw-relative tw-min-h-screen tw-bg-gray-50 tw-py-8 org-chart-container\">\n <!-- Chart Content with Background Pattern -->\n <div class=\"tw-relative tw-z-10 tw-min-w-max tw-px-4 org-chart-content org-chart-content-with-bg\">\n @for (rootNode of orgStructure(); track rootNode._id) {\n <div class=\"tw-flex tw-flex-col tw-items-center tw-mb-12\">\n <!-- Render node recursively with unlimited depth -->\n <ng-container [ngTemplateOutlet]=\"entityNodeTemplate\" [ngTemplateOutletContext]=\"{ $implicit: rootNode, level: 0 }\"></ng-container>\n </div>\n }\n </div>\n </div>\n }\n</div>\n\n<!-- Entity Node Template (Recursive) -->\n<ng-template #entityNodeTemplate let-node let-level=\"level\">\n <div class=\"tw-flex tw-flex-col tw-items-center\">\n \n <!-- Entity Card -->\n <div class=\"tw-bg-white tw-shadow-lg tw-rounded-lg tw-p-4 tw-w-64 tw-max-w-72 tw-border-t-4 tw-transition-all tw-cursor-pointer hover:tw-shadow-xl hover:tw-scale-105 entity-card\"\n [class.level-0]=\"level === 0\"\n [class.level-1]=\"level === 1\"\n [class.level-2]=\"level === 2\"\n [class.level-3]=\"level === 3\"\n [class.level-4]=\"level === 4\"\n [class.level-5]=\"level === 5\"\n (click)=\"viewEntity(node._id)\">\n \n <!-- Card Content -->\n <div class=\"tw-flex tw-items-center tw-space-x-3\">\n <!-- Avatar -->\n <div class=\"tw-w-12 tw-h-12 tw-bg-purple-100 tw-rounded-full tw-flex tw-items-center tw-justify-center tw-flex-shrink-0\">\n <cide-ele-icon class=\"tw-w-6 tw-h-6 tw-text-purple-600\">person</cide-ele-icon>\n </div>\n \n <!-- Entity Info -->\n <div class=\"tw-flex-1 tw-min-w-0\">\n <h3 class=\"tw-text-sm tw-font-semibold tw-text-gray-900 tw-whitespace-nowrap tw-overflow-hidden tw-text-ellipsis\" \n [cideEleTooltip]=\"node.syen_name || ''\" \n [tooltipPlacement]=\"'top'\"\n [tooltipType]=\"'default'\">{{ node.syen_name }}</h3>\n <p class=\"tw-text-xs tw-text-gray-600 tw-mt-1\">{{ node.syen_entity_code }}</p>\n <p class=\"tw-text-xs tw-text-gray-500 tw-mt-1\">{{ node.syen_entity_type_sygms }}</p>\n </div>\n </div>\n \n <!-- Action Icons -->\n <div class=\"tw-flex tw-items-center tw-justify-between tw-mt-3\">\n <div class=\"tw-flex tw-items-center tw-space-x-2\">\n <!-- Phone Icon -->\n <button class=\"tw-w-6 tw-h-6 tw-rounded-full tw-bg-gray-100 tw-flex tw-items-center tw-justify-center tw-text-gray-600 hover:tw-bg-gray-200 tw-transition-colors\">\n <cide-ele-icon class=\"tw-text-xs\">phone</cide-ele-icon>\n </button>\n \n <!-- Email Icon -->\n <button class=\"tw-w-6 tw-h-6 tw-rounded-full tw-bg-gray-100 tw-flex tw-items-center tw-justify-center tw-text-gray-600 hover:tw-bg-gray-200 tw-transition-colors\">\n <cide-ele-icon class=\"tw-text-xs\">email</cide-ele-icon>\n </button>\n \n <!-- LinkedIn Icon -->\n <button class=\"tw-w-6 tw-h-6 tw-rounded-full tw-bg-blue-100 tw-flex tw-items-center tw-justify-center tw-text-blue-600 hover:tw-bg-blue-200 tw-transition-colors\">\n <cide-ele-icon class=\"tw-text-xs\">attach_money</cide-ele-icon>\n </button>\n \n <!-- Document Icon -->\n <button class=\"tw-w-6 tw-h-6 tw-rounded-full tw-bg-gray-100 tw-flex tw-items-center tw-justify-center tw-text-gray-600 hover:tw-bg-gray-200 tw-transition-colors\">\n <cide-ele-icon class=\"tw-text-xs\">description</cide-ele-icon>\n </button>\n \n <!-- More Options Icon -->\n <button class=\"tw-w-6 tw-h-6 tw-rounded-full tw-bg-gray-100 tw-flex tw-items-center tw-justify-center tw-text-gray-600 hover:tw-bg-gray-200 tw-transition-colors\">\n <cide-ele-icon class=\"tw-text-xs\">more_horiz</cide-ele-icon>\n </button>\n </div>\n \n <!-- Status Badge -->\n <div class=\"tw-flex-shrink-0\">\n @if (node.syen_isactive) {\n <div class=\"tw-inline-flex tw-items-center tw-px-1.5 tw-py-0.5 tw-rounded-full tw-text-xs tw-font-medium tw-bg-green-100 tw-text-green-800\">\n <div class=\"tw-w-1.5 tw-h-1.5 tw-bg-green-500 tw-rounded-full tw-mr-1\"></div>\n Active\n </div>\n } @else {\n <div class=\"tw-inline-flex tw-items-center tw-px-1.5 tw-py-0.5 tw-rounded-full tw-text-xs tw-font-medium tw-bg-red-100 tw-text-red-800\">\n <div class=\"tw-w-1.5 tw-h-1.5 tw-bg-red-500 tw-rounded-full tw-mr-1\"></div>\n Inactive\n </div>\n }\n </div>\n </div>\n </div>\n \n <!-- Children Entities (Recursive) -->\n @if (node.children && node.children.length > 0) {\n <!-- Flexible Connection Lines -->\n <div class=\"tw-relative tw-flex tw-flex-col tw-items-center tw-mt-4\">\n <!-- Connection dot -->\n <div class=\"tw-absolute tw-left-1/2 tw-transform tw--translate-x-1/2\" style=\"top: -8px;\">\n <div class=\"tw-w-2 tw-h-2 tw-bg-purple-600 tw-rounded-full tw-border tw-border-white\"></div>\n </div>\n \n <!-- Flexible SVG Lines -->\n <div class=\"tw-relative tw-w-full tw-flex tw-justify-center flexible-connection-lines\">\n @if (node.children && node.children.length === 1) {\n <!-- Single child - simple vertical line -->\n <svg class=\"tw-absolute tw-left-1/2\" style=\"transform: translateX(-50%); width: 2px; height: 40px; top: 0;\">\n <line x1=\"1\" y1=\"0\" x2=\"1\" y2=\"40\" stroke=\"#9333ea\" stroke-width=\"2\" stroke-linecap=\"round\"/>\n </svg>\n }\n @else if (node.children && node.children.length > 1) {\n <!-- Multiple children - flexible design -->\n <svg class=\"tw-absolute tw-left-1/2\" style=\"transform: translateX(-50%); width: 100%; height: 40px; top: 0;\">\n <!-- Vertical line from parent -->\n <line x1=\"50%\" y1=\"0\" x2=\"50%\" y2=\"15\" stroke=\"#9333ea\" stroke-width=\"2\" stroke-linecap=\"round\"/>\n \n <!-- Horizontal line spanning children -->\n <line x1=\"20%\" y1=\"15\" x2=\"80%\" y2=\"15\" stroke=\"#9333ea\" stroke-width=\"2\" stroke-linecap=\"round\"/>\n \n <!-- Vertical lines to children -->\n @for (child of node.children; track child._id; let i = $index) {\n <line [attr.x1]=\"(20 + (60 * i) / (node.children.length - 1)) + '%'\" \n y1=\"15\" \n [attr.x2]=\"(20 + (60 * i) / (node.children.length - 1)) + '%'\" \n y2=\"35\" \n stroke=\"#9333ea\" \n stroke-width=\"2\" \n stroke-linecap=\"round\"/>\n <!-- Connection dot -->\n <circle [attr.cx]=\"(20 + (60 * i) / (node.children.length - 1)) + '%'\" cy=\"35\" r=\"2.5\" fill=\"#9333ea\" stroke=\"#fff\" stroke-width=\"1\"/>\n }\n </svg>\n }\n </div>\n \n <!-- Children Container - Flexible Layout -->\n <div class=\"tw-flex tw-flex-wrap tw-justify-center tw-gap-4 tw-w-full tw-max-w-6xl tw-px-4 children-container\">\n @for (child of node.children; track child._id) {\n <div class=\"tw-flex tw-flex-col tw-items-center tw-flex-shrink-0\">\n <!-- Child node -->\n <ng-container [ngTemplateOutlet]=\"entityNodeTemplate\" [ngTemplateOutletContext]=\"{ $implicit: child, level: level + 1 }\"></ng-container>\n </div>\n }\n </div>\n </div>\n }\n </div>\n</ng-template>\n", styles: [".org-chart-container{overflow-x:auto;overflow-y:auto;min-height:100vh;max-height:100vh;position:relative}.org-chart-content{min-width:max-content;min-height:max-content;padding:2rem 1rem}.org-chart-content-with-bg{position:relative}.org-chart-content-with-bg:before{content:\"\";position:absolute;inset:0;opacity:.3;pointer-events:none;background-image:radial-gradient(circle,#e5e7eb 1px,transparent 1px);background-size:20px 20px;background-repeat:repeat;z-index:-1}.flexible-connection-lines{position:relative;width:100%;height:40px}.flexible-connection-lines svg{position:absolute;left:50%;transform:translate(-50%);width:100%;height:100%;top:0}.children-container{display:flex;flex-wrap:wrap;justify-content:center;gap:1rem;width:100%;max-width:80rem;padding:0 1rem}.children-container>div{flex-shrink:0;display:flex;flex-direction:column;align-items:center}.entity-card{width:16rem;max-width:18rem;transition:all .2s ease-in-out}.entity-card:hover{transform:translateY(-2px) scale(1.02);box-shadow:0 20px 25px -5px #0000001a,0 10px 10px -5px #0000000a}@media (max-width: 1400px){.children-container{max-width:70rem;gap:.875rem}.entity-card{width:15rem;max-width:17rem}}@media (max-width: 1200px){.children-container{max-width:60rem;gap:.75rem}.entity-card{width:14rem;max-width:16rem}}@media (max-width: 992px){.children-container{max-width:50rem;gap:.625rem}.entity-card{width:13rem;max-width:15rem}}@media (max-width: 768px){.children-container{max-width:40rem;gap:.5rem}.entity-card{width:12rem;max-width:14rem}.org-chart-content{padding:1.5rem .75rem}}@media (max-width: 576px){.children-container{max-width:30rem;gap:.375rem}.entity-card{width:11rem;max-width:13rem}.org-chart-content{padding:1rem .5rem}}.tw-overflow-y-auto::-webkit-scrollbar{width:6px}.tw-overflow-y-auto::-webkit-scrollbar-track{background:#f1f5f9;border-radius:3px}.tw-overflow-y-auto::-webkit-scrollbar-thumb{background:#cbd5e1;border-radius:3px}.tw-overflow-y-auto::-webkit-scrollbar-thumb:hover{background:#94a3b8}.tw-transition-shadow{transition:box-shadow .2s ease-in-out}.tw-shadow-lg:hover{box-shadow:0 20px 25px -5px #0000001a,0 10px 10px -5px #0000000a;transform:translateY(-2px);transition:all .2s ease-in-out}.tw-bg-purple-300,.tw-bg-purple-400{transition:all .3s ease-in-out}.connection-line-compact{display:block;overflow:visible}.connection-line-compact path,.connection-line-compact line{stroke-linecap:round;stroke-linejoin:round;transition:stroke-width .2s ease}.connection-line-compact:hover path,.connection-line-compact:hover line{stroke-width:3}.connection-line-compact circle{transition:all .2s ease}.connection-line-compact circle:hover{filter:drop-shadow(0 2px 4px rgba(147,51,234,.4))}.level-0{border-color:#fb923c!important}.level-1{border-color:#60a5fa!important}.level-2{border-color:#a78bfa!important}.level-3{border-color:#4ade80!important}.level-4{border-color:#818cf8!important}.level-5{border-color:#f472b6!important}@media (max-width: 1200px){.tw-max-w-7xl{max-width:100%}.tw-gap-6{gap:1rem}.tw-min-w-64{min-width:14rem}.tw-max-w-72{max-width:16rem}}@media (max-width: 768px){.tw-min-w-64{min-width:12rem}.tw-max-w-72{max-width:14rem}.tw-max-w-7xl{max-width:100%}.tw-gap-6{gap:.75rem}.tw-p-4{padding:.75rem}.tw-mb-4{margin-bottom:.75rem}.tw-space-x-3{gap:.5rem}.tw-space-x-2{gap:.375rem}.tw-w-12{width:2.5rem}.tw-h-12{height:2.5rem}}@media (max-width: 480px){.tw-min-w-72{min-width:12rem}.tw-max-w-80{max-width:14rem}.tw-p-4{padding:.5rem}.tw-space-x-3{gap:.375rem}.tw-space-x-2{gap:.25rem}.tw-mb-3{margin-bottom:.5rem}.tw-w-12{width:2rem}.tw-h-12{height:2rem}.tw-w-8{width:1.5rem}.tw-h-8{height:1.5rem}}@media print{.tw-bg-gray-50{background:#fff!important}.tw-shadow-lg{box-shadow:none!important;border:1px solid #e5e7eb!important}.tw-bg-purple-300{background:#6b7280!important}.tw-border-purple-300{border-color:#6b7280!important}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "component", type: CideEleButtonComponent, selector: "button[cideEleButton], a[cideEleButton], cide-ele-button", inputs: ["label", "variant", "size", "type", "shape", "elevation", "disabled", "id", "loading", "fullWidth", "leftIcon", "rightIcon", "customClass", "tooltip", "ariaLabel", "testId", "routerLink", "routerExtras", "preventDoubleClick", "animated"], outputs: ["btnClick", "doubleClick"] }, { kind: "component", type: CideIconComponent, selector: "cide-ele-icon", inputs: ["size", "type", "toolTip"] }, { kind: "component", type: CideSpinnerComponent, selector: "cide-ele-spinner", inputs: ["size", "type"] }, { kind: "directive", type: TooltipDirective, selector: "[cideEleTooltip]", inputs: ["cideEleTooltip", "tooltipColor", "tooltipBg", "tooltipPlacement", "tooltipType", "tooltipDelay", "tooltipDir", "tooltipShowArrow", "tooltipMultiline", "tooltipMaxWidth", "tooltipInteractive", "tooltipClass"] }] });
874
924
  }
875
925
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.7", ngImport: i0, type: CideSharedOrgStructureComponent, decorators: [{
876
926
  type: Component,
@@ -878,10 +928,769 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.7", ngImpor
878
928
  CommonModule,
879
929
  CideEleButtonComponent,
880
930
  CideIconComponent,
881
- CideSpinnerComponent
882
- ], template: "<!-- Organization Structure Component -->\r\n<div class=\"tw-flex-1 tw-overflow-hidden\">\r\n @if (loading()) {\r\n <div class=\"tw-flex tw-items-center tw-justify-center tw-py-12\">\r\n <cide-ele-spinner size=\"md\"></cide-ele-spinner>\r\n <span class=\"tw-ml-3 tw-text-gray-600 tw-text-sm\">Loading organization structure...</span>\r\n </div>\r\n } @else if (error()) {\r\n <div class=\"tw-text-center tw-py-12\">\r\n <div class=\"tw-w-16 tw-h-16 tw-bg-red-100 tw-rounded-full tw-flex tw-items-center tw-justify-center tw-mx-auto tw-mb-4\">\r\n <cide-ele-icon class=\"tw-w-8 tw-h-8 tw-text-red-500\">error</cide-ele-icon>\r\n </div>\r\n <h3 class=\"tw-text-lg tw-font-medium tw-text-gray-900 tw-mb-2\">Error Loading Structure</h3>\r\n <p class=\"tw-text-gray-600 tw-mb-4\">{{ error() }}</p>\r\n <button cideEleButton variant=\"primary\" size=\"sm\" (click)=\"loadOrgStructure()\">\r\n <cide-ele-icon>refresh</cide-ele-icon>\r\n Try Again\r\n </button>\r\n </div>\r\n } @else if (orgStructure().length === 0) {\r\n <div class=\"tw-text-center tw-py-12\">\r\n <div class=\"tw-w-16 tw-h-16 tw-bg-gray-100 tw-rounded-full tw-flex tw-items-center tw-justify-center tw-mx-auto tw-mb-4\">\r\n <cide-ele-icon class=\"tw-w-8 tw-h-8 tw-text-gray-400\">account_tree</cide-ele-icon>\r\n </div>\r\n <h3 class=\"tw-text-lg tw-font-medium tw-text-gray-900 tw-mb-2\">No Entities Found</h3>\r\n <p class=\"tw-text-gray-600 tw-mb-4\">No entities are available to display in the organization structure.</p>\r\n @if (mode() === 'view') {\r\n <button cideEleButton variant=\"primary\" size=\"sm\" (click)=\"router.navigate(['/control-panel/entity-list'])\">\r\n <cide-ele-icon>add</cide-ele-icon>\r\n Create First Entity\r\n </button>\r\n }\r\n </div>\r\n } @else {\r\n <!-- Organization Chart Container -->\r\n <div class=\"tw-relative tw-min-h-screen tw-bg-gray-50 tw-py-8 tw-overflow-x-auto org-chart-container\">\r\n <!-- Background Grid Pattern -->\r\n <div class=\"tw-absolute tw-inset-0 tw-opacity-30\" style=\"background-image: radial-gradient(circle, #e5e7eb 1px, transparent 1px); background-size: 20px 20px;\"></div>\r\n \r\n <!-- Chart Content -->\r\n <div class=\"tw-relative tw-z-10 tw-min-w-max tw-px-4 org-chart-content\">\r\n @for (rootNode of orgStructure(); track rootNode._id) {\r\n <div class=\"tw-flex tw-flex-col tw-items-center tw-mb-12\">\r\n <!-- Render node recursively with unlimited depth -->\r\n <ng-container [ngTemplateOutlet]=\"entityNodeTemplate\" [ngTemplateOutletContext]=\"{ $implicit: rootNode, level: 0 }\"></ng-container>\r\n </div>\r\n }\r\n </div>\r\n </div>\r\n }\r\n</div>\r\n\r\n<!-- Entity Node Template (Recursive) -->\r\n<ng-template #entityNodeTemplate let-node let-level=\"level\">\r\n <div class=\"tw-flex tw-flex-col tw-items-center\">\r\n \r\n <!-- Entity Card -->\r\n <div class=\"tw-bg-white tw-shadow-lg tw-rounded-lg tw-p-4 tw-w-64 tw-max-w-72 tw-border-t-4 tw-transition-all tw-cursor-pointer hover:tw-shadow-xl hover:tw-scale-105 entity-card\"\r\n [class.level-0]=\"level === 0\"\r\n [class.level-1]=\"level === 1\"\r\n [class.level-2]=\"level === 2\"\r\n [class.level-3]=\"level === 3\"\r\n [class.level-4]=\"level === 4\"\r\n [class.level-5]=\"level === 5\"\r\n (click)=\"viewEntity(node._id)\">\r\n \r\n <!-- Card Content -->\r\n <div class=\"tw-flex tw-items-center tw-space-x-3\">\r\n <!-- Avatar -->\r\n <div class=\"tw-w-12 tw-h-12 tw-bg-purple-100 tw-rounded-full tw-flex tw-items-center tw-justify-center tw-flex-shrink-0\">\r\n <cide-ele-icon class=\"tw-w-6 tw-h-6 tw-text-purple-600\">person</cide-ele-icon>\r\n </div>\r\n \r\n <!-- Entity Info -->\r\n <div class=\"tw-flex-1 tw-min-w-0\">\r\n <h3 class=\"tw-text-sm tw-font-semibold tw-text-gray-900 tw-whitespace-nowrap tw-overflow-hidden tw-text-ellipsis\">{{ node.syen_name }}</h3>\r\n <p class=\"tw-text-xs tw-text-gray-600 tw-mt-1\">{{ node.syen_entity_code }}</p>\r\n <p class=\"tw-text-xs tw-text-gray-500 tw-mt-1\">{{ node.syen_entity_type_sygms }}</p>\r\n </div>\r\n </div>\r\n \r\n <!-- Action Icons -->\r\n <div class=\"tw-flex tw-items-center tw-justify-between tw-mt-3\">\r\n <div class=\"tw-flex tw-items-center tw-space-x-2\">\r\n <!-- Phone Icon -->\r\n <button class=\"tw-w-6 tw-h-6 tw-rounded-full tw-bg-gray-100 tw-flex tw-items-center tw-justify-center tw-text-gray-600 hover:tw-bg-gray-200 tw-transition-colors\">\r\n <cide-ele-icon class=\"tw-text-xs\">phone</cide-ele-icon>\r\n </button>\r\n \r\n <!-- Email Icon -->\r\n <button class=\"tw-w-6 tw-h-6 tw-rounded-full tw-bg-gray-100 tw-flex tw-items-center tw-justify-center tw-text-gray-600 hover:tw-bg-gray-200 tw-transition-colors\">\r\n <cide-ele-icon class=\"tw-text-xs\">email</cide-ele-icon>\r\n </button>\r\n \r\n <!-- LinkedIn Icon -->\r\n <button class=\"tw-w-6 tw-h-6 tw-rounded-full tw-bg-blue-100 tw-flex tw-items-center tw-justify-center tw-text-blue-600 hover:tw-bg-blue-200 tw-transition-colors\">\r\n <cide-ele-icon class=\"tw-text-xs\">attach_money</cide-ele-icon>\r\n </button>\r\n \r\n <!-- Document Icon -->\r\n <button class=\"tw-w-6 tw-h-6 tw-rounded-full tw-bg-gray-100 tw-flex tw-items-center tw-justify-center tw-text-gray-600 hover:tw-bg-gray-200 tw-transition-colors\">\r\n <cide-ele-icon class=\"tw-text-xs\">description</cide-ele-icon>\r\n </button>\r\n \r\n <!-- More Options Icon -->\r\n <button class=\"tw-w-6 tw-h-6 tw-rounded-full tw-bg-gray-100 tw-flex tw-items-center tw-justify-center tw-text-gray-600 hover:tw-bg-gray-200 tw-transition-colors\">\r\n <cide-ele-icon class=\"tw-text-xs\">more_horiz</cide-ele-icon>\r\n </button>\r\n </div>\r\n \r\n <!-- Status Badge -->\r\n <div class=\"tw-flex-shrink-0\">\r\n @if (node.syen_isactive) {\r\n <div class=\"tw-inline-flex tw-items-center tw-px-1.5 tw-py-0.5 tw-rounded-full tw-text-xs tw-font-medium tw-bg-green-100 tw-text-green-800\">\r\n <div class=\"tw-w-1.5 tw-h-1.5 tw-bg-green-500 tw-rounded-full tw-mr-1\"></div>\r\n Active\r\n </div>\r\n } @else {\r\n <div class=\"tw-inline-flex tw-items-center tw-px-1.5 tw-py-0.5 tw-rounded-full tw-text-xs tw-font-medium tw-bg-red-100 tw-text-red-800\">\r\n <div class=\"tw-w-1.5 tw-h-1.5 tw-bg-red-500 tw-rounded-full tw-mr-1\"></div>\r\n Inactive\r\n </div>\r\n }\r\n </div>\r\n </div>\r\n </div>\r\n \r\n <!-- Children Entities (Recursive) -->\r\n @if (node.children && node.children.length > 0) {\r\n <!-- Flexible Connection Lines -->\r\n <div class=\"tw-relative tw-flex tw-flex-col tw-items-center tw-mt-4\">\r\n <!-- Connection dot -->\r\n <div class=\"tw-absolute tw-left-1/2 tw-transform tw--translate-x-1/2\" style=\"top: -8px;\">\r\n <div class=\"tw-w-2 tw-h-2 tw-bg-purple-600 tw-rounded-full tw-border tw-border-white\"></div>\r\n </div>\r\n \r\n <!-- Flexible SVG Lines -->\r\n <div class=\"tw-relative tw-w-full tw-flex tw-justify-center flexible-connection-lines\">\r\n @if (node.children && node.children.length === 1) {\r\n <!-- Single child - simple vertical line -->\r\n <svg class=\"tw-absolute tw-left-1/2\" style=\"transform: translateX(-50%); width: 2px; height: 40px; top: 0;\">\r\n <line x1=\"1\" y1=\"0\" x2=\"1\" y2=\"40\" stroke=\"#9333ea\" stroke-width=\"2\" stroke-linecap=\"round\"/>\r\n </svg>\r\n }\r\n @else if (node.children && node.children.length > 1) {\r\n <!-- Multiple children - flexible design -->\r\n <svg class=\"tw-absolute tw-left-1/2\" style=\"transform: translateX(-50%); width: 100%; height: 40px; top: 0;\">\r\n <!-- Vertical line from parent -->\r\n <line x1=\"50%\" y1=\"0\" x2=\"50%\" y2=\"15\" stroke=\"#9333ea\" stroke-width=\"2\" stroke-linecap=\"round\"/>\r\n \r\n <!-- Horizontal line spanning children -->\r\n <line x1=\"20%\" y1=\"15\" x2=\"80%\" y2=\"15\" stroke=\"#9333ea\" stroke-width=\"2\" stroke-linecap=\"round\"/>\r\n \r\n <!-- Vertical lines to children -->\r\n @for (child of node.children; track child._id; let i = $index) {\r\n <line [attr.x1]=\"(20 + (60 * i) / (node.children.length - 1)) + '%'\" \r\n y1=\"15\" \r\n [attr.x2]=\"(20 + (60 * i) / (node.children.length - 1)) + '%'\" \r\n y2=\"35\" \r\n stroke=\"#9333ea\" \r\n stroke-width=\"2\" \r\n stroke-linecap=\"round\"/>\r\n <!-- Connection dot -->\r\n <circle [attr.cx]=\"(20 + (60 * i) / (node.children.length - 1)) + '%'\" cy=\"35\" r=\"2.5\" fill=\"#9333ea\" stroke=\"#fff\" stroke-width=\"1\"/>\r\n }\r\n </svg>\r\n }\r\n </div>\r\n \r\n <!-- Children Container - Flexible Layout -->\r\n <div class=\"tw-flex tw-flex-wrap tw-justify-center tw-gap-4 tw-w-full tw-max-w-6xl tw-px-4 children-container\">\r\n @for (child of node.children; track child._id) {\r\n <div class=\"tw-flex tw-flex-col tw-items-center tw-flex-shrink-0\">\r\n <!-- Child node -->\r\n <ng-container [ngTemplateOutlet]=\"entityNodeTemplate\" [ngTemplateOutletContext]=\"{ $implicit: child, level: level + 1 }\"></ng-container>\r\n </div>\r\n }\r\n </div>\r\n </div>\r\n }\r\n </div>\r\n</ng-template>\r\n", styles: [".org-chart-container{overflow-x:auto;overflow-y:visible;min-height:100vh}.org-chart-content{min-width:max-content;padding:2rem 1rem}.flexible-connection-lines{position:relative;width:100%;height:40px}.flexible-connection-lines svg{position:absolute;left:50%;transform:translate(-50%);width:100%;height:100%;top:0}.children-container{display:flex;flex-wrap:wrap;justify-content:center;gap:1rem;width:100%;max-width:80rem;padding:0 1rem}.children-container>div{flex-shrink:0;display:flex;flex-direction:column;align-items:center}.entity-card{width:16rem;max-width:18rem;transition:all .2s ease-in-out}.entity-card:hover{transform:translateY(-2px) scale(1.02);box-shadow:0 20px 25px -5px #0000001a,0 10px 10px -5px #0000000a}@media (max-width: 1400px){.children-container{max-width:70rem;gap:.875rem}.entity-card{width:15rem;max-width:17rem}}@media (max-width: 1200px){.children-container{max-width:60rem;gap:.75rem}.entity-card{width:14rem;max-width:16rem}}@media (max-width: 992px){.children-container{max-width:50rem;gap:.625rem}.entity-card{width:13rem;max-width:15rem}}@media (max-width: 768px){.children-container{max-width:40rem;gap:.5rem}.entity-card{width:12rem;max-width:14rem}.org-chart-content{padding:1.5rem .75rem}}@media (max-width: 576px){.children-container{max-width:30rem;gap:.375rem}.entity-card{width:11rem;max-width:13rem}.org-chart-content{padding:1rem .5rem}}.tw-overflow-y-auto::-webkit-scrollbar{width:6px}.tw-overflow-y-auto::-webkit-scrollbar-track{background:#f1f5f9;border-radius:3px}.tw-overflow-y-auto::-webkit-scrollbar-thumb{background:#cbd5e1;border-radius:3px}.tw-overflow-y-auto::-webkit-scrollbar-thumb:hover{background:#94a3b8}.tw-transition-shadow{transition:box-shadow .2s ease-in-out}.tw-shadow-lg:hover{box-shadow:0 20px 25px -5px #0000001a,0 10px 10px -5px #0000000a;transform:translateY(-2px);transition:all .2s ease-in-out}.tw-bg-purple-300,.tw-bg-purple-400{transition:all .3s ease-in-out}.connection-line-compact{display:block;overflow:visible}.connection-line-compact path,.connection-line-compact line{stroke-linecap:round;stroke-linejoin:round;transition:stroke-width .2s ease}.connection-line-compact:hover path,.connection-line-compact:hover line{stroke-width:3}.connection-line-compact circle{transition:all .2s ease}.connection-line-compact circle:hover{filter:drop-shadow(0 2px 4px rgba(147,51,234,.4))}.level-0{border-color:#fb923c!important}.level-1{border-color:#60a5fa!important}.level-2{border-color:#a78bfa!important}.level-3{border-color:#4ade80!important}.level-4{border-color:#818cf8!important}.level-5{border-color:#f472b6!important}@media (max-width: 1200px){.tw-max-w-7xl{max-width:100%}.tw-gap-6{gap:1rem}.tw-min-w-64{min-width:14rem}.tw-max-w-72{max-width:16rem}}@media (max-width: 768px){.tw-min-w-64{min-width:12rem}.tw-max-w-72{max-width:14rem}.tw-max-w-7xl{max-width:100%}.tw-gap-6{gap:.75rem}.tw-p-4{padding:.75rem}.tw-mb-4{margin-bottom:.75rem}.tw-space-x-3{gap:.5rem}.tw-space-x-2{gap:.375rem}.tw-w-12{width:2.5rem}.tw-h-12{height:2.5rem}}@media (max-width: 480px){.tw-min-w-72{min-width:12rem}.tw-max-w-80{max-width:14rem}.tw-p-4{padding:.5rem}.tw-space-x-3{gap:.375rem}.tw-space-x-2{gap:.25rem}.tw-mb-3{margin-bottom:.5rem}.tw-w-12{width:2rem}.tw-h-12{height:2rem}.tw-w-8{width:1.5rem}.tw-h-8{height:1.5rem}}@media print{.tw-bg-gray-50{background:#fff!important}.tw-shadow-lg{box-shadow:none!important;border:1px solid #e5e7eb!important}.tw-bg-purple-300{background:#6b7280!important}.tw-border-purple-300{border-color:#6b7280!important}}\n"] }]
931
+ CideSpinnerComponent,
932
+ TooltipDirective
933
+ ], template: "<!-- Organization Structure Component -->\n<div class=\"tw-flex-1 tw-overflow-hidden\">\n @if (loading()) {\n <div class=\"tw-flex tw-items-center tw-justify-center tw-py-12\">\n <cide-ele-spinner size=\"md\"></cide-ele-spinner>\n <span class=\"tw-ml-3 tw-text-gray-600 tw-text-sm\">Loading organization structure...</span>\n </div>\n } @else if (error()) {\n <div class=\"tw-text-center tw-py-12\">\n <div class=\"tw-w-16 tw-h-16 tw-bg-red-100 tw-rounded-full tw-flex tw-items-center tw-justify-center tw-mx-auto tw-mb-4\">\n <cide-ele-icon class=\"tw-w-8 tw-h-8 tw-text-red-500\">error</cide-ele-icon>\n </div>\n <h3 class=\"tw-text-lg tw-font-medium tw-text-gray-900 tw-mb-2\">Error Loading Structure</h3>\n <p class=\"tw-text-gray-600 tw-mb-4\">{{ error() }}</p>\n <button cideEleButton variant=\"primary\" size=\"sm\" (click)=\"loadOrgStructure()\">\n <cide-ele-icon>refresh</cide-ele-icon>\n Try Again\n </button>\n </div>\n } @else if (orgStructure().length === 0) {\n <div class=\"tw-text-center tw-py-12\">\n <div class=\"tw-w-16 tw-h-16 tw-bg-gray-100 tw-rounded-full tw-flex tw-items-center tw-justify-center tw-mx-auto tw-mb-4\">\n <cide-ele-icon class=\"tw-w-8 tw-h-8 tw-text-gray-400\">account_tree</cide-ele-icon>\n </div>\n <h3 class=\"tw-text-lg tw-font-medium tw-text-gray-900 tw-mb-2\">No Entities Found</h3>\n <p class=\"tw-text-gray-600 tw-mb-4\">No entities are available to display in the organization structure.</p>\n @if (mode() === 'view') {\n <button cideEleButton variant=\"primary\" size=\"sm\" (click)=\"router.navigate(['/control-panel/entity-list'])\">\n <cide-ele-icon>add</cide-ele-icon>\n Create First Entity\n </button>\n }\n </div>\n } @else {\n <!-- Organization Chart Container -->\n <div class=\"tw-relative tw-min-h-screen tw-bg-gray-50 tw-py-8 org-chart-container\">\n <!-- Chart Content with Background Pattern -->\n <div class=\"tw-relative tw-z-10 tw-min-w-max tw-px-4 org-chart-content org-chart-content-with-bg\">\n @for (rootNode of orgStructure(); track rootNode._id) {\n <div class=\"tw-flex tw-flex-col tw-items-center tw-mb-12\">\n <!-- Render node recursively with unlimited depth -->\n <ng-container [ngTemplateOutlet]=\"entityNodeTemplate\" [ngTemplateOutletContext]=\"{ $implicit: rootNode, level: 0 }\"></ng-container>\n </div>\n }\n </div>\n </div>\n }\n</div>\n\n<!-- Entity Node Template (Recursive) -->\n<ng-template #entityNodeTemplate let-node let-level=\"level\">\n <div class=\"tw-flex tw-flex-col tw-items-center\">\n \n <!-- Entity Card -->\n <div class=\"tw-bg-white tw-shadow-lg tw-rounded-lg tw-p-4 tw-w-64 tw-max-w-72 tw-border-t-4 tw-transition-all tw-cursor-pointer hover:tw-shadow-xl hover:tw-scale-105 entity-card\"\n [class.level-0]=\"level === 0\"\n [class.level-1]=\"level === 1\"\n [class.level-2]=\"level === 2\"\n [class.level-3]=\"level === 3\"\n [class.level-4]=\"level === 4\"\n [class.level-5]=\"level === 5\"\n (click)=\"viewEntity(node._id)\">\n \n <!-- Card Content -->\n <div class=\"tw-flex tw-items-center tw-space-x-3\">\n <!-- Avatar -->\n <div class=\"tw-w-12 tw-h-12 tw-bg-purple-100 tw-rounded-full tw-flex tw-items-center tw-justify-center tw-flex-shrink-0\">\n <cide-ele-icon class=\"tw-w-6 tw-h-6 tw-text-purple-600\">person</cide-ele-icon>\n </div>\n \n <!-- Entity Info -->\n <div class=\"tw-flex-1 tw-min-w-0\">\n <h3 class=\"tw-text-sm tw-font-semibold tw-text-gray-900 tw-whitespace-nowrap tw-overflow-hidden tw-text-ellipsis\" \n [cideEleTooltip]=\"node.syen_name || ''\" \n [tooltipPlacement]=\"'top'\"\n [tooltipType]=\"'default'\">{{ node.syen_name }}</h3>\n <p class=\"tw-text-xs tw-text-gray-600 tw-mt-1\">{{ node.syen_entity_code }}</p>\n <p class=\"tw-text-xs tw-text-gray-500 tw-mt-1\">{{ node.syen_entity_type_sygms }}</p>\n </div>\n </div>\n \n <!-- Action Icons -->\n <div class=\"tw-flex tw-items-center tw-justify-between tw-mt-3\">\n <div class=\"tw-flex tw-items-center tw-space-x-2\">\n <!-- Phone Icon -->\n <button class=\"tw-w-6 tw-h-6 tw-rounded-full tw-bg-gray-100 tw-flex tw-items-center tw-justify-center tw-text-gray-600 hover:tw-bg-gray-200 tw-transition-colors\">\n <cide-ele-icon class=\"tw-text-xs\">phone</cide-ele-icon>\n </button>\n \n <!-- Email Icon -->\n <button class=\"tw-w-6 tw-h-6 tw-rounded-full tw-bg-gray-100 tw-flex tw-items-center tw-justify-center tw-text-gray-600 hover:tw-bg-gray-200 tw-transition-colors\">\n <cide-ele-icon class=\"tw-text-xs\">email</cide-ele-icon>\n </button>\n \n <!-- LinkedIn Icon -->\n <button class=\"tw-w-6 tw-h-6 tw-rounded-full tw-bg-blue-100 tw-flex tw-items-center tw-justify-center tw-text-blue-600 hover:tw-bg-blue-200 tw-transition-colors\">\n <cide-ele-icon class=\"tw-text-xs\">attach_money</cide-ele-icon>\n </button>\n \n <!-- Document Icon -->\n <button class=\"tw-w-6 tw-h-6 tw-rounded-full tw-bg-gray-100 tw-flex tw-items-center tw-justify-center tw-text-gray-600 hover:tw-bg-gray-200 tw-transition-colors\">\n <cide-ele-icon class=\"tw-text-xs\">description</cide-ele-icon>\n </button>\n \n <!-- More Options Icon -->\n <button class=\"tw-w-6 tw-h-6 tw-rounded-full tw-bg-gray-100 tw-flex tw-items-center tw-justify-center tw-text-gray-600 hover:tw-bg-gray-200 tw-transition-colors\">\n <cide-ele-icon class=\"tw-text-xs\">more_horiz</cide-ele-icon>\n </button>\n </div>\n \n <!-- Status Badge -->\n <div class=\"tw-flex-shrink-0\">\n @if (node.syen_isactive) {\n <div class=\"tw-inline-flex tw-items-center tw-px-1.5 tw-py-0.5 tw-rounded-full tw-text-xs tw-font-medium tw-bg-green-100 tw-text-green-800\">\n <div class=\"tw-w-1.5 tw-h-1.5 tw-bg-green-500 tw-rounded-full tw-mr-1\"></div>\n Active\n </div>\n } @else {\n <div class=\"tw-inline-flex tw-items-center tw-px-1.5 tw-py-0.5 tw-rounded-full tw-text-xs tw-font-medium tw-bg-red-100 tw-text-red-800\">\n <div class=\"tw-w-1.5 tw-h-1.5 tw-bg-red-500 tw-rounded-full tw-mr-1\"></div>\n Inactive\n </div>\n }\n </div>\n </div>\n </div>\n \n <!-- Children Entities (Recursive) -->\n @if (node.children && node.children.length > 0) {\n <!-- Flexible Connection Lines -->\n <div class=\"tw-relative tw-flex tw-flex-col tw-items-center tw-mt-4\">\n <!-- Connection dot -->\n <div class=\"tw-absolute tw-left-1/2 tw-transform tw--translate-x-1/2\" style=\"top: -8px;\">\n <div class=\"tw-w-2 tw-h-2 tw-bg-purple-600 tw-rounded-full tw-border tw-border-white\"></div>\n </div>\n \n <!-- Flexible SVG Lines -->\n <div class=\"tw-relative tw-w-full tw-flex tw-justify-center flexible-connection-lines\">\n @if (node.children && node.children.length === 1) {\n <!-- Single child - simple vertical line -->\n <svg class=\"tw-absolute tw-left-1/2\" style=\"transform: translateX(-50%); width: 2px; height: 40px; top: 0;\">\n <line x1=\"1\" y1=\"0\" x2=\"1\" y2=\"40\" stroke=\"#9333ea\" stroke-width=\"2\" stroke-linecap=\"round\"/>\n </svg>\n }\n @else if (node.children && node.children.length > 1) {\n <!-- Multiple children - flexible design -->\n <svg class=\"tw-absolute tw-left-1/2\" style=\"transform: translateX(-50%); width: 100%; height: 40px; top: 0;\">\n <!-- Vertical line from parent -->\n <line x1=\"50%\" y1=\"0\" x2=\"50%\" y2=\"15\" stroke=\"#9333ea\" stroke-width=\"2\" stroke-linecap=\"round\"/>\n \n <!-- Horizontal line spanning children -->\n <line x1=\"20%\" y1=\"15\" x2=\"80%\" y2=\"15\" stroke=\"#9333ea\" stroke-width=\"2\" stroke-linecap=\"round\"/>\n \n <!-- Vertical lines to children -->\n @for (child of node.children; track child._id; let i = $index) {\n <line [attr.x1]=\"(20 + (60 * i) / (node.children.length - 1)) + '%'\" \n y1=\"15\" \n [attr.x2]=\"(20 + (60 * i) / (node.children.length - 1)) + '%'\" \n y2=\"35\" \n stroke=\"#9333ea\" \n stroke-width=\"2\" \n stroke-linecap=\"round\"/>\n <!-- Connection dot -->\n <circle [attr.cx]=\"(20 + (60 * i) / (node.children.length - 1)) + '%'\" cy=\"35\" r=\"2.5\" fill=\"#9333ea\" stroke=\"#fff\" stroke-width=\"1\"/>\n }\n </svg>\n }\n </div>\n \n <!-- Children Container - Flexible Layout -->\n <div class=\"tw-flex tw-flex-wrap tw-justify-center tw-gap-4 tw-w-full tw-max-w-6xl tw-px-4 children-container\">\n @for (child of node.children; track child._id) {\n <div class=\"tw-flex tw-flex-col tw-items-center tw-flex-shrink-0\">\n <!-- Child node -->\n <ng-container [ngTemplateOutlet]=\"entityNodeTemplate\" [ngTemplateOutletContext]=\"{ $implicit: child, level: level + 1 }\"></ng-container>\n </div>\n }\n </div>\n </div>\n }\n </div>\n</ng-template>\n", styles: [".org-chart-container{overflow-x:auto;overflow-y:auto;min-height:100vh;max-height:100vh;position:relative}.org-chart-content{min-width:max-content;min-height:max-content;padding:2rem 1rem}.org-chart-content-with-bg{position:relative}.org-chart-content-with-bg:before{content:\"\";position:absolute;inset:0;opacity:.3;pointer-events:none;background-image:radial-gradient(circle,#e5e7eb 1px,transparent 1px);background-size:20px 20px;background-repeat:repeat;z-index:-1}.flexible-connection-lines{position:relative;width:100%;height:40px}.flexible-connection-lines svg{position:absolute;left:50%;transform:translate(-50%);width:100%;height:100%;top:0}.children-container{display:flex;flex-wrap:wrap;justify-content:center;gap:1rem;width:100%;max-width:80rem;padding:0 1rem}.children-container>div{flex-shrink:0;display:flex;flex-direction:column;align-items:center}.entity-card{width:16rem;max-width:18rem;transition:all .2s ease-in-out}.entity-card:hover{transform:translateY(-2px) scale(1.02);box-shadow:0 20px 25px -5px #0000001a,0 10px 10px -5px #0000000a}@media (max-width: 1400px){.children-container{max-width:70rem;gap:.875rem}.entity-card{width:15rem;max-width:17rem}}@media (max-width: 1200px){.children-container{max-width:60rem;gap:.75rem}.entity-card{width:14rem;max-width:16rem}}@media (max-width: 992px){.children-container{max-width:50rem;gap:.625rem}.entity-card{width:13rem;max-width:15rem}}@media (max-width: 768px){.children-container{max-width:40rem;gap:.5rem}.entity-card{width:12rem;max-width:14rem}.org-chart-content{padding:1.5rem .75rem}}@media (max-width: 576px){.children-container{max-width:30rem;gap:.375rem}.entity-card{width:11rem;max-width:13rem}.org-chart-content{padding:1rem .5rem}}.tw-overflow-y-auto::-webkit-scrollbar{width:6px}.tw-overflow-y-auto::-webkit-scrollbar-track{background:#f1f5f9;border-radius:3px}.tw-overflow-y-auto::-webkit-scrollbar-thumb{background:#cbd5e1;border-radius:3px}.tw-overflow-y-auto::-webkit-scrollbar-thumb:hover{background:#94a3b8}.tw-transition-shadow{transition:box-shadow .2s ease-in-out}.tw-shadow-lg:hover{box-shadow:0 20px 25px -5px #0000001a,0 10px 10px -5px #0000000a;transform:translateY(-2px);transition:all .2s ease-in-out}.tw-bg-purple-300,.tw-bg-purple-400{transition:all .3s ease-in-out}.connection-line-compact{display:block;overflow:visible}.connection-line-compact path,.connection-line-compact line{stroke-linecap:round;stroke-linejoin:round;transition:stroke-width .2s ease}.connection-line-compact:hover path,.connection-line-compact:hover line{stroke-width:3}.connection-line-compact circle{transition:all .2s ease}.connection-line-compact circle:hover{filter:drop-shadow(0 2px 4px rgba(147,51,234,.4))}.level-0{border-color:#fb923c!important}.level-1{border-color:#60a5fa!important}.level-2{border-color:#a78bfa!important}.level-3{border-color:#4ade80!important}.level-4{border-color:#818cf8!important}.level-5{border-color:#f472b6!important}@media (max-width: 1200px){.tw-max-w-7xl{max-width:100%}.tw-gap-6{gap:1rem}.tw-min-w-64{min-width:14rem}.tw-max-w-72{max-width:16rem}}@media (max-width: 768px){.tw-min-w-64{min-width:12rem}.tw-max-w-72{max-width:14rem}.tw-max-w-7xl{max-width:100%}.tw-gap-6{gap:.75rem}.tw-p-4{padding:.75rem}.tw-mb-4{margin-bottom:.75rem}.tw-space-x-3{gap:.5rem}.tw-space-x-2{gap:.375rem}.tw-w-12{width:2.5rem}.tw-h-12{height:2.5rem}}@media (max-width: 480px){.tw-min-w-72{min-width:12rem}.tw-max-w-80{max-width:14rem}.tw-p-4{padding:.5rem}.tw-space-x-3{gap:.375rem}.tw-space-x-2{gap:.25rem}.tw-mb-3{margin-bottom:.5rem}.tw-w-12{width:2rem}.tw-h-12{height:2rem}.tw-w-8{width:1.5rem}.tw-h-8{height:1.5rem}}@media print{.tw-bg-gray-50{background:#fff!important}.tw-shadow-lg{box-shadow:none!important;border:1px solid #e5e7eb!important}.tw-bg-purple-300{background:#6b7280!important}.tw-border-purple-300{border-color:#6b7280!important}}\n"] }]
883
934
  }] });
884
935
 
936
+ class CideSharedProgramSectionSelectorComponent {
937
+ // Inputs
938
+ formGroup = input.required(...(ngDevMode ? [{ debugName: "formGroup" }] : []));
939
+ classProgramControlName = input('class_program_id', ...(ngDevMode ? [{ debugName: "classProgramControlName" }] : []));
940
+ branchControlName = input('branch_id', ...(ngDevMode ? [{ debugName: "branchControlName" }] : []));
941
+ termControlName = input('term_id', ...(ngDevMode ? [{ debugName: "termControlName" }] : []));
942
+ sectionControlName = input('section_id', ...(ngDevMode ? [{ debugName: "sectionControlName" }] : []));
943
+ academicYearId = input(null, ...(ngDevMode ? [{ debugName: "academicYearId" }] : []));
944
+ entityId = input(null, ...(ngDevMode ? [{ debugName: "entityId" }] : []));
945
+ disabled = input(false, ...(ngDevMode ? [{ debugName: "disabled" }] : []));
946
+ showLabels = input(true, ...(ngDevMode ? [{ debugName: "showLabels" }] : []));
947
+ gridCols = input('tw-grid-cols-1 md:tw-grid-cols-3', ...(ngDevMode ? [{ debugName: "gridCols" }] : []));
948
+ showAllPrograms = input(false, ...(ngDevMode ? [{ debugName: "showAllPrograms" }] : [])); // If true, shows all programs regardless of academic year/entity filters
949
+ includeInactive = input(false, ...(ngDevMode ? [{ debugName: "includeInactive" }] : [])); // If true, includes inactive programs
950
+ // Outputs
951
+ valuesChange = output();
952
+ // Services
953
+ destroyRef = inject(DestroyRef);
954
+ http = inject(HttpClient);
955
+ classProgramService = inject(CLASS_PROGRAM_MASTER_SERVICE_TOKEN);
956
+ notificationService = inject(NotificationService);
957
+ // State
958
+ classProgramMasters = signal([], ...(ngDevMode ? [{ debugName: "classProgramMasters" }] : []));
959
+ classProgramBranches = signal([], ...(ngDevMode ? [{ debugName: "classProgramBranches" }] : []));
960
+ classProgramTerms = signal([], ...(ngDevMode ? [{ debugName: "classProgramTerms" }] : []));
961
+ programTermSections = signal([], ...(ngDevMode ? [{ debugName: "programTermSections" }] : []));
962
+ classProgramMastersLoading = signal(false, ...(ngDevMode ? [{ debugName: "classProgramMastersLoading" }] : []));
963
+ classProgramBranchesLoading = signal(false, ...(ngDevMode ? [{ debugName: "classProgramBranchesLoading" }] : []));
964
+ classProgramTermsLoading = signal(false, ...(ngDevMode ? [{ debugName: "classProgramTermsLoading" }] : []));
965
+ programTermSectionsLoading = signal(false, ...(ngDevMode ? [{ debugName: "programTermSectionsLoading" }] : []));
966
+ selectedClassProgram = signal(null, ...(ngDevMode ? [{ debugName: "selectedClassProgram" }] : []));
967
+ hasSections = signal(false, ...(ngDevMode ? [{ debugName: "hasSections" }] : []));
968
+ isProcessingInitialValues = false;
969
+ // FormControl getters
970
+ getClassProgramControl() {
971
+ return this.formGroup().get(this.classProgramControlName());
972
+ }
973
+ getBranchControl() {
974
+ return this.formGroup().get(this.branchControlName());
975
+ }
976
+ getTermControl() {
977
+ return this.formGroup().get(this.termControlName());
978
+ }
979
+ getSectionControl() {
980
+ return this.formGroup().get(this.sectionControlName());
981
+ }
982
+ // Computed signals
983
+ classProgramOptions = computed(() => {
984
+ return this.classProgramMasters().map(cp => ({
985
+ value: cp._id || '',
986
+ label: cp.acacpm_alise_title || ''
987
+ }));
988
+ }, ...(ngDevMode ? [{ debugName: "classProgramOptions" }] : []));
989
+ showBranchDropdown = computed(() => {
990
+ const program = this.selectedClassProgram();
991
+ if (!program)
992
+ return false;
993
+ const generalMaster = program.acacpm_class_program_id_sygms || program.acacpm_class_program_id_sygmt;
994
+ if (!generalMaster || typeof generalMaster !== 'object')
995
+ return false;
996
+ let config;
997
+ if ('sygms_configuration' in generalMaster) {
998
+ const configValue = generalMaster.sygms_configuration;
999
+ if (typeof configValue === 'string') {
1000
+ try {
1001
+ config = JSON.parse(configValue);
1002
+ }
1003
+ catch {
1004
+ return false;
1005
+ }
1006
+ }
1007
+ else if (typeof configValue === 'object' && configValue !== null) {
1008
+ config = configValue;
1009
+ }
1010
+ }
1011
+ return config?.branchType === true;
1012
+ }, ...(ngDevMode ? [{ debugName: "showBranchDropdown" }] : []));
1013
+ showTermDropdown = computed(() => {
1014
+ const program = this.selectedClassProgram();
1015
+ if (!program)
1016
+ return false;
1017
+ const generalMaster = program.acacpm_class_program_id_sygms || program.acacpm_class_program_id_sygmt;
1018
+ if (!generalMaster || typeof generalMaster !== 'object')
1019
+ return false;
1020
+ let config;
1021
+ if ('sygms_configuration' in generalMaster) {
1022
+ const configValue = generalMaster.sygms_configuration;
1023
+ if (typeof configValue === 'string') {
1024
+ try {
1025
+ config = JSON.parse(configValue);
1026
+ }
1027
+ catch {
1028
+ return false;
1029
+ }
1030
+ }
1031
+ else if (typeof configValue === 'object' && configValue !== null) {
1032
+ config = configValue;
1033
+ }
1034
+ }
1035
+ // If termType is explicitly false, don't show the dropdown
1036
+ if (config?.termType === false) {
1037
+ return false;
1038
+ }
1039
+ // Special case: Hide dropdown if termType=true, termLevelUpTo=1, and termProgramLevelOneConnected=true
1040
+ if (config?.termType === true &&
1041
+ config?.termLevelUpTo === 1 &&
1042
+ config?.termProgramLevelOneConnected === true) {
1043
+ return false;
1044
+ }
1045
+ // Check for both termType (current) and semesterType (legacy) for backward compatibility
1046
+ return config?.termType === true || config?.semesterType === true;
1047
+ }, ...(ngDevMode ? [{ debugName: "showTermDropdown" }] : []));
1048
+ constructor() {
1049
+ // Effect to emit values when form changes
1050
+ effect(() => {
1051
+ const form = this.formGroup();
1052
+ if (form) {
1053
+ const classProgramId = form.get(this.classProgramControlName())?.value;
1054
+ const branchId = form.get(this.branchControlName())?.value;
1055
+ const termId = form.get(this.termControlName())?.value;
1056
+ const sectionId = form.get(this.sectionControlName())?.value;
1057
+ this.valuesChange.emit({
1058
+ classProgramId: classProgramId || null,
1059
+ branchId: branchId || null,
1060
+ termId: termId || null,
1061
+ sectionId: sectionId || null
1062
+ });
1063
+ }
1064
+ });
1065
+ }
1066
+ ngOnInit() {
1067
+ this.loadClassProgramMasters();
1068
+ this.setupFormListeners();
1069
+ // Check for initial values in the form and load cascading options
1070
+ // Use setTimeout to ensure form is ready
1071
+ setTimeout(() => {
1072
+ this.handleInitialValues();
1073
+ }, 0);
1074
+ // Also watch for form value changes to handle values set after component initialization
1075
+ const form = this.formGroup();
1076
+ if (form) {
1077
+ // Watch for class program value being set
1078
+ form.get(this.classProgramControlName())?.valueChanges
1079
+ .pipe(takeUntilDestroyed(this.destroyRef))
1080
+ .subscribe(() => {
1081
+ // If class program is set but we haven't processed initial values yet, process them
1082
+ const classProgramId = form.get(this.classProgramControlName())?.value;
1083
+ if (classProgramId && this.classProgramMasters().length > 0 && !this.selectedClassProgram()) {
1084
+ // Form value was set, but we haven't processed it yet - process now
1085
+ setTimeout(() => {
1086
+ this.handleInitialValues();
1087
+ }, 100);
1088
+ }
1089
+ });
1090
+ }
1091
+ }
1092
+ handleInitialValues() {
1093
+ const form = this.formGroup();
1094
+ if (!form)
1095
+ return;
1096
+ const classProgramId = form.get(this.classProgramControlName())?.value;
1097
+ if (!classProgramId)
1098
+ return;
1099
+ // Set flag to prevent clearing values during initial load
1100
+ this.isProcessingInitialValues = true;
1101
+ // Wait for class programs to load, then process initial values
1102
+ const checkInterval = setInterval(() => {
1103
+ if (this.classProgramMasters().length > 0) {
1104
+ clearInterval(checkInterval);
1105
+ // Find the selected program
1106
+ const selectedProgram = this.classProgramMasters().find(cp => cp._id === classProgramId);
1107
+ if (selectedProgram) {
1108
+ this.selectedClassProgram.set(selectedProgram);
1109
+ // Read config directly from the program to determine what to show
1110
+ const generalMaster = selectedProgram.acacpm_class_program_id_sygms || selectedProgram.acacpm_class_program_id_sygmt;
1111
+ let config;
1112
+ if (generalMaster && typeof generalMaster === 'object') {
1113
+ if ('sygms_configuration' in generalMaster) {
1114
+ const configValue = generalMaster.sygms_configuration;
1115
+ if (typeof configValue === 'string') {
1116
+ try {
1117
+ config = JSON.parse(configValue);
1118
+ }
1119
+ catch {
1120
+ console.warn('Failed to parse config:', configValue);
1121
+ }
1122
+ }
1123
+ else if (typeof configValue === 'object' && configValue !== null) {
1124
+ config = configValue;
1125
+ }
1126
+ }
1127
+ }
1128
+ // Wait a tick for computed signals to update
1129
+ setTimeout(() => {
1130
+ const branchId = form.get(this.branchControlName())?.value;
1131
+ const termId = form.get(this.termControlName())?.value;
1132
+ const sectionId = form.get(this.sectionControlName())?.value;
1133
+ // Load branches if config says so AND we have a branch ID
1134
+ const shouldShowBranch = config?.branchType === true;
1135
+ if (shouldShowBranch) {
1136
+ this.loadClassProgramBranches(classProgramId);
1137
+ // Wait for branches to load
1138
+ if (branchId) {
1139
+ const branchCheckInterval = setInterval(() => {
1140
+ if (this.classProgramBranches().length > 0 || !this.classProgramBranchesLoading()) {
1141
+ clearInterval(branchCheckInterval);
1142
+ // Branch value is already set, now load terms
1143
+ this.loadClassProgramTerms(classProgramId, branchId);
1144
+ // Wait for terms to load
1145
+ if (termId) {
1146
+ const termCheckInterval = setInterval(() => {
1147
+ if (this.classProgramTerms().length > 0 || !this.classProgramTermsLoading()) {
1148
+ clearInterval(termCheckInterval);
1149
+ // Term value is already set, now load sections
1150
+ if (sectionId) {
1151
+ this.loadProgramTermSections(termId);
1152
+ }
1153
+ }
1154
+ }, 100);
1155
+ setTimeout(() => clearInterval(termCheckInterval), 5000);
1156
+ }
1157
+ }
1158
+ }, 100);
1159
+ setTimeout(() => clearInterval(branchCheckInterval), 5000);
1160
+ }
1161
+ else {
1162
+ // No branch ID but should show branch dropdown - load terms after branches load
1163
+ const branchCheckInterval = setInterval(() => {
1164
+ if (!this.classProgramBranchesLoading()) {
1165
+ clearInterval(branchCheckInterval);
1166
+ if (termId) {
1167
+ this.loadClassProgramTerms(classProgramId, undefined);
1168
+ if (sectionId) {
1169
+ const termCheckInterval = setInterval(() => {
1170
+ if (this.classProgramTerms().length > 0 || !this.classProgramTermsLoading()) {
1171
+ clearInterval(termCheckInterval);
1172
+ this.loadProgramTermSections(termId);
1173
+ }
1174
+ }, 100);
1175
+ setTimeout(() => clearInterval(termCheckInterval), 5000);
1176
+ }
1177
+ }
1178
+ }
1179
+ }, 100);
1180
+ setTimeout(() => clearInterval(branchCheckInterval), 5000);
1181
+ }
1182
+ }
1183
+ else {
1184
+ // No branch dropdown, load terms directly if config allows
1185
+ const shouldShowTerm = config?.termType === true || config?.semesterType === true;
1186
+ if (shouldShowTerm && termId) {
1187
+ this.loadClassProgramTerms(classProgramId, undefined);
1188
+ // Wait for terms to load, then load sections
1189
+ if (sectionId) {
1190
+ const termCheckInterval = setInterval(() => {
1191
+ if (this.classProgramTerms().length > 0 || !this.classProgramTermsLoading()) {
1192
+ clearInterval(termCheckInterval);
1193
+ this.loadProgramTermSections(termId);
1194
+ }
1195
+ }, 100);
1196
+ setTimeout(() => clearInterval(termCheckInterval), 5000);
1197
+ }
1198
+ }
1199
+ }
1200
+ }, 0);
1201
+ }
1202
+ }
1203
+ }, 100);
1204
+ // Timeout after 5 seconds
1205
+ setTimeout(() => {
1206
+ clearInterval(checkInterval);
1207
+ // Reset flag after processing is complete
1208
+ this.isProcessingInitialValues = false;
1209
+ }, 5000);
1210
+ }
1211
+ ngOnDestroy() {
1212
+ // Cleanup handled by takeUntilDestroyed
1213
+ }
1214
+ setupFormListeners() {
1215
+ const form = this.formGroup();
1216
+ if (!form)
1217
+ return;
1218
+ // Listen for class program selection changes
1219
+ form.get(this.classProgramControlName())?.valueChanges
1220
+ .pipe(takeUntilDestroyed(this.destroyRef))
1221
+ .subscribe(classProgramId => {
1222
+ this.onClassProgramChange(classProgramId);
1223
+ });
1224
+ // Listen for branch selection changes
1225
+ form.get(this.branchControlName())?.valueChanges
1226
+ .pipe(takeUntilDestroyed(this.destroyRef))
1227
+ .subscribe(branchId => {
1228
+ this.onBranchChange(branchId);
1229
+ });
1230
+ // Listen for term selection changes
1231
+ form.get(this.termControlName())?.valueChanges
1232
+ .pipe(takeUntilDestroyed(this.destroyRef))
1233
+ .subscribe(termId => {
1234
+ this.onTermChange(termId);
1235
+ });
1236
+ }
1237
+ loadClassProgramMasters() {
1238
+ this.classProgramMastersLoading.set(true);
1239
+ const academicYearId = this.academicYearId();
1240
+ const entityId = this.entityId();
1241
+ const showAll = this.showAllPrograms();
1242
+ const includeInactive = this.includeInactive();
1243
+ // Build payload - only include filters if not showing all programs
1244
+ const payload = {
1245
+ pagination: false
1246
+ };
1247
+ // Only add academic year filter if not showing all programs and academic year is provided
1248
+ if (!showAll && academicYearId) {
1249
+ payload['acacpm_academic_year_id_acayr'] = academicYearId;
1250
+ }
1251
+ // Only add entity filter if not showing all programs and entity is provided
1252
+ if (!showAll && entityId) {
1253
+ payload['acacpm_entity_id_syen'] = entityId;
1254
+ }
1255
+ // Only add active filter if not including inactive programs
1256
+ if (!includeInactive) {
1257
+ payload['acacpm_isactive'] = true;
1258
+ }
1259
+ this.classProgramService.searchClassProgramMasters(payload).subscribe({
1260
+ next: (response) => {
1261
+ if (response?.success && response.data) {
1262
+ const dataArray = Array.isArray(response.data) ? response.data : [];
1263
+ this.classProgramMasters.set(dataArray);
1264
+ console.log(`✅ Loaded ${dataArray.length} class programs`, {
1265
+ showAll,
1266
+ includeInactive,
1267
+ academicYearId,
1268
+ entityId,
1269
+ filters: payload
1270
+ });
1271
+ }
1272
+ else {
1273
+ this.classProgramMasters.set([]);
1274
+ console.warn('⚠️ No class programs found', response);
1275
+ }
1276
+ this.classProgramMastersLoading.set(false);
1277
+ },
1278
+ error: (response) => {
1279
+ console.error('❌ Error loading class programs:', response);
1280
+ const errorMessage = response?.error?.message;
1281
+ if (errorMessage) {
1282
+ this.notificationService.error(errorMessage);
1283
+ }
1284
+ this.classProgramMasters.set([]);
1285
+ this.classProgramMastersLoading.set(false);
1286
+ }
1287
+ });
1288
+ }
1289
+ onClassProgramSearch(event) {
1290
+ this.classProgramMastersLoading.set(true);
1291
+ const academicYearId = this.academicYearId();
1292
+ const entityId = this.entityId();
1293
+ const showAll = this.showAllPrograms();
1294
+ const includeInactive = this.includeInactive();
1295
+ // Build payload for search
1296
+ const payload = {
1297
+ query: event.query || "",
1298
+ acacpm_id: event.value ? String(event.value) : "",
1299
+ pagination: false
1300
+ };
1301
+ // Only add academic year filter if not showing all programs and academic year is provided
1302
+ if (!showAll && academicYearId) {
1303
+ payload['acacpm_academic_year_id_acayr'] = academicYearId;
1304
+ }
1305
+ // Only add entity filter if not showing all programs and entity is provided
1306
+ if (!showAll && entityId) {
1307
+ payload['acacpm_entity_id_syen'] = entityId;
1308
+ }
1309
+ // Only add active filter if not including inactive programs
1310
+ if (!includeInactive) {
1311
+ payload['acacpm_isactive'] = true;
1312
+ }
1313
+ this.classProgramService.searchClassProgramMasters(payload).subscribe({
1314
+ next: (response) => {
1315
+ if (response?.success && response.data) {
1316
+ const dataArray = Array.isArray(response.data) ? response.data : [];
1317
+ this.classProgramMasters.set(dataArray);
1318
+ }
1319
+ else {
1320
+ this.classProgramMasters.set([]);
1321
+ }
1322
+ this.classProgramMastersLoading.set(false);
1323
+ },
1324
+ error: () => {
1325
+ this.classProgramMasters.set([]);
1326
+ this.classProgramMastersLoading.set(false);
1327
+ }
1328
+ });
1329
+ }
1330
+ onClassProgramChange(classProgramId) {
1331
+ // Clear previous selections state
1332
+ this.selectedClassProgram.set(null);
1333
+ this.classProgramBranches.set([]);
1334
+ this.classProgramTerms.set([]);
1335
+ this.programTermSections.set([]);
1336
+ if (!classProgramId) {
1337
+ // Clear all dependent values if no class program selected
1338
+ const form = this.formGroup();
1339
+ if (form) {
1340
+ form.patchValue({
1341
+ [this.branchControlName()]: '',
1342
+ [this.termControlName()]: '',
1343
+ [this.sectionControlName()]: ''
1344
+ }, { emitEvent: false });
1345
+ }
1346
+ return;
1347
+ }
1348
+ // Find the selected program from the list
1349
+ const selectedProgram = this.classProgramMasters().find(cp => cp._id === classProgramId);
1350
+ if (!selectedProgram) {
1351
+ return;
1352
+ }
1353
+ this.selectedClassProgram.set(selectedProgram);
1354
+ const form = this.formGroup();
1355
+ if (!form)
1356
+ return;
1357
+ // Get current form values before loading options
1358
+ const currentBranchId = form.get(this.branchControlName())?.value;
1359
+ const currentTermId = form.get(this.termControlName())?.value;
1360
+ const currentSectionId = form.get(this.sectionControlName())?.value;
1361
+ // Load branches if branchType is true
1362
+ if (this.showBranchDropdown()) {
1363
+ this.loadClassProgramBranches(classProgramId);
1364
+ // After branches load, check if current branch value is valid
1365
+ const branchCheckInterval = setInterval(() => {
1366
+ if (!this.classProgramBranchesLoading()) {
1367
+ clearInterval(branchCheckInterval);
1368
+ // Check if current branch value exists in loaded branches
1369
+ const branchExists = currentBranchId &&
1370
+ this.classProgramBranches().some(b => b.value === currentBranchId);
1371
+ if (!branchExists && currentBranchId) {
1372
+ // Current branch value is not in the list, clear it
1373
+ form.patchValue({
1374
+ [this.branchControlName()]: ''
1375
+ }, { emitEvent: false });
1376
+ }
1377
+ // Load terms with the branch (or without if branch was cleared)
1378
+ const branchIdToUse = branchExists ? currentBranchId : undefined;
1379
+ if (this.showTermDropdown()) {
1380
+ this.loadClassProgramTerms(classProgramId, branchIdToUse);
1381
+ // After terms load, check if current term value is valid
1382
+ const termCheckInterval = setInterval(() => {
1383
+ if (!this.classProgramTermsLoading()) {
1384
+ clearInterval(termCheckInterval);
1385
+ // Check if current term value exists in loaded terms
1386
+ const termExists = currentTermId &&
1387
+ this.classProgramTerms().some(t => t.value === currentTermId);
1388
+ if (!termExists && currentTermId) {
1389
+ // Current term value is not in the list, clear it
1390
+ form.patchValue({
1391
+ [this.termControlName()]: ''
1392
+ }, { emitEvent: false });
1393
+ // Also clear section if term is cleared
1394
+ form.patchValue({
1395
+ [this.sectionControlName()]: ''
1396
+ }, { emitEvent: false });
1397
+ }
1398
+ else if (termExists && currentSectionId) {
1399
+ // Term is valid, load sections
1400
+ this.loadProgramTermSections(currentTermId);
1401
+ // After sections load, check if current section value is valid
1402
+ const sectionCheckInterval = setInterval(() => {
1403
+ if (!this.programTermSectionsLoading()) {
1404
+ clearInterval(sectionCheckInterval);
1405
+ // Check if current section value exists in loaded sections
1406
+ const sectionExists = this.programTermSections().some(s => s.value === currentSectionId);
1407
+ if (!sectionExists) {
1408
+ // Current section value is not in the list, clear it
1409
+ form.patchValue({
1410
+ [this.sectionControlName()]: ''
1411
+ }, { emitEvent: false });
1412
+ }
1413
+ }
1414
+ }, 100);
1415
+ setTimeout(() => clearInterval(sectionCheckInterval), 5000);
1416
+ }
1417
+ }
1418
+ }, 100);
1419
+ setTimeout(() => clearInterval(termCheckInterval), 5000);
1420
+ }
1421
+ }
1422
+ }, 100);
1423
+ setTimeout(() => clearInterval(branchCheckInterval), 5000);
1424
+ }
1425
+ else {
1426
+ // If no branch dropdown but term dropdown should be shown, load terms directly
1427
+ if (this.showTermDropdown()) {
1428
+ this.loadClassProgramTerms(classProgramId, undefined);
1429
+ // After terms load, check if current term value is valid
1430
+ const termCheckInterval = setInterval(() => {
1431
+ if (!this.classProgramTermsLoading()) {
1432
+ clearInterval(termCheckInterval);
1433
+ // Check if current term value exists in loaded terms
1434
+ const termExists = currentTermId &&
1435
+ this.classProgramTerms().some(t => t.value === currentTermId);
1436
+ if (!termExists && currentTermId) {
1437
+ // Current term value is not in the list, clear it
1438
+ form.patchValue({
1439
+ [this.termControlName()]: ''
1440
+ }, { emitEvent: false });
1441
+ // Also clear section if term is cleared
1442
+ form.patchValue({
1443
+ [this.sectionControlName()]: ''
1444
+ }, { emitEvent: false });
1445
+ }
1446
+ else if (termExists && currentSectionId) {
1447
+ // Term is valid, load sections
1448
+ this.loadProgramTermSections(currentTermId);
1449
+ // After sections load, check if current section value is valid
1450
+ const sectionCheckInterval = setInterval(() => {
1451
+ if (!this.programTermSectionsLoading()) {
1452
+ clearInterval(sectionCheckInterval);
1453
+ // Check if current section value exists in loaded sections
1454
+ const sectionExists = this.programTermSections().some(s => s.value === currentSectionId);
1455
+ if (!sectionExists) {
1456
+ // Current section value is not in the list, clear it
1457
+ form.patchValue({
1458
+ [this.sectionControlName()]: ''
1459
+ }, { emitEvent: false });
1460
+ }
1461
+ }
1462
+ }, 100);
1463
+ setTimeout(() => clearInterval(sectionCheckInterval), 5000);
1464
+ }
1465
+ }
1466
+ }, 100);
1467
+ setTimeout(() => clearInterval(termCheckInterval), 5000);
1468
+ }
1469
+ }
1470
+ }
1471
+ loadClassProgramBranches(classProgramId) {
1472
+ this.classProgramBranchesLoading.set(true);
1473
+ const payload = new AcaClassPrgBranchListPayload({
1474
+ acabrn_class_program_id_acacpm: classProgramId,
1475
+ pagination: false
1476
+ });
1477
+ const query = generateStringFromObject(payload);
1478
+ const url = cidePath.join([
1479
+ hostManagerRoutesUrl.cideSuiteHost,
1480
+ academicsRoutesUrl.module,
1481
+ academicsRoutesUrl.classProgramBranch,
1482
+ query
1483
+ ]);
1484
+ this.http.get(url).subscribe({
1485
+ next: (response) => {
1486
+ if (response?.success && response?.data) {
1487
+ const branches = Array.isArray(response.data) ? response.data : [];
1488
+ this.classProgramBranches.set(branches.map((branch) => ({
1489
+ value: branch._id || '',
1490
+ label: branch.acabrn_name || branch.acabrn_code || ''
1491
+ })));
1492
+ }
1493
+ else {
1494
+ this.classProgramBranches.set([]);
1495
+ }
1496
+ this.classProgramBranchesLoading.set(false);
1497
+ },
1498
+ error: (response) => {
1499
+ console.error('Error loading branches:', response);
1500
+ const errorMessage = response?.error?.message;
1501
+ if (errorMessage) {
1502
+ this.notificationService.error(errorMessage);
1503
+ }
1504
+ this.classProgramBranches.set([]);
1505
+ this.classProgramBranchesLoading.set(false);
1506
+ }
1507
+ });
1508
+ }
1509
+ onBranchChange(branchId) {
1510
+ this.classProgramTerms.set([]);
1511
+ this.programTermSections.set([]);
1512
+ const form = this.formGroup();
1513
+ if (form) {
1514
+ form.patchValue({
1515
+ [this.termControlName()]: '',
1516
+ [this.sectionControlName()]: ''
1517
+ }, { emitEvent: false });
1518
+ }
1519
+ const classProgramId = form?.get(this.classProgramControlName())?.value;
1520
+ if (classProgramId && this.showTermDropdown()) {
1521
+ this.loadClassProgramTerms(classProgramId, branchId || undefined);
1522
+ }
1523
+ }
1524
+ loadClassProgramTerms(classProgramId, branchId) {
1525
+ this.classProgramTermsLoading.set(true);
1526
+ const payload = {
1527
+ acapt_class_program_id_acacpm: classProgramId,
1528
+ acapt_class_prg_branch_acabrn: branchId || undefined,
1529
+ pagination: false
1530
+ };
1531
+ const query = generateStringFromObject(payload);
1532
+ const url = cidePath.join([
1533
+ hostManagerRoutesUrl.cideSuiteHost,
1534
+ academicsRoutesUrl.module,
1535
+ academicsRoutesUrl.classProgramTerm,
1536
+ query
1537
+ ]);
1538
+ this.http.get(url)
1539
+ .pipe(takeUntilDestroyed(this.destroyRef))
1540
+ .subscribe({
1541
+ next: (response) => {
1542
+ if (response?.success && response?.data) {
1543
+ const terms = Array.isArray(response.data) ? response.data : [];
1544
+ // Transform to SelectOption format with parent field for tree view
1545
+ const options = terms.map((term) => {
1546
+ // Normalize parent field for tree view compatibility
1547
+ let parentId = '';
1548
+ if (term.acapt_parent_class_prog_term_acapt) {
1549
+ if (typeof term.acapt_parent_class_prog_term_acapt === 'object') {
1550
+ parentId = term.acapt_parent_class_prog_term_acapt._id || '';
1551
+ }
1552
+ else if (typeof term.acapt_parent_class_prog_term_acapt === 'string') {
1553
+ parentId = term.acapt_parent_class_prog_term_acapt || '';
1554
+ }
1555
+ }
1556
+ return {
1557
+ value: term._id || '',
1558
+ label: term.acapt_name || term.acapt_code || '',
1559
+ _id: term._id || '',
1560
+ acapt_parent_class_prog_term_acapt: parentId
1561
+ };
1562
+ });
1563
+ this.classProgramTerms.set(options);
1564
+ }
1565
+ else {
1566
+ this.classProgramTerms.set([]);
1567
+ }
1568
+ this.classProgramTermsLoading.set(false);
1569
+ },
1570
+ error: (response) => {
1571
+ console.error('Error loading program terms:', response);
1572
+ const errorMessage = response?.error?.message;
1573
+ if (errorMessage) {
1574
+ this.notificationService.error(errorMessage);
1575
+ }
1576
+ this.classProgramTerms.set([]);
1577
+ this.classProgramTermsLoading.set(false);
1578
+ }
1579
+ });
1580
+ }
1581
+ onTermChange(termId) {
1582
+ this.programTermSections.set([]);
1583
+ const form = this.formGroup();
1584
+ if (form) {
1585
+ form.patchValue({
1586
+ [this.sectionControlName()]: ''
1587
+ }, { emitEvent: false });
1588
+ }
1589
+ if (termId) {
1590
+ this.loadProgramTermSections(termId);
1591
+ }
1592
+ else {
1593
+ this.hasSections.set(false);
1594
+ const sectionControl = form?.get(this.sectionControlName());
1595
+ if (sectionControl) {
1596
+ sectionControl.clearValidators();
1597
+ sectionControl.updateValueAndValidity();
1598
+ }
1599
+ }
1600
+ }
1601
+ loadProgramTermSections(termId) {
1602
+ this.programTermSectionsLoading.set(true);
1603
+ const payload = new MPrgTrmSection({
1604
+ acapts_parent_class_prog_term_acapt: termId,
1605
+ pagination: false,
1606
+ pageIndex: 0,
1607
+ pageSize: 1000,
1608
+ query: "",
1609
+ sort: {
1610
+ key: 'acapts_name',
1611
+ order: 'asc'
1612
+ }
1613
+ });
1614
+ const query = generateStringFromObject(payload);
1615
+ const url = cidePath.join([
1616
+ hostManagerRoutesUrl.cideSuiteHost,
1617
+ academicsRoutesUrl.module,
1618
+ academicsRoutesUrl.programTermSection,
1619
+ query
1620
+ ]);
1621
+ this.http.get(url)
1622
+ .pipe(takeUntilDestroyed(this.destroyRef))
1623
+ .subscribe({
1624
+ next: (response) => {
1625
+ if (response?.success && response?.data) {
1626
+ const sections = Array.isArray(response.data) ? response.data : [];
1627
+ const hasSections = sections.length > 0;
1628
+ this.hasSections.set(hasSections);
1629
+ if (hasSections) {
1630
+ this.programTermSections.set(sections.map(section => ({
1631
+ value: section._id || '',
1632
+ label: section.acapts_name || section.acapts_code || ''
1633
+ })));
1634
+ const form = this.formGroup();
1635
+ const sectionControl = form?.get(this.sectionControlName());
1636
+ if (sectionControl) {
1637
+ sectionControl.setValidators([Validators.required]);
1638
+ sectionControl.updateValueAndValidity();
1639
+ }
1640
+ }
1641
+ else {
1642
+ this.programTermSections.set([]);
1643
+ const form = this.formGroup();
1644
+ const sectionControl = form?.get(this.sectionControlName());
1645
+ if (sectionControl) {
1646
+ sectionControl.clearValidators();
1647
+ sectionControl.updateValueAndValidity();
1648
+ }
1649
+ }
1650
+ }
1651
+ else {
1652
+ this.hasSections.set(false);
1653
+ this.programTermSections.set([]);
1654
+ const form = this.formGroup();
1655
+ const sectionControl = form?.get(this.sectionControlName());
1656
+ if (sectionControl) {
1657
+ sectionControl.clearValidators();
1658
+ sectionControl.updateValueAndValidity();
1659
+ }
1660
+ }
1661
+ this.programTermSectionsLoading.set(false);
1662
+ },
1663
+ error: (response) => {
1664
+ console.error('Error loading sections:', response);
1665
+ const errorMessage = response?.error?.message;
1666
+ if (errorMessage) {
1667
+ this.notificationService.error(errorMessage);
1668
+ }
1669
+ this.hasSections.set(false);
1670
+ this.programTermSections.set([]);
1671
+ const form = this.formGroup();
1672
+ const sectionControl = form?.get(this.sectionControlName());
1673
+ if (sectionControl) {
1674
+ sectionControl.clearValidators();
1675
+ sectionControl.updateValueAndValidity();
1676
+ }
1677
+ this.programTermSectionsLoading.set(false);
1678
+ }
1679
+ });
1680
+ }
1681
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.7", ngImport: i0, type: CideSharedProgramSectionSelectorComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1682
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.1.7", type: CideSharedProgramSectionSelectorComponent, isStandalone: true, selector: "cide-shared-program-section-selector", inputs: { formGroup: { classPropertyName: "formGroup", publicName: "formGroup", isSignal: true, isRequired: true, transformFunction: null }, classProgramControlName: { classPropertyName: "classProgramControlName", publicName: "classProgramControlName", isSignal: true, isRequired: false, transformFunction: null }, branchControlName: { classPropertyName: "branchControlName", publicName: "branchControlName", isSignal: true, isRequired: false, transformFunction: null }, termControlName: { classPropertyName: "termControlName", publicName: "termControlName", isSignal: true, isRequired: false, transformFunction: null }, sectionControlName: { classPropertyName: "sectionControlName", publicName: "sectionControlName", isSignal: true, isRequired: false, transformFunction: null }, academicYearId: { classPropertyName: "academicYearId", publicName: "academicYearId", isSignal: true, isRequired: false, transformFunction: null }, entityId: { classPropertyName: "entityId", publicName: "entityId", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, showLabels: { classPropertyName: "showLabels", publicName: "showLabels", isSignal: true, isRequired: false, transformFunction: null }, gridCols: { classPropertyName: "gridCols", publicName: "gridCols", isSignal: true, isRequired: false, transformFunction: null }, showAllPrograms: { classPropertyName: "showAllPrograms", publicName: "showAllPrograms", isSignal: true, isRequired: false, transformFunction: null }, includeInactive: { classPropertyName: "includeInactive", publicName: "includeInactive", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { valuesChange: "valuesChange" }, ngImport: i0, template: "<div class=\"program-section-selector\" [ngClass]=\"gridCols()\">\r\n <!-- Class/Program Dropdown -->\r\n <cide-ele-select \r\n [label]=\"showLabels() ? 'Program/Class' : ''\" \r\n [formControl]=\"getClassProgramControl()\"\r\n [options]=\"classProgramOptions()\"\r\n [loading]=\"classProgramMastersLoading()\"\r\n [searchable]=\"true\"\r\n (searchChange)=\"onClassProgramSearch($event)\"\r\n placeholder=\"Search and select class/program\"\r\n [disabled]=\"disabled()\"\r\n size=\"sm\">\r\n </cide-ele-select>\r\n\r\n <!-- Branch/Specialization Dropdown (conditional) -->\r\n @if (showBranchDropdown()) {\r\n <cide-ele-select \r\n [label]=\"showLabels() ? 'Branch (Specialization)' : ''\" \r\n [formControl]=\"getBranchControl()\"\r\n [options]=\"classProgramBranches()\"\r\n [loading]=\"classProgramBranchesLoading()\"\r\n placeholder=\"Select branch\"\r\n [disabled]=\"disabled() || !formGroup().get(classProgramControlName())?.value\"\r\n size=\"sm\">\r\n </cide-ele-select>\r\n }\r\n\r\n <!-- Program Term Dropdown (conditional) -->\r\n @if (showTermDropdown()) {\r\n <cide-ele-select \r\n [label]=\"showLabels() ? 'Program Term' : ''\" \r\n [formControl]=\"getTermControl()\"\r\n [options]=\"classProgramTerms()\"\r\n [loading]=\"classProgramTermsLoading()\"\r\n placeholder=\"Select term/semester\"\r\n [disabled]=\"disabled() || !formGroup().get(classProgramControlName())?.value\"\r\n size=\"sm\"\r\n valueKey=\"_id\"\r\n labelKey=\"label\"\r\n [treeView]=\"{ enabled: true, primaryKey: '_id', foreignKey: 'acapt_parent_class_prog_term_acapt' }\">\r\n </cide-ele-select>\r\n }\r\n\r\n <!-- Section Dropdown (conditional) -->\r\n @if (hasSections()) {\r\n <cide-ele-select \r\n [label]=\"showLabels() ? 'Section' : ''\" \r\n [formControl]=\"getSectionControl()\"\r\n [options]=\"programTermSections()\"\r\n [loading]=\"programTermSectionsLoading()\"\r\n placeholder=\"Select section\"\r\n [disabled]=\"disabled() || !formGroup().get(termControlName())?.value\"\r\n size=\"sm\">\r\n </cide-ele-select>\r\n }\r\n</div>\r\n\r\n", styles: [".program-section-selector{display:grid;gap:1rem;width:100%;border:none;padding:0;margin:0;background:transparent}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "component", type: CideSelectComponent, selector: "cide-ele-select", inputs: ["label", "labelHide", "placeholder", "helperText", "errorText", "required", "disabled", "id", "size", "fill", "labelPlacement", "labelDir", "leadingIcon", "trailingIcon", "clearInput", "options", "multiple", "searchable", "showSearchInput", "loading", "valueKey", "labelKey", "treeView"], outputs: ["ngModelChange", "change", "searchChange"] }] });
1683
+ }
1684
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.7", ngImport: i0, type: CideSharedProgramSectionSelectorComponent, decorators: [{
1685
+ type: Component,
1686
+ args: [{ selector: 'cide-shared-program-section-selector', standalone: true, imports: [
1687
+ CommonModule,
1688
+ ReactiveFormsModule,
1689
+ CideSelectComponent,
1690
+ NgClass
1691
+ ], template: "<div class=\"program-section-selector\" [ngClass]=\"gridCols()\">\r\n <!-- Class/Program Dropdown -->\r\n <cide-ele-select \r\n [label]=\"showLabels() ? 'Program/Class' : ''\" \r\n [formControl]=\"getClassProgramControl()\"\r\n [options]=\"classProgramOptions()\"\r\n [loading]=\"classProgramMastersLoading()\"\r\n [searchable]=\"true\"\r\n (searchChange)=\"onClassProgramSearch($event)\"\r\n placeholder=\"Search and select class/program\"\r\n [disabled]=\"disabled()\"\r\n size=\"sm\">\r\n </cide-ele-select>\r\n\r\n <!-- Branch/Specialization Dropdown (conditional) -->\r\n @if (showBranchDropdown()) {\r\n <cide-ele-select \r\n [label]=\"showLabels() ? 'Branch (Specialization)' : ''\" \r\n [formControl]=\"getBranchControl()\"\r\n [options]=\"classProgramBranches()\"\r\n [loading]=\"classProgramBranchesLoading()\"\r\n placeholder=\"Select branch\"\r\n [disabled]=\"disabled() || !formGroup().get(classProgramControlName())?.value\"\r\n size=\"sm\">\r\n </cide-ele-select>\r\n }\r\n\r\n <!-- Program Term Dropdown (conditional) -->\r\n @if (showTermDropdown()) {\r\n <cide-ele-select \r\n [label]=\"showLabels() ? 'Program Term' : ''\" \r\n [formControl]=\"getTermControl()\"\r\n [options]=\"classProgramTerms()\"\r\n [loading]=\"classProgramTermsLoading()\"\r\n placeholder=\"Select term/semester\"\r\n [disabled]=\"disabled() || !formGroup().get(classProgramControlName())?.value\"\r\n size=\"sm\"\r\n valueKey=\"_id\"\r\n labelKey=\"label\"\r\n [treeView]=\"{ enabled: true, primaryKey: '_id', foreignKey: 'acapt_parent_class_prog_term_acapt' }\">\r\n </cide-ele-select>\r\n }\r\n\r\n <!-- Section Dropdown (conditional) -->\r\n @if (hasSections()) {\r\n <cide-ele-select \r\n [label]=\"showLabels() ? 'Section' : ''\" \r\n [formControl]=\"getSectionControl()\"\r\n [options]=\"programTermSections()\"\r\n [loading]=\"programTermSectionsLoading()\"\r\n placeholder=\"Select section\"\r\n [disabled]=\"disabled() || !formGroup().get(termControlName())?.value\"\r\n size=\"sm\">\r\n </cide-ele-select>\r\n }\r\n</div>\r\n\r\n", styles: [".program-section-selector{display:grid;gap:1rem;width:100%;border:none;padding:0;margin:0;background:transparent}\n"] }]
1692
+ }], ctorParameters: () => [] });
1693
+
885
1694
  /*
886
1695
  * Public API Surface of cloud-ide-shared
887
1696
  */
@@ -890,5 +1699,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.7", ngImpor
890
1699
  * Generated bundle index. Do not edit.
891
1700
  */
892
1701
 
893
- export { ACADEMIC_YEAR_SERVICE_TOKEN, APP_STATE_SERVICE_TOKEN, AUTH_SERVICE_TOKEN, CLASS_PROGRAM_MASTER_SERVICE_TOKEN, COUNTRY_SERVICE_TOKEN, CideCoreGeneralMasterService, CideCoreGeneralMasterTypeService, CideSharedOrgStructureComponent, CloudIdeShared, ENTITY_SERVICE_TOKEN, FINANCIAL_YEAR_SERVICE_TOKEN, NATIONALITY_SERVICE_TOKEN, PIN_CODE_SERVICE_TOKEN, SharedObjectIdService, USER_SERVICE_TOKEN, authGuard };
1702
+ export { ACADEMIC_YEAR_SERVICE_TOKEN, APP_STATE_SERVICE_TOKEN, AUTH_SERVICE_TOKEN, CLASS_PROGRAM_MASTER_SERVICE_TOKEN, COUNTRY_SERVICE_TOKEN, CideCoreGeneralMasterService, CideCoreGeneralMasterTypeService, CideSharedOrgStructureComponent, CideSharedProgramSectionSelectorComponent, CloudIdeShared, ENTITY_SERVICE_TOKEN, FEE_PAYMENT_SERVICE_TOKEN, FEE_STRUCTURE_SERVICE_TOKEN, FINANCIAL_YEAR_SERVICE_TOKEN, NATIONALITY_SERVICE_TOKEN, PIN_CODE_SERVICE_TOKEN, PROGRAM_TERM_SECTION_SERVICE_TOKEN, SharedObjectIdService, USER_SERVICE_TOKEN, authGuard };
894
1703
  //# sourceMappingURL=cloud-ide-shared.mjs.map