@memberjunction/ng-tasks 5.14.0 → 5.16.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,4 +1,4 @@
1
1
 
2
- > @memberjunction/ng-tasks@5.14.0 build
2
+ > @memberjunction/ng-tasks@5.16.0 build
3
3
  > ngc
4
4
 
package/CHANGELOG.md CHANGED
@@ -1,5 +1,30 @@
1
1
  # @memberjunction/ng-tasks
2
2
 
3
+ ## 5.16.0
4
+
5
+ ### Patch Changes
6
+
7
+ - Updated dependencies [2387400]
8
+ - Updated dependencies [11dba07]
9
+ - @memberjunction/core@5.16.0
10
+ - @memberjunction/ai-engine-base@5.16.0
11
+ - @memberjunction/ai-core-plus@5.16.0
12
+ - @memberjunction/ng-shared-generic@5.16.0
13
+ - @memberjunction/core-entities@5.16.0
14
+
15
+ ## 5.15.0
16
+
17
+ ### Patch Changes
18
+
19
+ - Updated dependencies [662d56b]
20
+ - Updated dependencies [d01f697]
21
+ - Updated dependencies [c3e8b94]
22
+ - @memberjunction/core@5.15.0
23
+ - @memberjunction/ai-core-plus@5.15.0
24
+ - @memberjunction/ai-engine-base@5.15.0
25
+ - @memberjunction/ng-shared-generic@5.15.0
26
+ - @memberjunction/core-entities@5.15.0
27
+
3
28
  ## 5.14.0
4
29
 
5
30
  ### Patch Changes
@@ -16,11 +16,13 @@ export declare class GanttTaskViewerComponent implements OnChanges, AfterViewIni
16
16
  ganttContainer: ElementRef<HTMLDivElement>;
17
17
  selectedTask: MJTaskEntity | null;
18
18
  detailPanelWidth: number;
19
+ IsGanttLoading: boolean;
20
+ private ganttLib;
19
21
  private ganttInitialized;
20
22
  private isResizing;
21
23
  private resizeStartX;
22
24
  private resizeStartWidth;
23
- ngAfterViewInit(): void;
25
+ ngAfterViewInit(): Promise<void>;
24
26
  ngOnChanges(): void;
25
27
  ngOnDestroy(): void;
26
28
  private initGantt;
@@ -1 +1 @@
1
- {"version":3,"file":"gantt-task-viewer.component.d.ts","sourceRoot":"","sources":["../../../src/lib/components/gantt-task-viewer.component.ts"],"names":[],"mappings":"AAAA,OAAO,EAA4B,YAAY,EAAE,SAAS,EAAE,aAAa,EAAE,UAAU,EAAa,SAAS,EAAgB,MAAM,eAAe,CAAC;AAEjJ,OAAO,EAAE,YAAY,EAAE,sBAAsB,EAAE,MAAM,+BAA+B,CAAC;;AAKrF;;GAEG;AACH,qBA8Ga,wBAAyB,YAAW,SAAS,EAAE,aAAa,EAAE,SAAS;IACzE,KAAK,EAAE,YAAY,EAAE,CAAM;IAC3B,gBAAgB,EAAE,sBAAsB,EAAE,CAAM;IAChD,WAAW,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,WAAW,6BAAoC;IAC/C,gBAAgB;oBAAkC,MAAM;kBAAY,MAAM;OAAM;IAE1C,cAAc,EAAG,UAAU,CAAC,cAAc,CAAC,CAAC;IAErF,YAAY,EAAE,YAAY,GAAG,IAAI,CAAQ;IACzC,gBAAgB,EAAE,MAAM,CAAO;IAEtC,OAAO,CAAC,gBAAgB,CAAS;IACjC,OAAO,CAAC,UAAU,CAAS;IAC3B,OAAO,CAAC,YAAY,CAAK;IACzB,OAAO,CAAC,gBAAgB,CAAK;IAE7B,eAAe;IAWf,WAAW;IAgBX,WAAW;IAOX,OAAO,CAAC,SAAS;IAgFjB,OAAO,CAAC,eAAe;IAgCvB,OAAO,CAAC,oBAAoB;IA6J5B,OAAO,CAAC,iBAAiB;IASzB,OAAO,CAAC,mBAAmB;IAOpB,aAAa,CAAC,IAAI,EAAE,YAAY,GAAG,MAAM,GAAG,IAAI;IAIhD,gBAAgB,IAAI,IAAI;IAIxB,kBAAkB,CAAC,KAAK,EAAE;QAAE,UAAU,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI;IAIzE,WAAW,CAAC,KAAK,EAAE,UAAU,GAAG,IAAI;IAQ3C,YAAY,CAAC,KAAK,EAAE,UAAU,GAAG,IAAI;IAWrC,UAAU,IAAI,IAAI;IAYlB,OAAO,CAAC,sBAAsB;yCA3XnB,wBAAwB;2CAAxB,wBAAwB;CA0ZpC"}
1
+ {"version":3,"file":"gantt-task-viewer.component.d.ts","sourceRoot":"","sources":["../../../src/lib/components/gantt-task-viewer.component.ts"],"names":[],"mappings":"AAAA,OAAO,EAA4B,YAAY,EAAE,SAAS,EAAE,aAAa,EAAE,UAAU,EAAa,SAAS,EAAgB,MAAM,eAAe,CAAC;AAEjJ,OAAO,EAAE,YAAY,EAAE,sBAAsB,EAAE,MAAM,+BAA+B,CAAC;;AAMrF;;GAEG;AACH,qBA6Ha,wBAAyB,YAAW,SAAS,EAAE,aAAa,EAAE,SAAS;IACzE,KAAK,EAAE,YAAY,EAAE,CAAM;IAC3B,gBAAgB,EAAE,sBAAsB,EAAE,CAAM;IAChD,WAAW,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,WAAW,6BAAoC;IAC/C,gBAAgB;oBAAkC,MAAM;kBAAY,MAAM;OAAM;IAE1C,cAAc,EAAG,UAAU,CAAC,cAAc,CAAC,CAAC;IAErF,YAAY,EAAE,YAAY,GAAG,IAAI,CAAQ;IACzC,gBAAgB,EAAE,MAAM,CAAO;IAC/B,cAAc,UAAQ;IAE7B,OAAO,CAAC,QAAQ,CAA4B;IAC5C,OAAO,CAAC,gBAAgB,CAAS;IACjC,OAAO,CAAC,UAAU,CAAS;IAC3B,OAAO,CAAC,YAAY,CAAK;IACzB,OAAO,CAAC,gBAAgB,CAAK;IAEvB,eAAe;IAoBrB,WAAW;IAkBX,WAAW;IAOX,OAAO,CAAC,SAAS;IAiFjB,OAAO,CAAC,eAAe;IAiCvB,OAAO,CAAC,oBAAoB;IA8J5B,OAAO,CAAC,iBAAiB;IASzB,OAAO,CAAC,mBAAmB;IAOpB,aAAa,CAAC,IAAI,EAAE,YAAY,GAAG,MAAM,GAAG,IAAI;IAIhD,gBAAgB,IAAI,IAAI;IAIxB,kBAAkB,CAAC,KAAK,EAAE;QAAE,UAAU,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI;IAIzE,WAAW,CAAC,KAAK,EAAE,UAAU,GAAG,IAAI;IAQ3C,YAAY,CAAC,KAAK,EAAE,UAAU,GAAG,IAAI;IAWrC,UAAU,IAAI,IAAI;IAYlB,OAAO,CAAC,sBAAsB;yCA3YnB,wBAAwB;2CAAxB,wBAAwB;CA4apC"}
@@ -1,46 +1,61 @@
1
1
  import { Component, Input, Output, EventEmitter, ViewChild, HostListener } from '@angular/core';
2
- import { gantt } from 'dhtmlx-gantt';
3
2
  import { TaskDetailPanelComponent } from './task-detail-panel.component';
3
+ import { SharedGenericModule } from '@memberjunction/ng-shared-generic';
4
4
  import { UUIDsEqual } from '@memberjunction/global';
5
5
  import * as i0 from "@angular/core";
6
+ import * as i1 from "@memberjunction/ng-shared-generic";
6
7
  const _c0 = ["ganttContainer"];
7
8
  function GanttTaskViewerComponent_Conditional_1_Template(rf, ctx) { if (rf & 1) {
8
9
  i0.ɵɵelementStart(0, "div", 2);
9
- i0.ɵɵelement(1, "i", 4);
10
+ i0.ɵɵelement(1, "mj-loading", 3);
11
+ i0.ɵɵelementEnd();
12
+ } }
13
+ function GanttTaskViewerComponent_Conditional_2_Conditional_0_Template(rf, ctx) { if (rf & 1) {
14
+ i0.ɵɵelementStart(0, "div", 4);
15
+ i0.ɵɵelement(1, "i", 6);
10
16
  i0.ɵɵelementStart(2, "p");
11
17
  i0.ɵɵtext(3, "No tasks to display in Gantt view");
12
18
  i0.ɵɵelementEnd()();
13
19
  } }
