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.
- package/fesm2022/cloud-ide-shared.mjs +801 -8
- package/fesm2022/cloud-ide-shared.mjs.map +1 -1
- package/index.d.ts +190 -24
- package/package.json +1 -1
|
@@ -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
|