cloud-ide-shared 1.0.32 → 1.0.33

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 });
@@ -722,6 +725,37 @@ const FINANCIAL_YEAR_SERVICE_TOKEN = new InjectionToken('CideAccFinancialYearSer
722
725
  */
723
726
  const ACADEMIC_YEAR_SERVICE_TOKEN = new InjectionToken('CideLytAcademicYearService');
724
727
 
728
+ /**
729
+ * Injection token for Fee Structure Service (CideFeeFeeStructureService)
730
+ * Used to provide and inject fee structure service implementations
731
+ * This enables dependency injection with interface-based contracts
732
+ *
733
+ * @example
734
+ * ```typescript
735
+ * // In app.config.ts
736
+ * { provide: FEE_STRUCTURE_SERVICE_TOKEN, useExisting: CideFeeFeeStructureService }
737
+ *
738
+ * // In component
739
+ * private feeStructureService = inject(FEE_STRUCTURE_SERVICE_TOKEN);
740
+ * ```
741
+ */
742
+ const FEE_STRUCTURE_SERVICE_TOKEN = new InjectionToken('CideFeeFeeStructureService');
743
+ /**
744
+ * Injection token for Fee Payment Service (CideFeeFeePaymentService)
745
+ * Used to provide and inject fee payment service implementations
746
+ * This enables dependency injection with interface-based contracts
747
+ *
748
+ * @example
749
+ * ```typescript
750
+ * // In app.config.ts
751
+ * { provide: FEE_PAYMENT_SERVICE_TOKEN, useExisting: CideFeeFeePaymentService }
752
+ *
753
+ * // In component
754
+ * private feePaymentService = inject(FEE_PAYMENT_SERVICE_TOKEN);
755
+ * ```
756
+ */
757
+ const FEE_PAYMENT_SERVICE_TOKEN = new InjectionToken('CideFeeFeePaymentService');
758
+
725
759
  class CideSharedOrgStructureComponent {
726
760
  // Input parameters for configuration
727
761
  allowSwitching = input(true, ...(ngDevMode ? [{ debugName: "allowSwitching" }] : [])); // Allow entity switching (default: true)
@@ -870,7 +904,7 @@ class CideSharedOrgStructureComponent {
870
904
  });
871
905
  }
872
906
  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"] }] });
907
+ 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
908
  }
875
909
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.7", ngImport: i0, type: CideSharedOrgStructureComponent, decorators: [{
876
910
  type: Component,
@@ -878,10 +912,769 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.7", ngImpor
878
912
  CommonModule,
879
913
  CideEleButtonComponent,
880
914
  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"] }]
915
+ CideSpinnerComponent,
916
+ TooltipDirective
917
+ ], 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
918
  }] });
884
919
 