14
- function GanttTaskViewerComponent_Conditional_2_Conditional_3_Template(rf, ctx) { if (rf & 1) {
20
+ function GanttTaskViewerComponent_Conditional_2_Conditional_1_Conditional_3_Template(rf, ctx) { if (rf & 1) {
15
21
  const _r1 = i0.ɵɵgetCurrentView();
16
- i0.ɵɵelementStart(0, "div", 8);
17
- i0.ɵɵlistener("mousedown", function GanttTaskViewerComponent_Conditional_2_Conditional_3_Template_div_mousedown_0_listener($event) { i0.ɵɵrestoreView(_r1); const ctx_r1 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r1.startResize($event)); });
22
+ i0.ɵɵelementStart(0, "div", 10);
23
+ i0.ɵɵlistener("mousedown", function GanttTaskViewerComponent_Conditional_2_Conditional_1_Conditional_3_Template_div_mousedown_0_listener($event) { i0.ɵɵrestoreView(_r1); const ctx_r1 = i0.ɵɵnextContext(3); return i0.ɵɵresetView(ctx_r1.startResize($event)); });
18
24
  i0.ɵɵelementEnd();
19
25
  } }
20
- function GanttTaskViewerComponent_Conditional_2_Conditional_4_Template(rf, ctx) { if (rf & 1) {
26
+ function GanttTaskViewerComponent_Conditional_2_Conditional_1_Conditional_4_Template(rf, ctx) { if (rf & 1) {
21
27
  const _r3 = i0.ɵɵgetCurrentView();
22
- i0.ɵɵelementStart(0, "div", 9)(1, "mj-task-detail-panel", 10);
23
- i0.ɵɵlistener("closePanel", function GanttTaskViewerComponent_Conditional_2_Conditional_4_Template_mj_task_detail_panel_closePanel_1_listener() { i0.ɵɵrestoreView(_r3); const ctx_r1 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r1.closeDetailPanel()); })("openEntityRecord", function GanttTaskViewerComponent_Conditional_2_Conditional_4_Template_mj_task_detail_panel_openEntityRecord_1_listener($event) { i0.ɵɵrestoreView(_r3); const ctx_r1 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r1.onOpenEntityRecord($event)); });
28
+ i0.ɵɵelementStart(0, "div", 11)(1, "mj-task-detail-panel", 12);
29
+ i0.ɵɵlistener("closePanel", function GanttTaskViewerComponent_Conditional_2_Conditional_1_Conditional_4_Template_mj_task_detail_panel_closePanel_1_listener() { i0.ɵɵrestoreView(_r3); const ctx_r1 = i0.ɵɵnextContext(3); return i0.ɵɵresetView(ctx_r1.closeDetailPanel()); })("openEntityRecord", function GanttTaskViewerComponent_Conditional_2_Conditional_1_Conditional_4_Template_mj_task_detail_panel_openEntityRecord_1_listener($event) { i0.ɵɵrestoreView(_r3); const ctx_r1 = i0.ɵɵnextContext(3); return i0.ɵɵresetView(ctx_r1.onOpenEntityRecord($event)); });
24
30
  i0.ɵɵelementEnd()();
25
31
  } if (rf & 2) {
26
- const ctx_r1 = i0.ɵɵnextContext(2);
32
+ const ctx_r1 = i0.ɵɵnextContext(3);
27
33
  i0.ɵɵstyleProp("width", ctx_r1.detailPanelWidth, "px");
28
34
  i0.ɵɵadvance();
29
35
  i0.ɵɵproperty("task", ctx_r1.selectedTask)("agentRunId", ctx_r1.getAgentRunId(ctx_r1.selectedTask));
30
36
  } }
31
- function GanttTaskViewerComponent_Conditional_2_Template(rf, ctx) { if (rf & 1) {
32
- i0.ɵɵelementStart(0, "div", 3);
33
- i0.ɵɵelement(1, "div", 5, 0);
34
- i0.ɵɵconditionalCreate(3, GanttTaskViewerComponent_Conditional_2_Conditional_3_Template, 1, 0, "div", 6);
35
- i0.ɵɵconditionalCreate(4, GanttTaskViewerComponent_Conditional_2_Conditional_4_Template, 2, 4, "div", 7);
37
+ function GanttTaskViewerComponent_Conditional_2_Conditional_1_Template(rf, ctx) { if (rf & 1) {
38
+ i0.ɵɵelementStart(0, "div", 5);
39
+ i0.ɵɵelement(1, "div", 7, 0);
40
+ i0.ɵɵconditionalCreate(3, GanttTaskViewerComponent_Conditional_2_Conditional_1_Conditional_3_Template, 1, 0, "div", 8);
41
+ i0.ɵɵconditionalCreate(4, GanttTaskViewerComponent_Conditional_2_Conditional_1_Conditional_4_Template, 2, 4, "div", 9);
36
42
  i0.ɵɵelementEnd();
37
43
  } if (rf & 2) {
38
- const ctx_r1 = i0.ɵɵnextContext();
44
+ const ctx_r1 = i0.ɵɵnextContext(2);
39
45
  i0.ɵɵadvance(3);
40
46
  i0.ɵɵconditional(ctx_r1.selectedTask ? 3 : -1);
41
47
  i0.ɵɵadvance();
42
48
  i0.ɵɵconditional(ctx_r1.selectedTask ? 4 : -1);
43
49
  } }
50
+ function GanttTaskViewerComponent_Conditional_2_Template(rf, ctx) { if (rf & 1) {
51
+ i0.ɵɵconditionalCreate(0, GanttTaskViewerComponent_Conditional_2_Conditional_0_Template, 4, 0, "div", 4);
52
+ i0.ɵɵconditionalCreate(1, GanttTaskViewerComponent_Conditional_2_Conditional_1_Template, 5, 2, "div", 5);
53
+ } if (rf & 2) {
54
+ const ctx_r1 = i0.ɵɵnextContext();
55
+ i0.ɵɵconditional(!ctx_r1.tasks || ctx_r1.tasks.length === 0 ? 0 : -1);
56
+ i0.ɵɵadvance();
57
+ i0.ɵɵconditional(ctx_r1.tasks && ctx_r1.tasks.length > 0 ? 1 : -1);
58
+ } }
44
59
  /**
45
60
  * Gantt chart view for tasks using DHTMLX Gantt
46
61
  */
@@ -53,17 +68,28 @@ export class GanttTaskViewerComponent {
53
68
  ganttContainer;
54
69
  selectedTask = null;
55
70
  detailPanelWidth = 400;
71
+ IsGanttLoading = true;
72
+ ganttLib = null;
56
73
  ganttInitialized = false;
57
74
  isResizing = false;
58
75
  resizeStartX = 0;
59
76
  resizeStartWidth = 0;
60
- ngAfterViewInit() {
77
+ async ngAfterViewInit() {
61
78
  console.log('🔧 ngAfterViewInit called', {
62
79
  taskCount: this.tasks?.length || 0,
63
80
  hasContainer: !!this.ganttContainer
64
81
  });
65
- if (this.tasks && this.tasks.length > 0 && this.ganttContainer) {
66
- this.initGantt();
82
+ try {
83
+ const module = await import('dhtmlx-gantt');
84
+ this.ganttLib = module.gantt;
85
+ this.IsGanttLoading = false;
86
+ if (this.tasks && this.tasks.length > 0 && this.ganttContainer) {
87
+ this.initGantt();
88
+ }
89
+ }
90
+ catch (error) {
91
+ console.error('Failed to load dhtmlx-gantt:', error);
92
+ this.IsGanttLoading = false;
67
93
  }
68
94
  }
69
95
  ngOnChanges() {
@@ -72,6 +98,8 @@ export class GanttTaskViewerComponent {
72
98
  hasContainer: !!this.ganttContainer,
73
99
  taskCount: this.tasks?.length || 0
74
100
  });
101
+ if (!this.ganttLib)
102
+ return; // Library not yet loaded
75
103
  if (this.ganttInitialized && this.ganttContainer) {
76
104
  this.updateGanttData();
77
105
  }
@@ -82,34 +110,35 @@ export class GanttTaskViewerComponent {
82
110
  }
83
111
  }
84
112
  ngOnDestroy() {
85
- if (this.ganttInitialized) {
86
- gantt.clearAll();
113
+ if (this.ganttInitialized && this.ganttLib) {
114
+ this.ganttLib.clearAll();
87
115
  this.ganttInitialized = false;
88
116
  }
89
117
  }
90
118
  initGantt() {
91
119
  try {
92
120
  console.log('🎨 Initializing DHTMLX Gantt');
121
+ const g = this.ganttLib;
93
122
  // IMPORTANT: Clear any previous configuration
94
- gantt.clearAll();
123
+ g.clearAll();
95
124
  // Configure Gantt layout and appearance
96
- gantt.config.date_format = '%Y-%m-%d %H:%i';
97
- gantt.config.scale_unit = 'day';
98
- gantt.config.date_scale = '%d %M';
99
- gantt.config.subscales = [];
100
- gantt.config.show_progress = true;
101
- gantt.config.show_links = true;
102
- gantt.config.auto_types = true;
103
- gantt.config.readonly = true; // Read-only for now
104
- gantt.config.fit_tasks = true; // Auto-fit timeline to tasks
125
+ g.config.date_format = '%Y-%m-%d %H:%i';
126
+ g.config.scale_unit = 'day';
127
+ g.config.date_scale = '%d %M';
128
+ g.config.subscales = [];
129
+ g.config.show_progress = true;
130
+ g.config.show_links = true;
131
+ g.config.auto_types = true;
132
+ g.config.readonly = true; // Read-only for now
133
+ g.config.fit_tasks = true; // Auto-fit timeline to tasks
105
134
  // Disable auto-scheduling - we calculate dates ourselves based on dependencies
106
- gantt.config.auto_scheduling = false;
135
+ g.config.auto_scheduling = false;
107
136
  // Grid configuration
108
- gantt.config.grid_width = 350;
109
- gantt.config.row_height = 36;
110
- gantt.config.scale_height = 0; // Hide date scale - dates are just for positioning
137
+ g.config.grid_width = 350;
138
+ g.config.row_height = 36;
139
+ g.config.scale_height = 0; // Hide date scale - dates are just for positioning
111
140
  // Layout configuration - ensure timeline is visible
112
- gantt.config.layout = {
141
+ g.config.layout = {
113
142
  css: "gantt_container",
114
143
  rows: [
115
144
  {
@@ -124,14 +153,14 @@ export class GanttTaskViewerComponent {
124
153
  ]
125
154
  };
126
155
  // Column configuration - only show task names, hide dates
127
- gantt.config.columns = [
156
+ g.config.columns = [
128
157
  { name: 'text', label: 'Task name', tree: true, width: '*' }
129
158
  ];
130
159
  // Initialize Gantt in the container
131
- gantt.init(this.ganttContainer.nativeElement);
160
+ g.init(this.ganttContainer.nativeElement);
132
161
  this.ganttInitialized = true;
133
162
  // Attach click event
134
- gantt.attachEvent('onTaskClick', (id) => {
163
+ g.attachEvent('onTaskClick', (id) => {
135
164
  const originalTask = this.tasks.find(t => UUIDsEqual(t.ID, id));
136
165
  if (originalTask) {
137
166
  this.selectedTask = originalTask;
@@ -143,7 +172,7 @@ export class GanttTaskViewerComponent {
143
172
  this.updateGanttData();
144
173
  // Force resize after data load to ensure proper rendering
145
174
  setTimeout(() => {
146
- gantt.setSizes();
175
+ g.setSizes();
147
176
  }, 0);
148
177
  // Expand and select after render completes
149
178
  setTimeout(() => {
@@ -160,17 +189,18 @@ export class GanttTaskViewerComponent {
160
189
  return;
161
190
  try {
162
191
  console.log('📊 Updating Gantt data with', this.tasks.length, 'tasks');
192
+ const g = this.ganttLib;
163
193
  const ganttData = this.convertToGanttFormat(this.tasks);
164
- gantt.clearAll();
165
- gantt.parse(ganttData);
194
+ g.clearAll();
195
+ g.parse(ganttData);
166
196
  // Log final parsed data for debugging
167
197
  console.log('📋 Gantt data after parse:', {
168
- tasks: gantt.getTaskByTime(),
169
- links: gantt.getLinks()
198
+ tasks: g.getTaskByTime(),
199
+ links: g.getLinks()
170
200
  });
171
201
  // Force resize after data update
172
202
  setTimeout(() => {
173
- gantt.setSizes();
203
+ g.setSizes();
174
204
  }, 0);
175
205
  // Expand and select after render completes
176
206
  setTimeout(() => {
@@ -260,15 +290,16 @@ export class GanttTaskViewerComponent {
260
290
  }
261
291
  });
262
292
  // Add 1 day padding before and after
293
+ const g = this.ganttLib;
263
294
  if (minDate) {
264
295
  const paddedStart = new Date(minDate);
265
296
  paddedStart.setDate(paddedStart.getDate() - 1);
266
- gantt.config.start_date = paddedStart;
297
+ g.config.start_date = paddedStart;
267
298
  }
268
299
  if (maxDate) {
269
300
  const paddedEnd = new Date(maxDate);
270
301
  paddedEnd.setDate(paddedEnd.getDate() + 1);
271
- gantt.config.end_date = paddedEnd;
302
+ g.config.end_date = paddedEnd;
272
303
  }
273
304
  // Now create Gantt tasks with calculated dates
274
305
  tasks.forEach((task) => {
@@ -357,36 +388,37 @@ export class GanttTaskViewerComponent {
357
388
  this.isResizing = false;
358
389
  // Resize gantt chart after panel resize completes
359
390
  setTimeout(() => {
360
- if (this.ganttInitialized) {
361
- gantt.setSizes();
391
+ if (this.ganttInitialized && this.ganttLib) {
392
+ this.ganttLib.setSizes();
362
393
  }
363
394
  }, 0);
364
395
  }
365
396
  }
366
397
  expandAllAndSelectRoot() {
367
398
  try {
399
+ const g = this.ganttLib;
368
400
  // Expand all tasks
369
- gantt.eachTask((task) => {
370
- gantt.open(task.id);
401
+ g.eachTask((task) => {
402
+ g.open(task.id);
371
403
  });
372
404
  // Find and select the root task (task with parent = 0)
373
- let rootTask = null;
374
- gantt.eachTask((task) => {
405
+ let rootTaskId = null;
406
+ g.eachTask((task) => {
375
407
  if (task.parent === 0 || task.parent === '0') {
376
- rootTask = task;
408
+ rootTaskId = task.id;
377
409
  return false; // Stop iteration
378
410
  }
379
411
  });
380
- if (rootTask) {
381
- gantt.selectTask(rootTask.id);
412
+ if (rootTaskId != null) {
413
+ g.selectTask(rootTaskId);
382
414
  // Trigger task click event to open detail panel
383
- const originalTask = this.tasks.find(t => UUIDsEqual(t.ID, rootTask.id));
415
+ const originalTask = this.tasks.find(t => UUIDsEqual(t.ID, String(rootTaskId)));
384
416
  if (originalTask) {
385
417
  this.selectedTask = originalTask;
386
418
  this.taskClicked.emit(originalTask);
387
419
  }
388
420
  }
389
- console.log('✅ Expanded all tasks and selected root:', rootTask?.id);
421
+ console.log('✅ Expanded all tasks and selected root:', rootTaskId);
390
422
  }
391
423
  catch (error) {
392
424
  console.error('❌ Error expanding/selecting tasks:', error);
@@ -400,50 +432,58 @@ export class GanttTaskViewerComponent {
400
432
  i0.ɵɵqueryRefresh(_t = i0.ɵɵloadQuery()) && (ctx.ganttContainer = _t.first);
401
433
  } }, hostBindings: function GanttTaskViewerComponent_HostBindings(rf, ctx) { if (rf & 1) {
402
434
  i0.ɵɵlistener("mousemove", function GanttTaskViewerComponent_mousemove_HostBindingHandler($event) { return ctx.handleResize($event); }, i0.ɵɵresolveDocument)("mouseup", function GanttTaskViewerComponent_mouseup_HostBindingHandler() { return ctx.stopResize(); }, i0.ɵɵresolveDocument);
403
- } }, inputs: { tasks: "tasks", taskDependencies: "taskDependencies", agentRunMap: "agentRunMap" }, outputs: { taskClicked: "taskClicked", openEntityRecord: "openEntityRecord" }, features: [i0.ɵɵNgOnChangesFeature], decls: 3, vars: 2, consts: [["ganttContainer", ""], [1, "gantt-task-viewer"], [1, "no-tasks"], [1, "gantt-layout"], [1, "fas", "fa-chart-gantt"], [1, "gantt-container"], [1, "gantt-resizer"], [1, "task-detail-panel", 3, "width"], [1, "gantt-resizer", 3, "mousedown"], [1, "task-detail-panel"], [3, "closePanel", "openEntityRecord", "task", "agentRunId"]], template: function GanttTaskViewerComponent_Template(rf, ctx) { if (rf & 1) {
435
+ } }, inputs: { tasks: "tasks", taskDependencies: "taskDependencies", agentRunMap: "agentRunMap" }, outputs: { taskClicked: "taskClicked", openEntityRecord: "openEntityRecord" }, features: [i0.ɵɵNgOnChangesFeature], decls: 3, vars: 2, consts: [["ganttContainer", ""], [1, "gantt-task-viewer"], [1, "loading-container"], ["text", "Loading Gantt chart..."], [1, "no-tasks"], [1, "gantt-layout"], [1, "fas", "fa-chart-gantt"], [1, "gantt-container"], [1, "gantt-resizer"], [1, "task-detail-panel", 3, "width"], [1, "gantt-resizer", 3, "mousedown"], [1, "task-detail-panel"], [3, "closePanel", "openEntityRecord", "task", "agentRunId"]], template: function GanttTaskViewerComponent_Template(rf, ctx) { if (rf & 1) {
404
436
  i0.ɵɵelementStart(0, "div", 1);
405
- i0.ɵɵconditionalCreate(1, GanttTaskViewerComponent_Conditional_1_Template, 4, 0, "div", 2);
406
- i0.ɵɵconditionalCreate(2, GanttTaskViewerComponent_Conditional_2_Template, 5, 2, "div", 3);
437
+ i0.ɵɵconditionalCreate(1, GanttTaskViewerComponent_Conditional_1_Template, 2, 0, "div", 2);
438
+ i0.ɵɵconditionalCreate(2, GanttTaskViewerComponent_Conditional_2_Template, 2, 2);
407
439
  i0.ɵɵelementEnd();
408
440
  } if (rf & 2) {
409
441
  i0.ɵɵadvance();
410
- i0.ɵɵconditional(!ctx.tasks || ctx.tasks.length === 0 ? 1 : -1);
442
+ i0.ɵɵconditional(ctx.IsGanttLoading ? 1 : -1);
411
443
  i0.ɵɵadvance();
412
- i0.ɵɵconditional(ctx.tasks && ctx.tasks.length > 0 ? 2 : -1);
413
- } }, dependencies: [TaskDetailPanelComponent], styles: [".gantt-task-viewer[_ngcontent-%COMP%] {\n height: 100%;\n background: var(--mj-bg-surface);\n overflow: hidden;\n display: flex;\n flex-direction: column;\n }\n\n .gantt-layout[_ngcontent-%COMP%] {\n display: flex;\n height: 600px;\n position: relative;\n }\n\n .gantt-container[_ngcontent-%COMP%] {\n flex: 1;\n min-width: 400px;\n height: 100%;\n position: relative;\n }\n\n .gantt-resizer[_ngcontent-%COMP%] {\n width: 4px;\n background: var(--mj-border-default);\n cursor: col-resize;\n flex-shrink: 0;\n transition: background 0.2s;\n }\n\n .gantt-resizer[_ngcontent-%COMP%]:hover {\n background: var(--mj-brand-primary);\n }\n\n .task-detail-panel[_ngcontent-%COMP%] {\n min-width: 300px;\n max-width: 600px;\n height: 100%;\n border-left: 1px solid var(--mj-border-default);\n flex-shrink: 0;\n }\n\n \n\n [_nghost-%COMP%] .gantt_container {\n font-family: inherit;\n font-size: 13px;\n }\n\n [_nghost-%COMP%] .gantt_grid_scale, \n [_nghost-%COMP%] .gantt_task_scale {\n background: var(--mj-bg-surface-sunken);\n border-bottom: 2px solid var(--mj-border-default);\n }\n\n [_nghost-%COMP%] .gantt_task .gantt_task_content {\n font-weight: 500;\n }\n\n .no-tasks[_ngcontent-%COMP%] {\n text-align: center;\n padding: 80px 20px;\n color: var(--mj-text-disabled);\n }\n\n .no-tasks[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n font-size: 64px;\n opacity: 0.3;\n margin-bottom: 16px;\n }\n\n .no-tasks[_ngcontent-%COMP%] p[_ngcontent-%COMP%] {\n margin: 0;\n font-size: 14px;\n }"] });
444
+ i0.ɵɵconditional(!ctx.IsGanttLoading ? 2 : -1);
445
+ } }, dependencies: [TaskDetailPanelComponent, SharedGenericModule, i1.LoadingComponent], styles: [".gantt-task-viewer[_ngcontent-%COMP%] {\n height: 100%;\n background: var(--mj-bg-surface);\n overflow: hidden;\n display: flex;\n flex-direction: column;\n }\n\n .gantt-layout[_ngcontent-%COMP%] {\n display: flex;\n height: 600px;\n position: relative;\n }\n\n .gantt-container[_ngcontent-%COMP%] {\n flex: 1;\n min-width: 400px;\n height: 100%;\n position: relative;\n }\n\n .gantt-resizer[_ngcontent-%COMP%] {\n width: 4px;\n background: var(--mj-border-default);\n cursor: col-resize;\n flex-shrink: 0;\n transition: background 0.2s;\n }\n\n .gantt-resizer[_ngcontent-%COMP%]:hover {\n background: var(--mj-brand-primary);\n }\n\n .task-detail-panel[_ngcontent-%COMP%] {\n min-width: 300px;\n max-width: 600px;\n height: 100%;\n border-left: 1px solid var(--mj-border-default);\n flex-shrink: 0;\n }\n\n \n\n [_nghost-%COMP%] .gantt_container {\n font-family: inherit;\n font-size: 13px;\n }\n\n [_nghost-%COMP%] .gantt_grid_scale, \n [_nghost-%COMP%] .gantt_task_scale {\n background: var(--mj-bg-surface-sunken);\n border-bottom: 2px solid var(--mj-border-default);\n }\n\n [_nghost-%COMP%] .gantt_task .gantt_task_content {\n font-weight: 500;\n }\n\n .loading-container[_ngcontent-%COMP%] {\n display: flex;\n justify-content: center;\n align-items: center;\n padding: 80px 20px;\n }\n\n .no-tasks[_ngcontent-%COMP%] {\n text-align: center;\n padding: 80px 20px;\n color: var(--mj-text-disabled);\n }\n\n .no-tasks[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n font-size: 64px;\n opacity: 0.3;\n margin-bottom: 16px;\n }\n\n .no-tasks[_ngcontent-%COMP%] p[_ngcontent-%COMP%] {\n margin: 0;\n font-size: 14px;\n }"] });
414
446
  }
415
447
  (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(GanttTaskViewerComponent, [{
416
448
  type: Component,
417
- args: [{ selector: 'mj-gantt-task-viewer', standalone: true, imports: [TaskDetailPanelComponent], template: `
449
+ args: [{ selector: 'mj-gantt-task-viewer', standalone: true, imports: [TaskDetailPanelComponent, SharedGenericModule], template: `
418
450
  <div class="gantt-task-viewer">
419
- @if (!tasks || tasks.length === 0) {
420
- <div class="no-tasks">
421
- <i class="fas fa-chart-gantt"></i>
422
- <p>No tasks to display in Gantt view</p>
451
+ @if (IsGanttLoading) {
452
+ <div class="loading-container">
453
+ <mj-loading text="Loading Gantt chart..."></mj-loading>
423
454
  </div>
424
455
  }
425
-
426
- @if (tasks && tasks.length > 0) {
427
- <div class="gantt-layout">
428
- <div #ganttContainer class="gantt-container"></div>
429
- @if (selectedTask) {
430
- <div class="gantt-resizer"
431
- (mousedown)="startResize($event)"></div>
432
- }
433
- @if (selectedTask) {
434
- <div class="task-detail-panel" [style.width.px]="detailPanelWidth">
435
- <mj-task-detail-panel
436
- [task]="selectedTask"
437
- [agentRunId]="getAgentRunId(selectedTask)"
438
- (closePanel)="closeDetailPanel()"
439
- (openEntityRecord)="onOpenEntityRecord($event)">
440
- </mj-task-detail-panel>
441
- </div>
442
- }
443
- </div>
456
+
457
+ @if (!IsGanttLoading) {
458
+ @if (!tasks || tasks.length === 0) {
459
+ <div class="no-tasks">
460
+ <i class="fas fa-chart-gantt"></i>
461
+ <p>No tasks to display in Gantt view</p>
462
+ </div>
463
+ }
464
+
465
+ @if (tasks && tasks.length > 0) {
466
+ <div class="gantt-layout">
467
+ <div #ganttContainer class="gantt-container"></div>
468
+ @if (selectedTask) {
469
+ <div class="gantt-resizer"
470
+ (mousedown)="startResize($event)"></div>
471
+ }
472
+ @if (selectedTask) {
473
+ <div class="task-detail-panel" [style.width.px]="detailPanelWidth">
474
+ <mj-task-detail-panel
475
+ [task]="selectedTask"
476
+ [agentRunId]="getAgentRunId(selectedTask)"
477
+ (closePanel)="closeDetailPanel()"
478
+ (openEntityRecord)="onOpenEntityRecord($event)">
479
+ </mj-task-detail-panel>
480
+ </div>
481
+ }
482
+ </div>
483
+ }
444
484
  }
445
485
  </div>
446
- `, styles: ["\n .gantt-task-viewer {\n height: 100%;\n background: var(--mj-bg-surface);\n overflow: hidden;\n display: flex;\n flex-direction: column;\n }\n\n .gantt-layout {\n display: flex;\n height: 600px;\n position: relative;\n }\n\n .gantt-container {\n flex: 1;\n min-width: 400px;\n height: 100%;\n position: relative;\n }\n\n .gantt-resizer {\n width: 4px;\n background: var(--mj-border-default);\n cursor: col-resize;\n flex-shrink: 0;\n transition: background 0.2s;\n }\n\n .gantt-resizer:hover {\n background: var(--mj-brand-primary);\n }\n\n .task-detail-panel {\n min-width: 300px;\n max-width: 600px;\n height: 100%;\n border-left: 1px solid var(--mj-border-default);\n flex-shrink: 0;\n }\n\n /* Override DHTMLX Gantt default styles */\n :host ::ng-deep .gantt_container {\n font-family: inherit;\n font-size: 13px;\n }\n\n :host ::ng-deep .gantt_grid_scale,\n :host ::ng-deep .gantt_task_scale {\n background: var(--mj-bg-surface-sunken);\n border-bottom: 2px solid var(--mj-border-default);\n }\n\n :host ::ng-deep .gantt_task .gantt_task_content {\n font-weight: 500;\n }\n\n .no-tasks {\n text-align: center;\n padding: 80px 20px;\n color: var(--mj-text-disabled);\n }\n\n .no-tasks i {\n font-size: 64px;\n opacity: 0.3;\n margin-bottom: 16px;\n }\n\n .no-tasks p {\n margin: 0;\n font-size: 14px;\n }\n "] }]
486
+ `, styles: ["\n .gantt-task-viewer {\n height: 100%;\n background: var(--mj-bg-surface);\n overflow: hidden;\n display: flex;\n flex-direction: column;\n }\n\n .gantt-layout {\n display: flex;\n height: 600px;\n position: relative;\n }\n\n .gantt-container {\n flex: 1;\n min-width: 400px;\n height: 100%;\n position: relative;\n }\n\n .gantt-resizer {\n width: 4px;\n background: var(--mj-border-default);\n cursor: col-resize;\n flex-shrink: 0;\n transition: background 0.2s;\n }\n\n .gantt-resizer:hover {\n background: var(--mj-brand-primary);\n }\n\n .task-detail-panel {\n min-width: 300px;\n max-width: 600px;\n height: 100%;\n border-left: 1px solid var(--mj-border-default);\n flex-shrink: 0;\n }\n\n /* Override DHTMLX Gantt default styles */\n :host ::ng-deep .gantt_container {\n font-family: inherit;\n font-size: 13px;\n }\n\n :host ::ng-deep .gantt_grid_scale,\n :host ::ng-deep .gantt_task_scale {\n background: var(--mj-bg-surface-sunken);\n border-bottom: 2px solid var(--mj-border-default);\n }\n\n :host ::ng-deep .gantt_task .gantt_task_content {\n font-weight: 500;\n }\n\n .loading-container {\n display: flex;\n justify-content: center;\n align-items: center;\n padding: 80px 20px;\n }\n\n .no-tasks {\n text-align: center;\n padding: 80px 20px;\n color: var(--mj-text-disabled);\n }\n\n .no-tasks i {\n font-size: 64px;\n opacity: 0.3;\n margin-bottom: 16px;\n }\n\n .no-tasks p {\n margin: 0;\n font-size: 14px;\n }\n "] }]
447
487
  }], null, { tasks: [{
448
488
  type: Input
449
489
  }], taskDependencies: [{
@@ -464,5 +504,5 @@ export class GanttTaskViewerComponent {
464
504
  type: HostListener,
465
505
  args: ['document:mouseup']
466
506
  }] }); })();
467
- (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(GanttTaskViewerComponent, { className: "GanttTaskViewerComponent", filePath: "src/lib/components/gantt-task-viewer.component.ts", lineNumber: 121 }); })();
507
+ (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(GanttTaskViewerComponent, { className: "GanttTaskViewerComponent", filePath: "src/lib/components/gantt-task-viewer.component.ts", lineNumber: 137 }); })();
468
508
  //# sourceMappingURL=gantt-task-viewer.component.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"gantt-task-viewer.component.js","sourceRoot":"","sources":["../../../src/lib/components/gantt-task-viewer.component.ts"],"names":[],"mappings":"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","sourcesContent":["import { Component, Input, Output, EventEmitter, OnChanges, AfterViewInit, ElementRef, ViewChild, OnDestroy, HostListener } from '@angular/core';\n\nimport { MJTaskEntity, MJTaskDependencyEntity } from '@memberjunction/core-entities';\nimport { gantt } from 'dhtmlx-gantt';\nimport { TaskDetailPanelComponent } from './task-detail-panel.component';\nimport { UUIDsEqual } from '@memberjunction/global';\n\n/**\n * Gantt chart view for tasks using DHTMLX Gantt\n */\n@Component({\n selector: 'mj-gantt-task-viewer',\n standalone: true,\n imports: [TaskDetailPanelComponent],\n template: `\n <div class=\"gantt-task-viewer\">\n @if (!tasks || tasks.length === 0) {\n <div class=\"no-tasks\">\n <i class=\"fas fa-chart-gantt\"></i>\n <p>No tasks to display in Gantt view</p>\n </div>\n }\n \n @if (tasks && tasks.length > 0) {\n <div class=\"gantt-layout\">\n <div #ganttContainer class=\"gantt-container\"></div>\n @if (selectedTask) {\n <div class=\"gantt-resizer\"\n (mousedown)=\"startResize($event)\"></div>\n }\n @if (selectedTask) {\n <div class=\"task-detail-panel\" [style.width.px]=\"detailPanelWidth\">\n <mj-task-detail-panel\n [task]=\"selectedTask\"\n [agentRunId]=\"getAgentRunId(selectedTask)\"\n (closePanel)=\"closeDetailPanel()\"\n (openEntityRecord)=\"onOpenEntityRecord($event)\">\n </mj-task-detail-panel>\n </div>\n }\n </div>\n }\n </div>\n `,\n styles: [`\n .gantt-task-viewer {\n height: 100%;\n background: var(--mj-bg-surface);\n overflow: hidden;\n display: flex;\n flex-direction: column;\n }\n\n .gantt-layout {\n display: flex;\n height: 600px;\n position: relative;\n }\n\n .gantt-container {\n flex: 1;\n min-width: 400px;\n height: 100%;\n position: relative;\n }\n\n .gantt-resizer {\n width: 4px;\n background: var(--mj-border-default);\n cursor: col-resize;\n flex-shrink: 0;\n transition: background 0.2s;\n }\n\n .gantt-resizer:hover {\n background: var(--mj-brand-primary);\n }\n\n .task-detail-panel {\n min-width: 300px;\n max-width: 600px;\n height: 100%;\n border-left: 1px solid var(--mj-border-default);\n flex-shrink: 0;\n }\n\n /* Override DHTMLX Gantt default styles */\n :host ::ng-deep .gantt_container {\n font-family: inherit;\n font-size: 13px;\n }\n\n :host ::ng-deep .gantt_grid_scale,\n :host ::ng-deep .gantt_task_scale {\n background: var(--mj-bg-surface-sunken);\n border-bottom: 2px solid var(--mj-border-default);\n }\n\n :host ::ng-deep .gantt_task .gantt_task_content {\n font-weight: 500;\n }\n\n .no-tasks {\n text-align: center;\n padding: 80px 20px;\n color: var(--mj-text-disabled);\n }\n\n .no-tasks i {\n font-size: 64px;\n opacity: 0.3;\n margin-bottom: 16px;\n }\n\n .no-tasks p {\n margin: 0;\n font-size: 14px;\n }\n `]\n})\nexport class GanttTaskViewerComponent implements OnChanges, AfterViewInit, OnDestroy {\n @Input() tasks: MJTaskEntity[] = [];\n @Input() taskDependencies: MJTaskDependencyEntity[] = [];\n @Input() agentRunMap?: Map<string, string>; // Maps TaskID -> AgentRunID\n @Output() taskClicked = new EventEmitter<MJTaskEntity>();\n @Output() openEntityRecord = new EventEmitter<{ entityName: string; recordId: string }>();\n\n @ViewChild('ganttContainer', { static: false }) ganttContainer!: ElementRef<HTMLDivElement>;\n\n public selectedTask: MJTaskEntity | null = null;\n public detailPanelWidth: number = 400;\n\n private ganttInitialized = false;\n private isResizing = false;\n private resizeStartX = 0;\n private resizeStartWidth = 0;\n\n ngAfterViewInit() {\n console.log('🔧 ngAfterViewInit called', {\n taskCount: this.tasks?.length || 0,\n hasContainer: !!this.ganttContainer\n });\n\n if (this.tasks && this.tasks.length > 0 && this.ganttContainer) {\n this.initGantt();\n }\n }\n\n ngOnChanges() {\n console.log('🔄 ngOnChanges called', {\n initialized: this.ganttInitialized,\n hasContainer: !!this.ganttContainer,\n taskCount: this.tasks?.length || 0\n });\n\n if (this.ganttInitialized && this.ganttContainer) {\n this.updateGanttData();\n } else if (!this.ganttInitialized && this.ganttContainer && this.tasks && this.tasks.length > 0) {\n // Initialize if we have container and tasks but haven't initialized yet\n console.log('🎨 Late initialization - gantt not initialized but container and tasks available');\n this.initGantt();\n }\n }\n\n ngOnDestroy() {\n if (this.ganttInitialized) {\n gantt.clearAll();\n this.ganttInitialized = false;\n }\n }\n\n private initGantt(): void {\n try {\n console.log('🎨 Initializing DHTMLX Gantt');\n\n // IMPORTANT: Clear any previous configuration\n gantt.clearAll();\n\n // Configure Gantt layout and appearance\n gantt.config.date_format = '%Y-%m-%d %H:%i';\n gantt.config.scale_unit = 'day';\n gantt.config.date_scale = '%d %M';\n gantt.config.subscales = [];\n gantt.config.show_progress = true;\n gantt.config.show_links = true;\n gantt.config.auto_types = true;\n gantt.config.readonly = true; // Read-only for now\n gantt.config.fit_tasks = true; // Auto-fit timeline to tasks\n\n // Disable auto-scheduling - we calculate dates ourselves based on dependencies\n gantt.config.auto_scheduling = false;\n\n // Grid configuration\n gantt.config.grid_width = 350;\n gantt.config.row_height = 36;\n gantt.config.scale_height = 0; // Hide date scale - dates are just for positioning\n\n // Layout configuration - ensure timeline is visible\n gantt.config.layout = {\n css: \"gantt_container\",\n rows: [\n {\n cols: [\n { view: \"grid\", group: \"grids\", scrollY: \"scrollVer\" },\n { resizer: true, width: 1 },\n { view: \"timeline\", scrollX: \"scrollHor\", scrollY: \"scrollVer\" },\n { view: \"scrollbar\", id: \"scrollVer\", group: \"vertical\" }\n ]\n },\n { view: \"scrollbar\", id: \"scrollHor\", group: \"horizontal\" }\n ]\n };\n\n // Column configuration - only show task names, hide dates\n gantt.config.columns = [\n { name: 'text', label: 'Task name', tree: true, width: '*' }\n ];\n\n // Initialize Gantt in the container\n gantt.init(this.ganttContainer.nativeElement);\n this.ganttInitialized = true;\n\n // Attach click event\n gantt.attachEvent('onTaskClick', (id: string) => {\n const originalTask = this.tasks.find(t => UUIDsEqual(t.ID, id));\n if (originalTask) {\n this.selectedTask = originalTask;\n this.taskClicked.emit(originalTask);\n }\n return true;\n });\n\n // Load data\n this.updateGanttData();\n\n // Force resize after data load to ensure proper rendering\n setTimeout(() => {\n gantt.setSizes();\n }, 0);\n\n // Expand and select after render completes\n setTimeout(() => {\n this.expandAllAndSelectRoot();\n }, 100);\n\n console.log('✅ DHTMLX Gantt initialized successfully');\n } catch (error) {\n console.error('❌ Error initializing Gantt:', error);\n }\n }\n\n private updateGanttData(): void {\n if (!this.ganttInitialized) return;\n\n try {\n console.log('📊 Updating Gantt data with', this.tasks.length, 'tasks');\n\n const ganttData = this.convertToGanttFormat(this.tasks);\n gantt.clearAll();\n gantt.parse(ganttData);\n\n // Log final parsed data for debugging\n console.log('📋 Gantt data after parse:', {\n tasks: gantt.getTaskByTime(),\n links: gantt.getLinks()\n });\n\n // Force resize after data update\n setTimeout(() => {\n gantt.setSizes();\n }, 0);\n\n // Expand and select after render completes\n setTimeout(() => {\n this.expandAllAndSelectRoot();\n }, 100);\n\n console.log('✅ Gantt data updated');\n } catch (error) {\n console.error('❌ Error updating Gantt data:', error);\n }\n }\n\n private convertToGanttFormat(tasks: MJTaskEntity[]): { data: any[], links: any[] } {\n const data: any[] = [];\n const links: any[] = [];\n\n console.log('🔍 Converting tasks:', tasks);\n console.log('🔗 Task dependencies:', this.taskDependencies);\n\n // Build a map of task ID to task for quick lookup\n const taskMap = new Map<string, MJTaskEntity>();\n tasks.forEach(t => taskMap.set(t.ID, t));\n\n // Build dependency map: taskId -> array of tasks it depends on\n const dependencyMap = new Map<string, string[]>();\n this.taskDependencies.forEach(dep => {\n if (!dependencyMap.has(dep.TaskID)) {\n dependencyMap.set(dep.TaskID, []);\n }\n dependencyMap.get(dep.TaskID)!.push(dep.DependsOnTaskID);\n });\n\n // Calculate start dates based on dependencies\n const taskStartDates = new Map<string, Date>();\n const baseDate = new Date();\n baseDate.setHours(0, 0, 0, 0);\n\n // Recursive function to calculate start date for a task\n const calculateStartDate = (taskId: string, visited = new Set<string>()): Date => {\n // Prevent circular dependencies\n if (visited.has(taskId)) {\n return new Date(baseDate);\n }\n visited.add(taskId);\n\n // If already calculated, return it\n if (taskStartDates.has(taskId)) {\n return taskStartDates.get(taskId)!;\n }\n\n const task = taskMap.get(taskId);\n if (!task) return new Date(baseDate);\n\n const dependencies = dependencyMap.get(taskId) || [];\n\n if (dependencies.length === 0) {\n // No dependencies - use base date or task's actual start date\n const startDate = task.StartedAt ? new Date(task.StartedAt) : new Date(baseDate);\n taskStartDates.set(taskId, startDate);\n return startDate;\n }\n\n // Has dependencies - start after the latest dependency ends\n let latestEnd = new Date(baseDate);\n for (const depId of dependencies) {\n const depTask = taskMap.get(depId);\n if (depTask) {\n const depStart = calculateStartDate(depId, new Set(visited));\n const depDuration = this.calculateDuration(depTask);\n const depEnd = new Date(depStart);\n depEnd.setDate(depEnd.getDate() + depDuration);\n\n if (depEnd > latestEnd) {\n latestEnd = depEnd;\n }\n }\n }\n\n taskStartDates.set(taskId, latestEnd);\n return latestEnd;\n };\n\n // Calculate start dates for all tasks\n tasks.forEach(task => calculateStartDate(task.ID));\n\n // Calculate min/max dates for timeline display range\n let minDate: Date | null = null;\n let maxDate: Date | null = null;\n\n tasks.forEach(task => {\n const startDate = taskStartDates.get(task.ID);\n if (startDate) {\n const duration = this.calculateDuration(task);\n const endDate = new Date(startDate);\n endDate.setDate(endDate.getDate() + duration);\n\n if (!minDate || startDate < minDate) {\n minDate = startDate;\n }\n if (!maxDate || endDate > maxDate) {\n maxDate = endDate;\n }\n }\n });\n\n // Add 1 day padding before and after\n if (minDate) {\n const paddedStart = new Date(minDate);\n paddedStart.setDate(paddedStart.getDate() - 1);\n gantt.config.start_date = paddedStart;\n }\n if (maxDate) {\n const paddedEnd = new Date(maxDate);\n paddedEnd.setDate(paddedEnd.getDate() + 1);\n gantt.config.end_date = paddedEnd;\n }\n\n // Now create Gantt tasks with calculated dates\n tasks.forEach((task) => {\n console.log('📝 Processing task:', {\n ID: task.ID,\n Name: task.Name,\n ParentID: task.ParentID\n });\n\n // Calculate progress (0-1 scale for DHTMLX)\n let progress = (task.PercentComplete || 0) / 100;\n if (task.Status === 'Complete') {\n progress = 1;\n }\n\n const duration = this.calculateDuration(task);\n const startDate = taskStartDates.get(task.ID) || new Date(baseDate);\n\n const ganttTask: any = {\n id: task.ID,\n text: task.Name || 'Untitled Task',\n start_date: this.formatDateForDHTMLX(startDate),\n duration: duration,\n progress: progress\n };\n\n // Add parent relationship for tree structure (not dependency)\n if (task.ParentID) {\n ganttTask.parent = task.ParentID;\n } else {\n // Root tasks need parent: 0\n ganttTask.parent = 0;\n }\n\n console.log('✅ Created Gantt task:', ganttTask);\n data.push(ganttTask);\n });\n\n // Create links from MJTaskDependencyEntity records\n this.taskDependencies.forEach((dep, index) => {\n links.push({\n id: dep.ID || `link_${index}`,\n source: dep.DependsOnTaskID, // The task being depended on\n target: dep.TaskID, // The task that depends on it\n type: '0' // finish-to-start (DHTMLX type 0)\n });\n console.log(`🔗 Created link: ${dep.DependsOnTaskID} -> ${dep.TaskID}`);\n });\n\n console.log('📊 Final DHTMLX format:', { data, links });\n return { data, links };\n }\n\n private calculateDuration(task: MJTaskEntity): number {\n if (task.StartedAt && task.DueAt) {\n const startDate = new Date(task.StartedAt);\n const endDate = new Date(task.DueAt);\n return Math.max(1, Math.ceil((endDate.getTime() - startDate.getTime()) / (1000 * 60 * 60 * 24)));\n }\n return 1; // Default to 1 day\n }\n\n private formatDateForDHTMLX(date: Date): string {\n const year = date.getFullYear();\n const month = String(date.getMonth() + 1).padStart(2, '0');\n const day = String(date.getDate()).padStart(2, '0');\n return `${year}-${month}-${day} 00:00`;\n }\n\n public getAgentRunId(task: MJTaskEntity): string | null {\n return this.agentRunMap?.get(task.ID) || null;\n }\n\n public closeDetailPanel(): void {\n this.selectedTask = null;\n }\n\n public onOpenEntityRecord(event: { entityName: string; recordId: string }): void {\n this.openEntityRecord.emit(event);\n }\n\n public startResize(event: MouseEvent): void {\n this.isResizing = true;\n this.resizeStartX = event.clientX;\n this.resizeStartWidth = this.detailPanelWidth;\n event.preventDefault();\n }\n\n @HostListener('document:mousemove', ['$event'])\n handleResize(event: MouseEvent): void {\n if (!this.isResizing) return;\n\n const delta = this.resizeStartX - event.clientX;\n const newWidth = this.resizeStartWidth + delta;\n\n // Constrain width between min and max\n this.detailPanelWidth = Math.max(300, Math.min(600, newWidth));\n }\n\n @HostListener('document:mouseup')\n stopResize(): void {\n if (this.isResizing) {\n this.isResizing = false;\n // Resize gantt chart after panel resize completes\n setTimeout(() => {\n if (this.ganttInitialized) {\n gantt.setSizes();\n }\n }, 0);\n }\n }\n\n private expandAllAndSelectRoot(): void {\n try {\n // Expand all tasks\n gantt.eachTask((task: any) => {\n gantt.open(task.id);\n });\n\n // Find and select the root task (task with parent = 0)\n let rootTask: any = null;\n gantt.eachTask((task: any) => {\n if (task.parent === 0 || task.parent === '0') {\n rootTask = task;\n return false; // Stop iteration\n }\n });\n\n if (rootTask) {\n gantt.selectTask(rootTask.id);\n // Trigger task click event to open detail panel\n const originalTask = this.tasks.find(t => UUIDsEqual(t.ID, rootTask.id));\n if (originalTask) {\n this.selectedTask = originalTask;\n this.taskClicked.emit(originalTask);\n }\n }\n\n console.log('✅ Expanded all tasks and selected root:', rootTask?.id);\n } catch (error) {\n console.error('❌ Error expanding/selecting tasks:', error);\n }\n }\n}\n"]}
1
+ {"version":3,"file":"gantt-task-viewer.component.js","sourceRoot":"","sources":["../../../src/lib/components/gantt-task-viewer.component.ts"],"names":[],"mappings":"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","sourcesContent":["import { Component, Input, Output, EventEmitter, OnChanges, AfterViewInit, ElementRef, ViewChild, OnDestroy, HostListener } from '@angular/core';\n\nimport { MJTaskEntity, MJTaskDependencyEntity } from '@memberjunction/core-entities';\nimport type { GanttStatic, Task as GanttTask, Link as GanttLink } from 'dhtmlx-gantt';\nimport { TaskDetailPanelComponent } from './task-detail-panel.component';\nimport { SharedGenericModule } from '@memberjunction/ng-shared-generic';\nimport { UUIDsEqual } from '@memberjunction/global';\n\n/**\n * Gantt chart view for tasks using DHTMLX Gantt\n */\n@Component({\n selector: 'mj-gantt-task-viewer',\n standalone: true,\n imports: [TaskDetailPanelComponent, SharedGenericModule],\n template: `\n <div class=\"gantt-task-viewer\">\n @if (IsGanttLoading) {\n <div class=\"loading-container\">\n <mj-loading text=\"Loading Gantt chart...\"></mj-loading>\n </div>\n }\n\n @if (!IsGanttLoading) {\n @if (!tasks || tasks.length === 0) {\n <div class=\"no-tasks\">\n <i class=\"fas fa-chart-gantt\"></i>\n <p>No tasks to display in Gantt view</p>\n </div>\n }\n\n @if (tasks && tasks.length > 0) {\n <div class=\"gantt-layout\">\n <div #ganttContainer class=\"gantt-container\"></div>\n @if (selectedTask) {\n <div class=\"gantt-resizer\"\n (mousedown)=\"startResize($event)\"></div>\n }\n @if (selectedTask) {\n <div class=\"task-detail-panel\" [style.width.px]=\"detailPanelWidth\">\n <mj-task-detail-panel\n [task]=\"selectedTask\"\n [agentRunId]=\"getAgentRunId(selectedTask)\"\n (closePanel)=\"closeDetailPanel()\"\n (openEntityRecord)=\"onOpenEntityRecord($event)\">\n </mj-task-detail-panel>\n </div>\n }\n </div>\n }\n }\n </div>\n `,\n styles: [`\n .gantt-task-viewer {\n height: 100%;\n background: var(--mj-bg-surface);\n overflow: hidden;\n display: flex;\n flex-direction: column;\n }\n\n .gantt-layout {\n display: flex;\n height: 600px;\n position: relative;\n }\n\n .gantt-container {\n flex: 1;\n min-width: 400px;\n height: 100%;\n position: relative;\n }\n\n .gantt-resizer {\n width: 4px;\n background: var(--mj-border-default);\n cursor: col-resize;\n flex-shrink: 0;\n transition: background 0.2s;\n }\n\n .gantt-resizer:hover {\n background: var(--mj-brand-primary);\n }\n\n .task-detail-panel {\n min-width: 300px;\n max-width: 600px;\n height: 100%;\n border-left: 1px solid var(--mj-border-default);\n flex-shrink: 0;\n }\n\n /* Override DHTMLX Gantt default styles */\n :host ::ng-deep .gantt_container {\n font-family: inherit;\n font-size: 13px;\n }\n\n :host ::ng-deep .gantt_grid_scale,\n :host ::ng-deep .gantt_task_scale {\n background: var(--mj-bg-surface-sunken);\n border-bottom: 2px solid var(--mj-border-default);\n }\n\n :host ::ng-deep .gantt_task .gantt_task_content {\n font-weight: 500;\n }\n\n .loading-container {\n display: flex;\n justify-content: center;\n align-items: center;\n padding: 80px 20px;\n }\n\n .no-tasks {\n text-align: center;\n padding: 80px 20px;\n color: var(--mj-text-disabled);\n }\n\n .no-tasks i {\n font-size: 64px;\n opacity: 0.3;\n margin-bottom: 16px;\n }\n\n .no-tasks p {\n margin: 0;\n font-size: 14px;\n }\n `]\n})\nexport class GanttTaskViewerComponent implements OnChanges, AfterViewInit, OnDestroy {\n @Input() tasks: MJTaskEntity[] = [];\n @Input() taskDependencies: MJTaskDependencyEntity[] = [];\n @Input() agentRunMap?: Map<string, string>; // Maps TaskID -> AgentRunID\n @Output() taskClicked = new EventEmitter<MJTaskEntity>();\n @Output() openEntityRecord = new EventEmitter<{ entityName: string; recordId: string }>();\n\n @ViewChild('ganttContainer', { static: false }) ganttContainer!: ElementRef<HTMLDivElement>;\n\n public selectedTask: MJTaskEntity | null = null;\n public detailPanelWidth: number = 400;\n public IsGanttLoading = true;\n\n private ganttLib: GanttStatic | null = null;\n private ganttInitialized = false;\n private isResizing = false;\n private resizeStartX = 0;\n private resizeStartWidth = 0;\n\n async ngAfterViewInit() {\n console.log('🔧 ngAfterViewInit called', {\n taskCount: this.tasks?.length || 0,\n hasContainer: !!this.ganttContainer\n });\n\n try {\n const module = await import('dhtmlx-gantt');\n this.ganttLib = module.gantt;\n this.IsGanttLoading = false;\n\n if (this.tasks && this.tasks.length > 0 && this.ganttContainer) {\n this.initGantt();\n }\n } catch (error) {\n console.error('Failed to load dhtmlx-gantt:', error);\n this.IsGanttLoading = false;\n }\n }\n\n ngOnChanges() {\n console.log('🔄 ngOnChanges called', {\n initialized: this.ganttInitialized,\n hasContainer: !!this.ganttContainer,\n taskCount: this.tasks?.length || 0\n });\n\n if (!this.ganttLib) return; // Library not yet loaded\n\n if (this.ganttInitialized && this.ganttContainer) {\n this.updateGanttData();\n } else if (!this.ganttInitialized && this.ganttContainer && this.tasks && this.tasks.length > 0) {\n // Initialize if we have container and tasks but haven't initialized yet\n console.log('🎨 Late initialization - gantt not initialized but container and tasks available');\n this.initGantt();\n }\n }\n\n ngOnDestroy() {\n if (this.ganttInitialized && this.ganttLib) {\n this.ganttLib.clearAll();\n this.ganttInitialized = false;\n }\n }\n\n private initGantt(): void {\n try {\n console.log('🎨 Initializing DHTMLX Gantt');\n const g = this.ganttLib!;\n\n // IMPORTANT: Clear any previous configuration\n g.clearAll();\n\n // Configure Gantt layout and appearance\n g.config.date_format = '%Y-%m-%d %H:%i';\n g.config.scale_unit = 'day';\n g.config.date_scale = '%d %M';\n g.config.subscales = [];\n g.config.show_progress = true;\n g.config.show_links = true;\n g.config.auto_types = true;\n g.config.readonly = true; // Read-only for now\n g.config.fit_tasks = true; // Auto-fit timeline to tasks\n\n // Disable auto-scheduling - we calculate dates ourselves based on dependencies\n g.config.auto_scheduling = false;\n\n // Grid configuration\n g.config.grid_width = 350;\n g.config.row_height = 36;\n g.config.scale_height = 0; // Hide date scale - dates are just for positioning\n\n // Layout configuration - ensure timeline is visible\n g.config.layout = {\n css: \"gantt_container\",\n rows: [\n {\n cols: [\n { view: \"grid\", group: \"grids\", scrollY: \"scrollVer\" },\n { resizer: true, width: 1 },\n { view: \"timeline\", scrollX: \"scrollHor\", scrollY: \"scrollVer\" },\n { view: \"scrollbar\", id: \"scrollVer\", group: \"vertical\" }\n ]\n },\n { view: \"scrollbar\", id: \"scrollHor\", group: \"horizontal\" }\n ]\n };\n\n // Column configuration - only show task names, hide dates\n g.config.columns = [\n { name: 'text', label: 'Task name', tree: true, width: '*' }\n ];\n\n // Initialize Gantt in the container\n g.init(this.ganttContainer.nativeElement);\n this.ganttInitialized = true;\n\n // Attach click event\n g.attachEvent('onTaskClick', (id: string) => {\n const originalTask = this.tasks.find(t => UUIDsEqual(t.ID, id));\n if (originalTask) {\n this.selectedTask = originalTask;\n this.taskClicked.emit(originalTask);\n }\n return true;\n });\n\n // Load data\n this.updateGanttData();\n\n // Force resize after data load to ensure proper rendering\n setTimeout(() => {\n g.setSizes();\n }, 0);\n\n // Expand and select after render completes\n setTimeout(() => {\n this.expandAllAndSelectRoot();\n }, 100);\n\n console.log('✅ DHTMLX Gantt initialized successfully');\n } catch (error) {\n console.error('❌ Error initializing Gantt:', error);\n }\n }\n\n private updateGanttData(): void {\n if (!this.ganttInitialized) return;\n\n try {\n console.log('📊 Updating Gantt data with', this.tasks.length, 'tasks');\n const g = this.ganttLib!;\n\n const ganttData = this.convertToGanttFormat(this.tasks);\n g.clearAll();\n g.parse(ganttData);\n\n // Log final parsed data for debugging\n console.log('📋 Gantt data after parse:', {\n tasks: g.getTaskByTime(),\n links: g.getLinks()\n });\n\n // Force resize after data update\n setTimeout(() => {\n g.setSizes();\n }, 0);\n\n // Expand and select after render completes\n setTimeout(() => {\n this.expandAllAndSelectRoot();\n }, 100);\n\n console.log('✅ Gantt data updated');\n } catch (error) {\n console.error('❌ Error updating Gantt data:', error);\n }\n }\n\n private convertToGanttFormat(tasks: MJTaskEntity[]): { data: GanttTask[], links: GanttLink[] } {\n const data: GanttTask[] = [];\n const links: GanttLink[] = [];\n\n console.log('🔍 Converting tasks:', tasks);\n console.log('🔗 Task dependencies:', this.taskDependencies);\n\n // Build a map of task ID to task for quick lookup\n const taskMap = new Map<string, MJTaskEntity>();\n tasks.forEach(t => taskMap.set(t.ID, t));\n\n // Build dependency map: taskId -> array of tasks it depends on\n const dependencyMap = new Map<string, string[]>();\n this.taskDependencies.forEach(dep => {\n if (!dependencyMap.has(dep.TaskID)) {\n dependencyMap.set(dep.TaskID, []);\n }\n dependencyMap.get(dep.TaskID)!.push(dep.DependsOnTaskID);\n });\n\n // Calculate start dates based on dependencies\n const taskStartDates = new Map<string, Date>();\n const baseDate = new Date();\n baseDate.setHours(0, 0, 0, 0);\n\n // Recursive function to calculate start date for a task\n const calculateStartDate = (taskId: string, visited = new Set<string>()): Date => {\n // Prevent circular dependencies\n if (visited.has(taskId)) {\n return new Date(baseDate);\n }\n visited.add(taskId);\n\n // If already calculated, return it\n if (taskStartDates.has(taskId)) {\n return taskStartDates.get(taskId)!;\n }\n\n const task = taskMap.get(taskId);\n if (!task) return new Date(baseDate);\n\n const dependencies = dependencyMap.get(taskId) || [];\n\n if (dependencies.length === 0) {\n // No dependencies - use base date or task's actual start date\n const startDate = task.StartedAt ? new Date(task.StartedAt) : new Date(baseDate);\n taskStartDates.set(taskId, startDate);\n return startDate;\n }\n\n // Has dependencies - start after the latest dependency ends\n let latestEnd = new Date(baseDate);\n for (const depId of dependencies) {\n const depTask = taskMap.get(depId);\n if (depTask) {\n const depStart = calculateStartDate(depId, new Set(visited));\n const depDuration = this.calculateDuration(depTask);\n const depEnd = new Date(depStart);\n depEnd.setDate(depEnd.getDate() + depDuration);\n\n if (depEnd > latestEnd) {\n latestEnd = depEnd;\n }\n }\n }\n\n taskStartDates.set(taskId, latestEnd);\n return latestEnd;\n };\n\n // Calculate start dates for all tasks\n tasks.forEach(task => calculateStartDate(task.ID));\n\n // Calculate min/max dates for timeline display range\n let minDate: Date | null = null;\n let maxDate: Date | null = null;\n\n tasks.forEach(task => {\n const startDate = taskStartDates.get(task.ID);\n if (startDate) {\n const duration = this.calculateDuration(task);\n const endDate = new Date(startDate);\n endDate.setDate(endDate.getDate() + duration);\n\n if (!minDate || startDate < minDate) {\n minDate = startDate;\n }\n if (!maxDate || endDate > maxDate) {\n maxDate = endDate;\n }\n }\n });\n\n // Add 1 day padding before and after\n const g = this.ganttLib!;\n if (minDate) {\n const paddedStart = new Date(minDate);\n paddedStart.setDate(paddedStart.getDate() - 1);\n g.config.start_date = paddedStart;\n }\n if (maxDate) {\n const paddedEnd = new Date(maxDate);\n paddedEnd.setDate(paddedEnd.getDate() + 1);\n g.config.end_date = paddedEnd;\n }\n\n // Now create Gantt tasks with calculated dates\n tasks.forEach((task) => {\n console.log('📝 Processing task:', {\n ID: task.ID,\n Name: task.Name,\n ParentID: task.ParentID\n });\n\n // Calculate progress (0-1 scale for DHTMLX)\n let progress = (task.PercentComplete || 0) / 100;\n if (task.Status === 'Complete') {\n progress = 1;\n }\n\n const duration = this.calculateDuration(task);\n const startDate = taskStartDates.get(task.ID) || new Date(baseDate);\n\n const ganttTask: Partial<GanttTask> & Pick<GanttTask, 'id'> = {\n id: task.ID,\n text: task.Name || 'Untitled Task',\n start_date: this.formatDateForDHTMLX(startDate) as unknown as Date,\n duration: duration,\n progress: progress\n };\n\n // Add parent relationship for tree structure (not dependency)\n if (task.ParentID) {\n ganttTask.parent = task.ParentID;\n } else {\n // Root tasks need parent: 0\n ganttTask.parent = 0;\n }\n\n console.log('✅ Created Gantt task:', ganttTask);\n data.push(ganttTask as GanttTask);\n });\n\n // Create links from MJTaskDependencyEntity records\n this.taskDependencies.forEach((dep, index) => {\n links.push({\n id: dep.ID || `link_${index}`,\n source: dep.DependsOnTaskID, // The task being depended on\n target: dep.TaskID, // The task that depends on it\n type: '0' // finish-to-start (DHTMLX type 0)\n });\n console.log(`🔗 Created link: ${dep.DependsOnTaskID} -> ${dep.TaskID}`);\n });\n\n console.log('📊 Final DHTMLX format:', { data, links });\n return { data, links };\n }\n\n private calculateDuration(task: MJTaskEntity): number {\n if (task.StartedAt && task.DueAt) {\n const startDate = new Date(task.StartedAt);\n const endDate = new Date(task.DueAt);\n return Math.max(1, Math.ceil((endDate.getTime() - startDate.getTime()) / (1000 * 60 * 60 * 24)));\n }\n return 1; // Default to 1 day\n }\n\n private formatDateForDHTMLX(date: Date): string {\n const year = date.getFullYear();\n const month = String(date.getMonth() + 1).padStart(2, '0');\n const day = String(date.getDate()).padStart(2, '0');\n return `${year}-${month}-${day} 00:00`;\n }\n\n public getAgentRunId(task: MJTaskEntity): string | null {\n return this.agentRunMap?.get(task.ID) || null;\n }\n\n public closeDetailPanel(): void {\n this.selectedTask = null;\n }\n\n public onOpenEntityRecord(event: { entityName: string; recordId: string }): void {\n this.openEntityRecord.emit(event);\n }\n\n public startResize(event: MouseEvent): void {\n this.isResizing = true;\n this.resizeStartX = event.clientX;\n this.resizeStartWidth = this.detailPanelWidth;\n event.preventDefault();\n }\n\n @HostListener('document:mousemove', ['$event'])\n handleResize(event: MouseEvent): void {\n if (!this.isResizing) return;\n\n const delta = this.resizeStartX - event.clientX;\n const newWidth = this.resizeStartWidth + delta;\n\n // Constrain width between min and max\n this.detailPanelWidth = Math.max(300, Math.min(600, newWidth));\n }\n\n @HostListener('document:mouseup')\n stopResize(): void {\n if (this.isResizing) {\n this.isResizing = false;\n // Resize gantt chart after panel resize completes\n setTimeout(() => {\n if (this.ganttInitialized && this.ganttLib) {\n this.ganttLib.setSizes();\n }\n }, 0);\n }\n }\n\n private expandAllAndSelectRoot(): void {\n try {\n const g = this.ganttLib!;\n\n // Expand all tasks\n g.eachTask((task: GanttTask) => {\n g.open(task.id);\n });\n\n // Find and select the root task (task with parent = 0)\n let rootTaskId: string | number | null = null;\n g.eachTask((task: GanttTask) => {\n if (task.parent === 0 || task.parent === '0') {\n rootTaskId = task.id;\n return false; // Stop iteration\n }\n });\n\n if (rootTaskId != null) {\n g.selectTask(rootTaskId);\n // Trigger task click event to open detail panel\n const originalTask = this.tasks.find(t => UUIDsEqual(t.ID, String(rootTaskId)));\n if (originalTask) {\n this.selectedTask = originalTask;\n this.taskClicked.emit(originalTask);\n }\n }\n\n console.log('✅ Expanded all tasks and selected root:', rootTaskId);\n } catch (error) {\n console.error('❌ Error expanding/selecting tasks:', error);\n }\n }\n}\n"]}
@@ -11,7 +11,8 @@ export class NgTasksModule {
11
11
  static ɵfac = function NgTasksModule_Factory(__ngFactoryType__) { return new (__ngFactoryType__ || NgTasksModule)(); };
12
12
  static ɵmod = /*@__PURE__*/ i0.ɵɵdefineNgModule({ type: NgTasksModule });
13
13
  static ɵinj = /*@__PURE__*/ i0.ɵɵdefineInjector({ imports: [TaskComponent,
14
- SimpleTaskViewerComponent] });
14
+ SimpleTaskViewerComponent,
15
+ GanttTaskViewerComponent] });
15
16
  }
16
17
  (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(NgTasksModule, [{
17
18
  type: NgModule,
@@ -1 +1 @@
1
- {"version":3,"file":"ng-tasks.module.js","sourceRoot":"","sources":["../../src/lib/ng-tasks.module.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACzC,OAAO,EAAE,aAAa,EAAE,MAAM,6BAA6B,CAAC;AAC5D,OAAO,EAAE,yBAAyB,EAAE,MAAM,2CAA2C,CAAC;AACtF,OAAO,EAAE,wBAAwB,EAAE,MAAM,0CAA0C,CAAC;;AAEpF;;;GAGG;AAaH,MAAM,OAAO,aAAa;uGAAb,aAAa;4DAAb,aAAa;gEAVtB,aAAa;YACb,yBAAyB;;iFAShB,aAAa;cAZzB,QAAQ;eAAC;gBACR,OAAO,EAAE;oBACP,aAAa;oBACb,yBAAyB;oBACzB,wBAAwB;iBACzB;gBACD,OAAO,EAAE;oBACP,aAAa;oBACb,yBAAyB;oBACzB,wBAAwB;iBACzB;aACF;;wFACY,aAAa,cAVtB,aAAa;QACb,yBAAyB;QACzB,wBAAwB,aAGxB,aAAa;QACb,yBAAyB;QACzB,wBAAwB","sourcesContent":["import { NgModule } from '@angular/core';\nimport { TaskComponent } from './components/task.component';\nimport { SimpleTaskViewerComponent } from './components/simple-task-viewer.component';\nimport { GanttTaskViewerComponent } from './components/gantt-task-viewer.component';\n\n/**\n * MemberJunction Tasks Module\n * Provides task visualization components with Gantt chart support\n */\n@NgModule({\n imports: [\n TaskComponent,\n SimpleTaskViewerComponent,\n GanttTaskViewerComponent\n ],\n exports: [\n TaskComponent,\n SimpleTaskViewerComponent,\n GanttTaskViewerComponent\n ]\n})\nexport class NgTasksModule { }\n"]}
1
+ {"version":3,"file":"ng-tasks.module.js","sourceRoot":"","sources":["../../src/lib/ng-tasks.module.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACzC,OAAO,EAAE,aAAa,EAAE,MAAM,6BAA6B,CAAC;AAC5D,OAAO,EAAE,yBAAyB,EAAE,MAAM,2CAA2C,CAAC;AACtF,OAAO,EAAE,wBAAwB,EAAE,MAAM,0CAA0C,CAAC;;AAEpF;;;GAGG;AAaH,MAAM,OAAO,aAAa;uGAAb,aAAa;4DAAb,aAAa;gEAVtB,aAAa;YACb,yBAAyB;YACzB,wBAAwB;;iFAQf,aAAa;cAZzB,QAAQ;eAAC;gBACR,OAAO,EAAE;oBACP,aAAa;oBACb,yBAAyB;oBACzB,wBAAwB;iBACzB;gBACD,OAAO,EAAE;oBACP,aAAa;oBACb,yBAAyB;oBACzB,wBAAwB;iBACzB;aACF;;wFACY,aAAa,cAVtB,aAAa;QACb,yBAAyB;QACzB,wBAAwB,aAGxB,aAAa;QACb,yBAAyB;QACzB,wBAAwB","sourcesContent":["import { NgModule } from '@angular/core';\nimport { TaskComponent } from './components/task.component';\nimport { SimpleTaskViewerComponent } from './components/simple-task-viewer.component';\nimport { GanttTaskViewerComponent } from './components/gantt-task-viewer.component';\n\n/**\n * MemberJunction Tasks Module\n * Provides task visualization components with Gantt chart support\n */\n@NgModule({\n imports: [\n TaskComponent,\n SimpleTaskViewerComponent,\n GanttTaskViewerComponent\n ],\n exports: [\n TaskComponent,\n SimpleTaskViewerComponent,\n GanttTaskViewerComponent\n ]\n})\nexport class NgTasksModule { }\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@memberjunction/ng-tasks",
3
- "version": "5.14.0",
3
+ "version": "5.16.0",
4
4
  "description": "MemberJunction: Angular components for task visualization and management with Gantt chart support",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -15,10 +15,11 @@
15
15
  "@angular/common": "21.1.3",
16
16
  "@angular/core": "21.1.3",
17
17
  "@angular/platform-browser": "21.1.3",
18
- "@memberjunction/core": "5.14.0",
19
- "@memberjunction/core-entities": "5.14.0",
20
- "@memberjunction/ai-core-plus": "5.14.0",
21
- "@memberjunction/ai-engine-base": "5.14.0",
18
+ "@memberjunction/core": "5.16.0",
19
+ "@memberjunction/core-entities": "5.16.0",
20
+ "@memberjunction/ai-core-plus": "5.16.0",
21
+ "@memberjunction/ai-engine-base": "5.16.0",
22
+ "@memberjunction/ng-shared-generic": "5.16.0",
22
23
  "dhtmlx-gantt": "^9.1.1",
23
24
  "rxjs": "^7.8.2"
24
25
  },
@@ -1,8 +1,9 @@
1
1
  import { Component, Input, Output, EventEmitter, OnChanges, AfterViewInit, ElementRef, ViewChild, OnDestroy, HostListener } from '@angular/core';
2
2
 
3
3
  import { MJTaskEntity, MJTaskDependencyEntity } from '@memberjunction/core-entities';
4
- import { gantt } from 'dhtmlx-gantt';
4
+ import type { GanttStatic, Task as GanttTask, Link as GanttLink } from 'dhtmlx-gantt';
5
5
  import { TaskDetailPanelComponent } from './task-detail-panel.component';
6
+ import { SharedGenericModule } from '@memberjunction/ng-shared-generic';
6
7
  import { UUIDsEqual } from '@memberjunction/global';
7
8
 
8
9
  /**
@@ -11,34 +12,42 @@ import { UUIDsEqual } from '@memberjunction/global';
11
12
  @Component({
12
13
  selector: 'mj-gantt-task-viewer',
13
14
  standalone: true,
14
- imports: [TaskDetailPanelComponent],
15
+ imports: [TaskDetailPanelComponent, SharedGenericModule],
15
16
  template: `
16
17
  <div class="gantt-task-viewer">
17
- @if (!tasks || tasks.length === 0) {
18
- <div class="no-tasks">
19
- <i class="fas fa-chart-gantt"></i>
20
- <p>No tasks to display in Gantt view</p>
18
+ @if (IsGanttLoading) {
19
+ <div class="loading-container">
20
+ <mj-loading text="Loading Gantt chart..."></mj-loading>
21
21
  </div>
22
22
  }
23
-
24
- @if (tasks && tasks.length > 0) {
25
- <div class="gantt-layout">
26
- <div #ganttContainer class="gantt-container"></div>
27
- @if (selectedTask) {
28
- <div class="gantt-resizer"
29
- (mousedown)="startResize($event)"></div>
30
- }
31
- @if (selectedTask) {
32
- <div class="task-detail-panel" [style.width.px]="detailPanelWidth">
33
- <mj-task-detail-panel
34
- [task]="selectedTask"
35
- [agentRunId]="getAgentRunId(selectedTask)"
36
- (closePanel)="closeDetailPanel()"
37
- (openEntityRecord)="onOpenEntityRecord($event)">
38
- </mj-task-detail-panel>
39
- </div>
40
- }
41
- </div>
23
+
24
+ @if (!IsGanttLoading) {
25
+ @if (!tasks || tasks.length === 0) {
26
+ <div class="no-tasks">
27
+ <i class="fas fa-chart-gantt"></i>
28
+ <p>No tasks to display in Gantt view</p>
29
+ </div>
30
+ }
31
+
32
+ @if (tasks && tasks.length > 0) {
33
+ <div class="gantt-layout">
34
+ <div #ganttContainer class="gantt-container"></div>
35
+ @if (selectedTask) {
36
+ <div class="gantt-resizer"
37
+ (mousedown)="startResize($event)"></div>
38
+ }
39
+ @if (selectedTask) {
40
+ <div class="task-detail-panel" [style.width.px]="detailPanelWidth">
41
+ <mj-task-detail-panel
42
+ [task]="selectedTask"
43
+ [agentRunId]="getAgentRunId(selectedTask)"
44
+ (closePanel)="closeDetailPanel()"
45
+ (openEntityRecord)="onOpenEntityRecord($event)">
46
+ </mj-task-detail-panel>
47
+ </div>
48
+ }
49
+ </div>
50
+ }
42
51
  }
43
52
  </div>
44
53
  `,
@@ -100,6 +109,13 @@ import { UUIDsEqual } from '@memberjunction/global';
100
109
  font-weight: 500;
101
110
  }
102
111
 
112
+ .loading-container {
113
+ display: flex;
114
+ justify-content: center;
115
+ align-items: center;
116
+ padding: 80px 20px;
117
+ }
118
+
103
119
  .no-tasks {
104
120
  text-align: center;
105
121
  padding: 80px 20px;
@@ -129,20 +145,31 @@ export class GanttTaskViewerComponent implements OnChanges, AfterViewInit, OnDes
129
145
 
130
146
  public selectedTask: MJTaskEntity | null = null;
131
147
  public detailPanelWidth: number = 400;
148
+ public IsGanttLoading = true;
132
149
 
150
+ private ganttLib: GanttStatic | null = null;
133
151
  private ganttInitialized = false;
134
152
  private isResizing = false;
135
153
  private resizeStartX = 0;
136
154
  private resizeStartWidth = 0;
137
155
 
138
- ngAfterViewInit() {
156
+ async ngAfterViewInit() {
139
157
  console.log('🔧 ngAfterViewInit called', {
140
158
  taskCount: this.tasks?.length || 0,
141
159
  hasContainer: !!this.ganttContainer
142
160
  });
143
161
 
144
- if (this.tasks && this.tasks.length > 0 && this.ganttContainer) {
145
- this.initGantt();
162
+ try {
163
+ const module = await import('dhtmlx-gantt');
164
+ this.ganttLib = module.gantt;
165
+ this.IsGanttLoading = false;
166
+
167
+ if (this.tasks && this.tasks.length > 0 && this.ganttContainer) {
168
+ this.initGantt();
169
+ }
170
+ } catch (error) {
171
+ console.error('Failed to load dhtmlx-gantt:', error);
172
+ this.IsGanttLoading = false;
146
173
  }
147
174
  }
148
175
 
@@ -153,6 +180,8 @@ export class GanttTaskViewerComponent implements OnChanges, AfterViewInit, OnDes
153
180
  taskCount: this.tasks?.length || 0
154
181
  });
155
182
 
183
+ if (!this.ganttLib) return; // Library not yet loaded
184
+
156
185
  if (this.ganttInitialized && this.ganttContainer) {
157
186
  this.updateGanttData();
158
187
  } else if (!this.ganttInitialized && this.ganttContainer && this.tasks && this.tasks.length > 0) {
@@ -163,8 +192,8 @@ export class GanttTaskViewerComponent implements OnChanges, AfterViewInit, OnDes
163
192
  }
164
193
 
165
194
  ngOnDestroy() {
166
- if (this.ganttInitialized) {
167
- gantt.clearAll();
195
+ if (this.ganttInitialized && this.ganttLib) {
196
+ this.ganttLib.clearAll();
168
197
  this.ganttInitialized = false;
169
198
  }
170
199
  }
@@ -172,31 +201,32 @@ export class GanttTaskViewerComponent implements OnChanges, AfterViewInit, OnDes
172
201
  private initGantt(): void {
173
202
  try {
174
203
  console.log('🎨 Initializing DHTMLX Gantt');
204
+ const g = this.ganttLib!;
175
205
 
176
206
  // IMPORTANT: Clear any previous configuration
177
- gantt.clearAll();
207
+ g.clearAll();
178
208
 
179
209
  // Configure Gantt layout and appearance
180
- gantt.config.date_format = '%Y-%m-%d %H:%i';
181
- gantt.config.scale_unit = 'day';
182
- gantt.config.date_scale = '%d %M';
183
- gantt.config.subscales = [];
184
- gantt.config.show_progress = true;
185
- gantt.config.show_links = true;
186
- gantt.config.auto_types = true;
187
- gantt.config.readonly = true; // Read-only for now
188
- gantt.config.fit_tasks = true; // Auto-fit timeline to tasks
210
+ g.config.date_format = '%Y-%m-%d %H:%i';
211
+ g.config.scale_unit = 'day';
212
+ g.config.date_scale = '%d %M';
213
+ g.config.subscales = [];
214
+ g.config.show_progress = true;
215
+ g.config.show_links = true;
216
+ g.config.auto_types = true;
217
+ g.config.readonly = true; // Read-only for now
218
+ g.config.fit_tasks = true; // Auto-fit timeline to tasks
189
219
 
190
220
  // Disable auto-scheduling - we calculate dates ourselves based on dependencies
191
- gantt.config.auto_scheduling = false;
221
+ g.config.auto_scheduling = false;
192
222
 
193
223
  // Grid configuration
194
- gantt.config.grid_width = 350;
195
- gantt.config.row_height = 36;
196
- gantt.config.scale_height = 0; // Hide date scale - dates are just for positioning
224
+ g.config.grid_width = 350;
225
+ g.config.row_height = 36;
226
+ g.config.scale_height = 0; // Hide date scale - dates are just for positioning
197
227
 
198
228
  // Layout configuration - ensure timeline is visible
199
- gantt.config.layout = {
229
+ g.config.layout = {
200
230
  css: "gantt_container",
201
231
  rows: [
202
232
  {
@@ -212,16 +242,16 @@ export class GanttTaskViewerComponent implements OnChanges, AfterViewInit, OnDes
212
242
  };
213
243
 
214
244
  // Column configuration - only show task names, hide dates
215
- gantt.config.columns = [
245
+ g.config.columns = [
216
246
  { name: 'text', label: 'Task name', tree: true, width: '*' }
217
247
  ];
218
248
 
219
249
  // Initialize Gantt in the container
220
- gantt.init(this.ganttContainer.nativeElement);
250
+ g.init(this.ganttContainer.nativeElement);
221
251
  this.ganttInitialized = true;
222
252
 
223
253
  // Attach click event
224
- gantt.attachEvent('onTaskClick', (id: string) => {
254
+ g.attachEvent('onTaskClick', (id: string) => {
225
255
  const originalTask = this.tasks.find(t => UUIDsEqual(t.ID, id));
226
256
  if (originalTask) {
227
257
  this.selectedTask = originalTask;
@@ -235,7 +265,7 @@ export class GanttTaskViewerComponent implements OnChanges, AfterViewInit, OnDes
235
265
 
236
266
  // Force resize after data load to ensure proper rendering
237
267
  setTimeout(() => {
238
- gantt.setSizes();
268
+ g.setSizes();
239
269
  }, 0);
240
270
 
241
271
  // Expand and select after render completes
@@ -254,20 +284,21 @@ export class GanttTaskViewerComponent implements OnChanges, AfterViewInit, OnDes
254
284
 
255
285
  try {
256
286
  console.log('📊 Updating Gantt data with', this.tasks.length, 'tasks');
287
+ const g = this.ganttLib!;
257
288
 
258
289
  const ganttData = this.convertToGanttFormat(this.tasks);
259
- gantt.clearAll();
260
- gantt.parse(ganttData);
290
+ g.clearAll();
291
+ g.parse(ganttData);
261
292
 
262
293
  // Log final parsed data for debugging
263
294
  console.log('📋 Gantt data after parse:', {
264
- tasks: gantt.getTaskByTime(),
265
- links: gantt.getLinks()
295
+ tasks: g.getTaskByTime(),
296
+ links: g.getLinks()
266
297
  });
267
298
 
268
299
  // Force resize after data update
269
300
  setTimeout(() => {
270
- gantt.setSizes();
301
+ g.setSizes();
271
302
  }, 0);
272
303
 
273
304
  // Expand and select after render completes
@@ -281,9 +312,9 @@ export class GanttTaskViewerComponent implements OnChanges, AfterViewInit, OnDes
281
312
  }
282
313
  }
283
314
 
284
- private convertToGanttFormat(tasks: MJTaskEntity[]): { data: any[], links: any[] } {
285
- const data: any[] = [];
286
- const links: any[] = [];
315
+ private convertToGanttFormat(tasks: MJTaskEntity[]): { data: GanttTask[], links: GanttLink[] } {
316
+ const data: GanttTask[] = [];
317
+ const links: GanttLink[] = [];
287
318
 
288
319
  console.log('🔍 Converting tasks:', tasks);
289
320
  console.log('🔗 Task dependencies:', this.taskDependencies);
@@ -375,15 +406,16 @@ export class GanttTaskViewerComponent implements OnChanges, AfterViewInit, OnDes
375
406
  });
376
407
 
377
408
  // Add 1 day padding before and after
409
+ const g = this.ganttLib!;
378
410
  if (minDate) {
379
411
  const paddedStart = new Date(minDate);
380
412
  paddedStart.setDate(paddedStart.getDate() - 1);
381
- gantt.config.start_date = paddedStart;
413
+ g.config.start_date = paddedStart;
382
414
  }
383
415
  if (maxDate) {
384
416
  const paddedEnd = new Date(maxDate);
385
417
  paddedEnd.setDate(paddedEnd.getDate() + 1);
386
- gantt.config.end_date = paddedEnd;
418
+ g.config.end_date = paddedEnd;
387
419
  }
388
420
 
389
421
  // Now create Gantt tasks with calculated dates
@@ -403,10 +435,10 @@ export class GanttTaskViewerComponent implements OnChanges, AfterViewInit, OnDes
403
435
  const duration = this.calculateDuration(task);
404
436
  const startDate = taskStartDates.get(task.ID) || new Date(baseDate);
405
437
 
406
- const ganttTask: any = {
438
+ const ganttTask: Partial<GanttTask> & Pick<GanttTask, 'id'> = {
407
439
  id: task.ID,
408
440
  text: task.Name || 'Untitled Task',
409
- start_date: this.formatDateForDHTMLX(startDate),
441
+ start_date: this.formatDateForDHTMLX(startDate) as unknown as Date,
410
442
  duration: duration,
411
443
  progress: progress
412
444
  };
@@ -420,7 +452,7 @@ export class GanttTaskViewerComponent implements OnChanges, AfterViewInit, OnDes
420
452
  }
421
453
 
422
454
  console.log('✅ Created Gantt task:', ganttTask);
423
- data.push(ganttTask);
455
+ data.push(ganttTask as GanttTask);
424
456
  });
425
457
 
426
458
  // Create links from MJTaskDependencyEntity records
@@ -490,8 +522,8 @@ export class GanttTaskViewerComponent implements OnChanges, AfterViewInit, OnDes
490
522
  this.isResizing = false;
491
523
  // Resize gantt chart after panel resize completes
492
524
  setTimeout(() => {
493
- if (this.ganttInitialized) {
494
- gantt.setSizes();
525
+ if (this.ganttInitialized && this.ganttLib) {
526
+ this.ganttLib.setSizes();
495
527
  }
496
528
  }, 0);
497
529
  }
@@ -499,31 +531,33 @@ export class GanttTaskViewerComponent implements OnChanges, AfterViewInit, OnDes
499
531
 
500
532
  private expandAllAndSelectRoot(): void {
501
533
  try {
534
+ const g = this.ganttLib!;
535
+
502
536
  // Expand all tasks
503
- gantt.eachTask((task: any) => {
504
- gantt.open(task.id);
537
+ g.eachTask((task: GanttTask) => {
538
+ g.open(task.id);
505
539
  });
506
540
 
507
541
  // Find and select the root task (task with parent = 0)
508
- let rootTask: any = null;
509
- gantt.eachTask((task: any) => {
542
+ let rootTaskId: string | number | null = null;
543
+ g.eachTask((task: GanttTask) => {
510
544
  if (task.parent === 0 || task.parent === '0') {
511
- rootTask = task;
545
+ rootTaskId = task.id;
512
546
  return false; // Stop iteration
513
547
  }
514
548
  });
515
549
 
516
- if (rootTask) {
517
- gantt.selectTask(rootTask.id);
550
+ if (rootTaskId != null) {
551
+ g.selectTask(rootTaskId);
518
552
  // Trigger task click event to open detail panel
519
- const originalTask = this.tasks.find(t => UUIDsEqual(t.ID, rootTask.id));
553
+ const originalTask = this.tasks.find(t => UUIDsEqual(t.ID, String(rootTaskId)));
520
554
  if (originalTask) {
521
555
  this.selectedTask = originalTask;
522
556
  this.taskClicked.emit(originalTask);
523
557
  }
524
558
  }
525
559
 
526
- console.log('✅ Expanded all tasks and selected root:', rootTask?.id);
560
+ console.log('✅ Expanded all tasks and selected root:', rootTaskId);
527
561
  } catch (error) {
528
562
  console.error('❌ Error expanding/selecting tasks:', error);
529
563
  }