920
+ class CideSharedProgramSectionSelectorComponent {
921
+ // Inputs
922
+ formGroup = input.required(...(ngDevMode ? [{ debugName: "formGroup" }] : []));
923
+ classProgramControlName = input('class_program_id', ...(ngDevMode ? [{ debugName: "classProgramControlName" }] : []));
924
+ branchControlName = input('branch_id', ...(ngDevMode ? [{ debugName: "branchControlName" }] : []));
925
+ termControlName = input('term_id', ...(ngDevMode ? [{ debugName: "termControlName" }] : []));
926
+ sectionControlName = input('section_id', ...(ngDevMode ? [{ debugName: "sectionControlName" }] : []));
927
+ academicYearId = input(null, ...(ngDevMode ? [{ debugName: "academicYearId" }] : []));
928
+ entityId = input(null, ...(ngDevMode ? [{ debugName: "entityId" }] : []));
929
+ disabled = input(false, ...(ngDevMode ? [{ debugName: "disabled" }] : []));
930
+ showLabels = input(true, ...(ngDevMode ? [{ debugName: "showLabels" }] : []));
931
+ gridCols = input('tw-grid-cols-1 md:tw-grid-cols-3', ...(ngDevMode ? [{ debugName: "gridCols" }] : []));
932
+ showAllPrograms = input(false, ...(ngDevMode ? [{ debugName: "showAllPrograms" }] : [])); // If true, shows all programs regardless of academic year/entity filters
933
+ includeInactive = input(false, ...(ngDevMode ? [{ debugName: "includeInactive" }] : [])); // If true, includes inactive programs
934
+ // Outputs
935
+ valuesChange = output();
936
+ // Services
937
+ destroyRef = inject(DestroyRef);
938
+ http = inject(HttpClient);
939
+ classProgramService = inject(CLASS_PROGRAM_MASTER_SERVICE_TOKEN);
940
+ notificationService = inject(NotificationService);
941
+ // State
942
+ classProgramMasters = signal([], ...(ngDevMode ? [{ debugName: "classProgramMasters" }] : []));
943
+ classProgramBranches = signal([], ...(ngDevMode ? [{ debugName: "classProgramBranches" }] : []));
944
+ classProgramTerms = signal([], ...(ngDevMode ? [{ debugName: "classProgramTerms" }] : []));
945
+ programTermSections = signal([], ...(ngDevMode ? [{ debugName: "programTermSections" }] : []));
946
+ classProgramMastersLoading = signal(false, ...(ngDevMode ? [{ debugName: "classProgramMastersLoading" }] : []));
947
+ classProgramBranchesLoading = signal(false, ...(ngDevMode ? [{ debugName: "classProgramBranchesLoading" }] : []));
948
+ classProgramTermsLoading = signal(false, ...(ngDevMode ? [{ debugName: "classProgramTermsLoading" }] : []));
949
+ programTermSectionsLoading = signal(false, ...(ngDevMode ? [{ debugName: "programTermSectionsLoading" }] : []));
950
+ selectedClassProgram = signal(null, ...(ngDevMode ? [{ debugName: "selectedClassProgram" }] : []));
951
+ hasSections = signal(false, ...(ngDevMode ? [{ debugName: "hasSections" }] : []));
952
+ isProcessingInitialValues = false;
953
+ // FormControl getters
954
+ getClassProgramControl() {
955
+ return this.formGroup().get(this.classProgramControlName());
956
+ }
957
+ getBranchControl() {
958
+ return this.formGroup().get(this.branchControlName());
959
+ }
960
+ getTermControl() {
961
+ return this.formGroup().get(this.termControlName());
962
+ }
963
+ getSectionControl() {
964
+ return this.formGroup().get(this.sectionControlName());
965
+ }
966
+ // Computed signals
967
+ classProgramOptions = computed(() => {
968
+ return this.classProgramMasters().map(cp => ({
969
+ value: cp._id || '',
970
+ label: cp.acacpm_alise_title || ''
971
+ }));
972
+ }, ...(ngDevMode ? [{ debugName: "classProgramOptions" }] : []));
973
+ showBranchDropdown = computed(() => {
974
+ const program = this.selectedClassProgram();
975
+ if (!program)
976
+ return false;
977
+ const generalMaster = program.acacpm_class_program_id_sygms || program.acacpm_class_program_id_sygmt;
978
+ if (!generalMaster || typeof generalMaster !== 'object')
979
+ return false;
980
+ let config;
981
+ if ('sygms_configuration' in generalMaster) {
982
+ const configValue = generalMaster.sygms_configuration;
983
+ if (typeof configValue === 'string') {
984
+ try {
985
+ config = JSON.parse(configValue);
986
+ }
987
+ catch {
988
+ return false;
989
+ }
990
+ }
991
+ else if (typeof configValue === 'object' && configValue !== null) {
992
+ config = configValue;
993
+ }
994
+ }
995
+ return config?.branchType === true;
996
+ }, ...(ngDevMode ? [{ debugName: "showBranchDropdown" }] : []));
997
+ showTermDropdown = computed(() => {
998
+ const program = this.selectedClassProgram();
999
+ if (!program)
1000
+ return false;
1001
+ const generalMaster = program.acacpm_class_program_id_sygms || program.acacpm_class_program_id_sygmt;
1002
+ if (!generalMaster || typeof generalMaster !== 'object')
1003
+ return false;
1004
+ let config;
1005
+ if ('sygms_configuration' in generalMaster) {
1006
+ const configValue = generalMaster.sygms_configuration;
1007
+ if (typeof configValue === 'string') {
1008
+ try {
1009
+ config = JSON.parse(configValue);
1010
+ }
1011
+ catch {
1012
+ return false;
1013
+ }
1014
+ }
1015
+ else if (typeof configValue === 'object' && configValue !== null) {
1016
+ config = configValue;
1017
+ }
1018
+ }
1019
+ // If termType is explicitly false, don't show the dropdown
1020
+ if (config?.termType === false) {
1021
+ return false;
1022
+ }
1023
+ // Special case: Hide dropdown if termType=true, termLevelUpTo=1, and termProgramLevelOneConnected=true
1024
+ if (config?.termType === true &&
1025
+ config?.termLevelUpTo === 1 &&
1026
+ config?.termProgramLevelOneConnected === true) {
1027
+ return false;
1028
+ }
1029
+ // Check for both termType (current) and semesterType (legacy) for backward compatibility
1030
+ return config?.termType === true || config?.semesterType === true;
1031
+ }, ...(ngDevMode ? [{ debugName: "showTermDropdown" }] : []));
1032
+ constructor() {
1033
+ // Effect to emit values when form changes
1034
+ effect(() => {
1035
+ const form = this.formGroup();
1036
+ if (form) {
1037
+ const classProgramId = form.get(this.classProgramControlName())?.value;
1038
+ const branchId = form.get(this.branchControlName())?.value;
1039
+ const termId = form.get(this.termControlName())?.value;
1040
+ const sectionId = form.get(this.sectionControlName())?.value;
1041
+ this.valuesChange.emit({
1042
+ classProgramId: classProgramId || null,
1043
+ branchId: branchId || null,
1044
+ termId: termId || null,
1045
+ sectionId: sectionId || null
1046
+ });
1047
+ }
1048
+ });
1049
+ }
1050
+ ngOnInit() {
1051
+ this.loadClassProgramMasters();
1052
+ this.setupFormListeners();
1053
+ // Check for initial values in the form and load cascading options
1054
+ // Use setTimeout to ensure form is ready
1055
+ setTimeout(() => {
1056
+ this.handleInitialValues();
1057
+ }, 0);
1058
+ // Also watch for form value changes to handle values set after component initialization
1059
+ const form = this.formGroup();
1060
+ if (form) {
1061
+ // Watch for class program value being set
1062
+ form.get(this.classProgramControlName())?.valueChanges
1063
+ .pipe(takeUntilDestroyed(this.destroyRef))
1064
+ .subscribe(() => {
1065
+ // If class program is set but we haven't processed initial values yet, process them
1066
+ const classProgramId = form.get(this.classProgramControlName())?.value;
1067
+ if (classProgramId && this.classProgramMasters().length > 0 && !this.selectedClassProgram()) {
1068
+ // Form value was set, but we haven't processed it yet - process now
1069
+ setTimeout(() => {
1070
+ this.handleInitialValues();
1071
+ }, 100);
1072
+ }
1073
+ });
1074
+ }
1075
+ }
1076
+ handleInitialValues() {
1077
+ const form = this.formGroup();
1078
+ if (!form)
1079
+ return;
1080
+ const classProgramId = form.get(this.classProgramControlName())?.value;
1081
+ if (!classProgramId)
1082
+ return;
1083
+ // Set flag to prevent clearing values during initial load
1084
+ this.isProcessingInitialValues = true;
1085
+ // Wait for class programs to load, then process initial values
1086
+ const checkInterval = setInterval(() => {
1087
+ if (this.classProgramMasters().length > 0) {
1088
+ clearInterval(checkInterval);
1089
+ // Find the selected program
1090
+ const selectedProgram = this.classProgramMasters().find(cp => cp._id === classProgramId);
1091
+ if (selectedProgram) {
1092
+ this.selectedClassProgram.set(selectedProgram);
1093
+ // Read config directly from the program to determine what to show
1094
+ const generalMaster = selectedProgram.acacpm_class_program_id_sygms || selectedProgram.acacpm_class_program_id_sygmt;
1095
+ let config;
1096
+ if (generalMaster && typeof generalMaster === 'object') {
1097
+ if ('sygms_configuration' in generalMaster) {
1098
+ const configValue = generalMaster.sygms_configuration;
1099
+ if (typeof configValue === 'string') {
1100
+ try {
1101
+ config = JSON.parse(configValue);
1102
+ }
1103
+ catch {
1104
+ console.warn('Failed to parse config:', configValue);
1105
+ }
1106
+ }
1107
+ else if (typeof configValue === 'object' && configValue !== null) {
1108
+ config = configValue;
1109
+ }
1110
+ }
1111
+ }
1112
+ // Wait a tick for computed signals to update
1113
+ setTimeout(() => {
1114
+ const branchId = form.get(this.branchControlName())?.value;
1115
+ const termId = form.get(this.termControlName())?.value;
1116
+ const sectionId = form.get(this.sectionControlName())?.value;
1117
+ // Load branches if config says so AND we have a branch ID
1118
+ const shouldShowBranch = config?.branchType === true;
1119
+ if (shouldShowBranch) {
1120
+ this.loadClassProgramBranches(classProgramId);
1121
+ // Wait for branches to load
1122
+ if (branchId) {
1123
+ const branchCheckInterval = setInterval(() => {
1124
+ if (this.classProgramBranches().length > 0 || !this.classProgramBranchesLoading()) {
1125
+ clearInterval(branchCheckInterval);
1126
+ // Branch value is already set, now load terms
1127
+ this.loadClassProgramTerms(classProgramId, branchId);
1128
+ // Wait for terms to load
1129
+ if (termId) {
1130
+ const termCheckInterval = setInterval(() => {
1131
+ if (this.classProgramTerms().length > 0 || !this.classProgramTermsLoading()) {
1132
+ clearInterval(termCheckInterval);
1133
+ // Term value is already set, now load sections
1134
+ if (sectionId) {
1135
+ this.loadProgramTermSections(termId);
1136
+ }
1137
+ }
1138
+ }, 100);
1139
+ setTimeout(() => clearInterval(termCheckInterval), 5000);
1140
+ }
1141
+ }
1142
+ }, 100);
1143
+ setTimeout(() => clearInterval(branchCheckInterval), 5000);
1144
+ }
1145
+ else {
1146
+ // No branch ID but should show branch dropdown - load terms after branches load
1147
+ const branchCheckInterval = setInterval(() => {
1148
+ if (!this.classProgramBranchesLoading()) {
1149
+ clearInterval(branchCheckInterval);
1150
+ if (termId) {
1151
+ this.loadClassProgramTerms(classProgramId, undefined);
1152
+ if (sectionId) {
1153
+ const termCheckInterval = setInterval(() => {
1154
+ if (this.classProgramTerms().length > 0 || !this.classProgramTermsLoading()) {
1155
+ clearInterval(termCheckInterval);
1156
+ this.loadProgramTermSections(termId);
1157
+ }
1158
+ }, 100);
1159
+ setTimeout(() => clearInterval(termCheckInterval), 5000);
1160
+ }
1161
+ }
1162
+ }
1163
+ }, 100);
1164
+ setTimeout(() => clearInterval(branchCheckInterval), 5000);
1165
+ }
1166
+ }
1167
+ else {
1168
+ // No branch dropdown, load terms directly if config allows
1169
+ const shouldShowTerm = config?.termType === true || config?.semesterType === true;
1170
+ if (shouldShowTerm && termId) {
1171
+ this.loadClassProgramTerms(classProgramId, undefined);
1172
+ // Wait for terms to load, then load sections
1173
+ if (sectionId) {
1174
+ const termCheckInterval = setInterval(() => {
1175
+ if (this.classProgramTerms().length > 0 || !this.classProgramTermsLoading()) {
1176
+ clearInterval(termCheckInterval);
1177
+ this.loadProgramTermSections(termId);
1178
+ }
1179
+ }, 100);
1180
+ setTimeout(() => clearInterval(termCheckInterval), 5000);
1181
+ }
1182
+ }
1183
+ }
1184
+ }, 0);
1185
+ }
1186
+ }
1187
+ }, 100);
1188
+ // Timeout after 5 seconds
1189
+ setTimeout(() => {
1190
+ clearInterval(checkInterval);
1191
+ // Reset flag after processing is complete
1192
+ this.isProcessingInitialValues = false;
1193
+ }, 5000);
1194
+ }
1195
+ ngOnDestroy() {
1196
+ // Cleanup handled by takeUntilDestroyed
1197
+ }
1198
+ setupFormListeners() {
1199
+ const form = this.formGroup();
1200
+ if (!form)
1201
+ return;
1202
+ // Listen for class program selection changes
1203
+ form.get(this.classProgramControlName())?.valueChanges
1204
+ .pipe(takeUntilDestroyed(this.destroyRef))
1205
+ .subscribe(classProgramId => {
1206
+ this.onClassProgramChange(classProgramId);
1207
+ });
1208
+ // Listen for branch selection changes
1209
+ form.get(this.branchControlName())?.valueChanges
1210
+ .pipe(takeUntilDestroyed(this.destroyRef))
1211
+ .subscribe(branchId => {
1212
+ this.onBranchChange(branchId);
1213
+ });
1214
+ // Listen for term selection changes
1215
+ form.get(this.termControlName())?.valueChanges
1216
+ .pipe(takeUntilDestroyed(this.destroyRef))
1217
+ .subscribe(termId => {
1218
+ this.onTermChange(termId);
1219
+ });
1220
+ }
1221
+ loadClassProgramMasters() {
1222
+ this.classProgramMastersLoading.set(true);
1223
+ const academicYearId = this.academicYearId();
1224
+ const entityId = this.entityId();
1225
+ const showAll = this.showAllPrograms();
1226
+ const includeInactive = this.includeInactive();
1227
+ // Build payload - only include filters if not showing all programs
1228
+ const payload = {
1229
+ pagination: false
1230
+ };
1231
+ // Only add academic year filter if not showing all programs and academic year is provided
1232
+ if (!showAll && academicYearId) {
1233
+ payload['acacpm_academic_year_id_acayr'] = academicYearId;
1234
+ }
1235
+ // Only add entity filter if not showing all programs and entity is provided
1236
+ if (!showAll && entityId) {
1237
+ payload['acacpm_entity_id_syen'] = entityId;
1238
+ }
1239
+ // Only add active filter if not including inactive programs
1240
+ if (!includeInactive) {
1241
+ payload['acacpm_isactive'] = true;
1242
+ }
1243
+ this.classProgramService.searchClassProgramMasters(payload).subscribe({
1244
+ next: (response) => {
1245
+ if (response?.success && response.data) {
1246
+ const dataArray = Array.isArray(response.data) ? response.data : [];
1247
+ this.classProgramMasters.set(dataArray);
1248
+ console.log(`✅ Loaded ${dataArray.length} class programs`, {
1249
+ showAll,
1250
+ includeInactive,
1251
+ academicYearId,
1252
+ entityId,
1253
+ filters: payload
1254
+ });
1255
+ }
1256
+ else {
1257
+ this.classProgramMasters.set([]);
1258
+ console.warn('⚠️ No class programs found', response);
1259
+ }
1260
+ this.classProgramMastersLoading.set(false);
1261
+ },
1262
+ error: (response) => {
1263
+ console.error('❌ Error loading class programs:', response);
1264
+ const errorMessage = response?.error?.message;
1265
+ if (errorMessage) {
1266
+ this.notificationService.error(errorMessage);
1267
+ }
1268
+ this.classProgramMasters.set([]);
1269
+ this.classProgramMastersLoading.set(false);
1270
+ }
1271
+ });
1272
+ }
1273
+ onClassProgramSearch(event) {
1274
+ this.classProgramMastersLoading.set(true);
1275
+ const academicYearId = this.academicYearId();
1276
+ const entityId = this.entityId();
1277
+ const showAll = this.showAllPrograms();
1278
+ const includeInactive = this.includeInactive();
1279
+ // Build payload for search
1280
+ const payload = {
1281
+ query: event.query || "",
1282
+ acacpm_id: event.value ? String(event.value) : "",
1283
+ pagination: false
1284
+ };
1285
+ // Only add academic year filter if not showing all programs and academic year is provided
1286
+ if (!showAll && academicYearId) {
1287
+ payload['acacpm_academic_year_id_acayr'] = academicYearId;
1288
+ }
1289
+ // Only add entity filter if not showing all programs and entity is provided
1290
+ if (!showAll && entityId) {
1291
+ payload['acacpm_entity_id_syen'] = entityId;
1292
+ }
1293
+ // Only add active filter if not including inactive programs
1294
+ if (!includeInactive) {
1295
+ payload['acacpm_isactive'] = true;
1296
+ }
1297
+ this.classProgramService.searchClassProgramMasters(payload).subscribe({
1298
+ next: (response) => {
1299
+ if (response?.success && response.data) {
1300
+ const dataArray = Array.isArray(response.data) ? response.data : [];
1301
+ this.classProgramMasters.set(dataArray);
1302
+ }
1303
+ else {
1304
+ this.classProgramMasters.set([]);
1305
+ }
1306
+ this.classProgramMastersLoading.set(false);
1307
+ },
1308
+ error: () => {
1309
+ this.classProgramMasters.set([]);
1310
+ this.classProgramMastersLoading.set(false);
1311
+ }
1312
+ });
1313
+ }
1314
+ onClassProgramChange(classProgramId) {
1315
+ // Clear previous selections state
1316
+ this.selectedClassProgram.set(null);
1317
+ this.classProgramBranches.set([]);
1318
+ this.classProgramTerms.set([]);
1319
+ this.programTermSections.set([]);
1320
+ if (!classProgramId) {
1321
+ // Clear all dependent values if no class program selected
1322
+ const form = this.formGroup();
1323
+ if (form) {
1324
+ form.patchValue({
1325
+ [this.branchControlName()]: '',
1326
+ [this.termControlName()]: '',
1327
+ [this.sectionControlName()]: ''
1328
+ }, { emitEvent: false });
1329
+ }
1330
+ return;
1331
+ }
1332
+ // Find the selected program from the list
1333
+ const selectedProgram = this.classProgramMasters().find(cp => cp._id === classProgramId);
1334
+ if (!selectedProgram) {
1335
+ return;
1336
+ }
1337
+ this.selectedClassProgram.set(selectedProgram);
1338
+ const form = this.formGroup();
1339
+ if (!form)
1340
+ return;
1341
+ // Get current form values before loading options
1342
+ const currentBranchId = form.get(this.branchControlName())?.value;
1343
+ const currentTermId = form.get(this.termControlName())?.value;
1344
+ const currentSectionId = form.get(this.sectionControlName())?.value;
1345
+ // Load branches if branchType is true
1346
+ if (this.showBranchDropdown()) {
1347
+ this.loadClassProgramBranches(classProgramId);
1348
+ // After branches load, check if current branch value is valid
1349
+ const branchCheckInterval = setInterval(() => {
1350
+ if (!this.classProgramBranchesLoading()) {
1351
+ clearInterval(branchCheckInterval);
1352
+ // Check if current branch value exists in loaded branches
1353
+ const branchExists = currentBranchId &&
1354
+ this.classProgramBranches().some(b => b.value === currentBranchId);
1355
+ if (!branchExists && currentBranchId) {
1356
+ // Current branch value is not in the list, clear it
1357
+ form.patchValue({
1358
+ [this.branchControlName()]: ''
1359
+ }, { emitEvent: false });
1360
+ }
1361
+ // Load terms with the branch (or without if branch was cleared)
1362
+ const branchIdToUse = branchExists ? currentBranchId : undefined;
1363
+ if (this.showTermDropdown()) {
1364
+ this.loadClassProgramTerms(classProgramId, branchIdToUse);
1365
+ // After terms load, check if current term value is valid
1366
+ const termCheckInterval = setInterval(() => {
1367
+ if (!this.classProgramTermsLoading()) {
1368
+ clearInterval(termCheckInterval);
1369
+ // Check if current term value exists in loaded terms
1370
+ const termExists = currentTermId &&
1371
+ this.classProgramTerms().some(t => t.value === currentTermId);
1372
+ if (!termExists && currentTermId) {
1373
+ // Current term value is not in the list, clear it
1374
+ form.patchValue({
1375
+ [this.termControlName()]: ''
1376
+ }, { emitEvent: false });
1377
+ // Also clear section if term is cleared
1378
+ form.patchValue({
1379
+ [this.sectionControlName()]: ''
1380
+ }, { emitEvent: false });
1381
+ }
1382
+ else if (termExists && currentSectionId) {
1383
+ // Term is valid, load sections
1384
+ this.loadProgramTermSections(currentTermId);
1385
+ // After sections load, check if current section value is valid
1386
+ const sectionCheckInterval = setInterval(() => {
1387
+ if (!this.programTermSectionsLoading()) {
1388
+ clearInterval(sectionCheckInterval);
1389
+ // Check if current section value exists in loaded sections
1390
+ const sectionExists = this.programTermSections().some(s => s.value === currentSectionId);
1391
+ if (!sectionExists) {
1392
+ // Current section value is not in the list, clear it
1393
+ form.patchValue({
1394
+ [this.sectionControlName()]: ''
1395
+ }, { emitEvent: false });
1396
+ }
1397
+ }
1398
+ }, 100);
1399
+ setTimeout(() => clearInterval(sectionCheckInterval), 5000);
1400
+ }
1401
+ }
1402
+ }, 100);
1403
+ setTimeout(() => clearInterval(termCheckInterval), 5000);
1404
+ }
1405
+ }
1406
+ }, 100);
1407
+ setTimeout(() => clearInterval(branchCheckInterval), 5000);
1408
+ }
1409
+ else {
1410
+ // If no branch dropdown but term dropdown should be shown, load terms directly
1411
+ if (this.showTermDropdown()) {
1412
+ this.loadClassProgramTerms(classProgramId, undefined);
1413
+ // After terms load, check if current term value is valid
1414
+ const termCheckInterval = setInterval(() => {
1415
+ if (!this.classProgramTermsLoading()) {
1416
+ clearInterval(termCheckInterval);
1417
+ // Check if current term value exists in loaded terms
1418
+ const termExists = currentTermId &&
1419
+ this.classProgramTerms().some(t => t.value === currentTermId);
1420
+ if (!termExists && currentTermId) {
1421
+ // Current term value is not in the list, clear it
1422
+ form.patchValue({
1423
+ [this.termControlName()]: ''
1424
+ }, { emitEvent: false });
1425
+ // Also clear section if term is cleared
1426
+ form.patchValue({
1427
+ [this.sectionControlName()]: ''
1428
+ }, { emitEvent: false });
1429
+ }
1430
+ else if (termExists && currentSectionId) {
1431
+ // Term is valid, load sections
1432
+ this.loadProgramTermSections(currentTermId);
1433
+ // After sections load, check if current section value is valid
1434
+ const sectionCheckInterval = setInterval(() => {
1435
+ if (!this.programTermSectionsLoading()) {
1436
+ clearInterval(sectionCheckInterval);
1437
+ // Check if current section value exists in loaded sections
1438
+ const sectionExists = this.programTermSections().some(s => s.value === currentSectionId);
1439
+ if (!sectionExists) {
1440
+ // Current section value is not in the list, clear it
1441
+ form.patchValue({
1442
+ [this.sectionControlName()]: ''
1443
+ }, { emitEvent: false });
1444
+ }
1445
+ }
1446
+ }, 100);
1447
+ setTimeout(() => clearInterval(sectionCheckInterval), 5000);
1448
+ }
1449
+ }
1450
+ }, 100);
1451
+ setTimeout(() => clearInterval(termCheckInterval), 5000);
1452
+ }
1453
+ }
1454
+ }
1455
+ loadClassProgramBranches(classProgramId) {
1456
+ this.classProgramBranchesLoading.set(true);
1457
+ const payload = new AcaClassPrgBranchListPayload({
1458
+ acabrn_class_program_id_acacpm: classProgramId,
1459
+ pagination: false
1460
+ });
1461
+ const query = generateStringFromObject(payload);
1462
+ const url = cidePath.join([
1463
+ hostManagerRoutesUrl.cideSuiteHost,
1464
+ academicsRoutesUrl.module,
1465
+ academicsRoutesUrl.classProgramBranch,
1466
+ query
1467
+ ]);
1468
+ this.http.get(url).subscribe({
1469
+ next: (response) => {
1470
+ if (response?.success && response?.data) {
1471
+ const branches = Array.isArray(response.data) ? response.data : [];
1472
+ this.classProgramBranches.set(branches.map((branch) => ({
1473
+ value: branch._id || '',
1474
+ label: branch.acabrn_name || branch.acabrn_code || ''
1475
+ })));
1476
+ }
1477
+ else {
1478
+ this.classProgramBranches.set([]);
1479
+ }
1480
+ this.classProgramBranchesLoading.set(false);
1481
+ },
1482
+ error: (response) => {
1483
+ console.error('Error loading branches:', response);
1484
+ const errorMessage = response?.error?.message;
1485
+ if (errorMessage) {
1486
+ this.notificationService.error(errorMessage);
1487
+ }
1488
+ this.classProgramBranches.set([]);
1489
+ this.classProgramBranchesLoading.set(false);
1490
+ }
1491
+ });
1492
+ }
1493
+ onBranchChange(branchId) {
1494
+ this.classProgramTerms.set([]);
1495
+ this.programTermSections.set([]);
1496
+ const form = this.formGroup();
1497
+ if (form) {
1498
+ form.patchValue({
1499
+ [this.termControlName()]: '',
1500
+ [this.sectionControlName()]: ''
1501
+ }, { emitEvent: false });
1502
+ }
1503
+ const classProgramId = form?.get(this.classProgramControlName())?.value;
1504
+ if (classProgramId && this.showTermDropdown()) {
1505
+ this.loadClassProgramTerms(classProgramId, branchId || undefined);
1506
+ }
1507
+ }
1508
+ loadClassProgramTerms(classProgramId, branchId) {
1509
+ this.classProgramTermsLoading.set(true);
1510
+ const payload = {
1511
+ acapt_class_program_id_acacpm: classProgramId,
1512
+ acapt_class_prg_branch_acabrn: branchId || undefined,
1513
+ pagination: false
1514
+ };
1515
+ const query = generateStringFromObject(payload);
1516
+ const url = cidePath.join([
1517
+ hostManagerRoutesUrl.cideSuiteHost,
1518
+ academicsRoutesUrl.module,
1519
+ academicsRoutesUrl.classProgramTerm,
1520
+ query
1521
+ ]);
1522
+ this.http.get(url)
1523
+ .pipe(takeUntilDestroyed(this.destroyRef))
1524
+ .subscribe({
1525
+ next: (response) => {
1526
+ if (response?.success && response?.data) {
1527
+ const terms = Array.isArray(response.data) ? response.data : [];
1528
+ // Transform to SelectOption format with parent field for tree view
1529
+ const options = terms.map((term) => {
1530
+ // Normalize parent field for tree view compatibility
1531
+ let parentId = '';
1532
+ if (term.acapt_parent_class_prog_term_acapt) {
1533
+ if (typeof term.acapt_parent_class_prog_term_acapt === 'object') {
1534
+ parentId = term.acapt_parent_class_prog_term_acapt._id || '';
1535
+ }
1536
+ else if (typeof term.acapt_parent_class_prog_term_acapt === 'string') {
1537
+ parentId = term.acapt_parent_class_prog_term_acapt || '';
1538
+ }
1539
+ }
1540
+ return {
1541
+ value: term._id || '',
1542
+ label: term.acapt_name || term.acapt_code || '',
1543
+ _id: term._id || '',
1544
+ acapt_parent_class_prog_term_acapt: parentId
1545
+ };
1546
+ });
1547
+ this.classProgramTerms.set(options);
1548
+ }
1549
+ else {
1550
+ this.classProgramTerms.set([]);
1551
+ }
1552
+ this.classProgramTermsLoading.set(false);
1553
+ },
1554
+ error: (response) => {
1555
+ console.error('Error loading program terms:', response);
1556
+ const errorMessage = response?.error?.message;
1557
+ if (errorMessage) {
1558
+ this.notificationService.error(errorMessage);
1559
+ }
1560
+ this.classProgramTerms.set([]);
1561
+ this.classProgramTermsLoading.set(false);
1562
+ }
1563
+ });
1564
+ }
1565
+ onTermChange(termId) {
1566
+ this.programTermSections.set([]);
1567
+ const form = this.formGroup();
1568
+ if (form) {
1569
+ form.patchValue({
1570
+ [this.sectionControlName()]: ''
1571
+ }, { emitEvent: false });
1572
+ }
1573
+ if (termId) {
1574
+ this.loadProgramTermSections(termId);
1575
+ }
1576
+ else {
1577
+ this.hasSections.set(false);
1578
+ const sectionControl = form?.get(this.sectionControlName());
1579
+ if (sectionControl) {
1580
+ sectionControl.clearValidators();
1581
+ sectionControl.updateValueAndValidity();
1582
+ }
1583
+ }
1584
+ }
1585
+ loadProgramTermSections(termId) {
1586
+ this.programTermSectionsLoading.set(true);
1587
+ const payload = new MPrgTrmSection({
1588
+ acapts_parent_class_prog_term_acapt: termId,
1589
+ pagination: false,
1590
+ pageIndex: 0,
1591
+ pageSize: 1000,
1592
+ query: "",
1593
+ sort: {
1594
+ key: 'acapts_name',
1595
+ order: 'asc'
1596
+ }
1597
+ });
1598
+ const query = generateStringFromObject(payload);
1599
+ const url = cidePath.join([
1600
+ hostManagerRoutesUrl.cideSuiteHost,
1601
+ academicsRoutesUrl.module,
1602
+ academicsRoutesUrl.programTermSection,
1603
+ query
1604
+ ]);
1605
+ this.http.get(url)
1606
+ .pipe(takeUntilDestroyed(this.destroyRef))
1607
+ .subscribe({
1608
+ next: (response) => {
1609
+ if (response?.success && response?.data) {
1610
+ const sections = Array.isArray(response.data) ? response.data : [];
1611
+ const hasSections = sections.length > 0;
1612
+ this.hasSections.set(hasSections);
1613
+ if (hasSections) {
1614
+ this.programTermSections.set(sections.map(section => ({
1615
+ value: section._id || '',
1616
+ label: section.acapts_name || section.acapts_code || ''
1617
+ })));
1618
+ const form = this.formGroup();
1619
+ const sectionControl = form?.get(this.sectionControlName());
1620
+ if (sectionControl) {
1621
+ sectionControl.setValidators([Validators.required]);
1622
+ sectionControl.updateValueAndValidity();
1623
+ }
1624
+ }
1625
+ else {
1626
+ this.programTermSections.set([]);
1627
+ const form = this.formGroup();
1628
+ const sectionControl = form?.get(this.sectionControlName());
1629
+ if (sectionControl) {
1630
+ sectionControl.clearValidators();
1631
+ sectionControl.updateValueAndValidity();
1632
+ }
1633
+ }
1634
+ }
1635
+ else {
1636
+ this.hasSections.set(false);
1637
+ this.programTermSections.set([]);
1638
+ const form = this.formGroup();
1639
+ const sectionControl = form?.get(this.sectionControlName());
1640
+ if (sectionControl) {
1641
+ sectionControl.clearValidators();
1642
+ sectionControl.updateValueAndValidity();
1643
+ }
1644
+ }
1645
+ this.programTermSectionsLoading.set(false);
1646
+ },
1647
+ error: (response) => {
1648
+ console.error('Error loading sections:', response);
1649
+ const errorMessage = response?.error?.message;
1650
+ if (errorMessage) {
1651
+ this.notificationService.error(errorMessage);
1652
+ }
1653
+ this.hasSections.set(false);
1654
+ this.programTermSections.set([]);
1655
+ const form = this.formGroup();
1656
+ const sectionControl = form?.get(this.sectionControlName());
1657
+ if (sectionControl) {
1658
+ sectionControl.clearValidators();
1659
+ sectionControl.updateValueAndValidity();
1660
+ }
1661
+ this.programTermSectionsLoading.set(false);
1662
+ }
1663
+ });
1664
+ }
1665
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.7", ngImport: i0, type: CideSharedProgramSectionSelectorComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1666
+ 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"] }] });
1667
+ }
1668
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.7", ngImport: i0, type: CideSharedProgramSectionSelectorComponent, decorators: [{
1669
+ type: Component,
1670
+ args: [{ selector: 'cide-shared-program-section-selector', standalone: true, imports: [
1671
+ CommonModule,
1672
+ ReactiveFormsModule,
1673
+ CideSelectComponent,
1674
+ NgClass
1675
+ ], 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"] }]
1676
+ }], ctorParameters: () => [] });
1677
+
885
1678
  /*
886
1679
  * Public API Surface of cloud-ide-shared
887
1680
  */
@@ -890,5 +1683,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.7", ngImpor
890
1683
  * Generated bundle index. Do not edit.
891
1684
  */
892
1685
 
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 };
1686
+ 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, SharedObjectIdService, USER_SERVICE_TOKEN, authGuard };
894
1687
  //# sourceMappingURL=cloud-ide-shared.mjs.map