@memberjunction/ng-dashboards 5.11.0 → 5.13.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.
Files changed (229) hide show
  1. package/dist/AI/components/agents/agent-configuration.component.d.ts +34 -2
  2. package/dist/AI/components/agents/agent-configuration.component.d.ts.map +1 -1
  3. package/dist/AI/components/agents/agent-configuration.component.js +586 -223
  4. package/dist/AI/components/agents/agent-configuration.component.js.map +1 -1
  5. package/dist/AI/components/agents/agent-editor.component.js +2 -2
  6. package/dist/AI/components/agents/agent-filter-panel.component.d.ts +8 -0
  7. package/dist/AI/components/agents/agent-filter-panel.component.d.ts.map +1 -1
  8. package/dist/AI/components/agents/agent-filter-panel.component.js +85 -52
  9. package/dist/AI/components/agents/agent-filter-panel.component.js.map +1 -1
  10. package/dist/AI/components/charts/performance-heatmap.component.d.ts +1 -0
  11. package/dist/AI/components/charts/performance-heatmap.component.d.ts.map +1 -1
  12. package/dist/AI/components/charts/performance-heatmap.component.js +27 -5
  13. package/dist/AI/components/charts/performance-heatmap.component.js.map +1 -1
  14. package/dist/AI/components/charts/time-series-chart.component.d.ts +5 -0
  15. package/dist/AI/components/charts/time-series-chart.component.d.ts.map +1 -1
  16. package/dist/AI/components/charts/time-series-chart.component.js +23 -8
  17. package/dist/AI/components/charts/time-series-chart.component.js.map +1 -1
  18. package/dist/AI/components/execution-monitoring.component.js +2 -2
  19. package/dist/AI/components/execution-monitoring.component.js.map +1 -1
  20. package/dist/AI/components/models/model-management.component.js +2 -2
  21. package/dist/AI/components/prompts/model-prompt-priority-matrix.component.js +2 -2
  22. package/dist/AI/components/prompts/prompt-filter-panel.component.js +2 -2
  23. package/dist/AI/components/prompts/prompt-management.component.js +3 -3
  24. package/dist/AI/components/prompts/prompt-management.component.js.map +1 -1
  25. package/dist/AI/components/prompts/prompt-version-control.component.js +2 -2
  26. package/dist/AI/components/requests/agent-requests-resource.component.d.ts +83 -0
  27. package/dist/AI/components/requests/agent-requests-resource.component.d.ts.map +1 -0
  28. package/dist/AI/components/requests/agent-requests-resource.component.js +547 -0
  29. package/dist/AI/components/requests/agent-requests-resource.component.js.map +1 -0
  30. package/dist/AI/components/system/system-config-filter-panel.component.js +2 -2
  31. package/dist/AI/components/system/system-configuration.component.js +2 -2
  32. package/dist/AI/components/widgets/kpi-card.component.js +7 -7
  33. package/dist/AI/components/widgets/kpi-card.component.js.map +1 -1
  34. package/dist/AI/components/widgets/live-execution-widget.component.d.ts.map +1 -1
  35. package/dist/AI/components/widgets/live-execution-widget.component.js +6 -6
  36. package/dist/AI/components/widgets/live-execution-widget.component.js.map +1 -1
  37. package/dist/AI/index.d.ts +1 -0
  38. package/dist/AI/index.d.ts.map +1 -1
  39. package/dist/AI/index.js +2 -0
  40. package/dist/AI/index.js.map +1 -1
  41. package/dist/APIKeys/api-applications-panel.component.js +3 -3
  42. package/dist/APIKeys/api-applications-panel.component.js.map +1 -1
  43. package/dist/APIKeys/api-key-create-dialog.component.js +3 -3
  44. package/dist/APIKeys/api-key-create-dialog.component.js.map +1 -1
  45. package/dist/APIKeys/api-key-edit-panel.component.js +1 -1
  46. package/dist/APIKeys/api-key-edit-panel.component.js.map +1 -1
  47. package/dist/APIKeys/api-key-list.component.js +3 -3
  48. package/dist/APIKeys/api-key-list.component.js.map +1 -1
  49. package/dist/APIKeys/api-keys-resource.component.js +1 -1
  50. package/dist/APIKeys/api-keys-resource.component.js.map +1 -1
  51. package/dist/APIKeys/api-scopes-panel.component.js +2 -2
  52. package/dist/APIKeys/api-usage-panel.component.js +2 -2
  53. package/dist/Actions/components/actions-overview.component.js +2 -2
  54. package/dist/Actions/components/execution-monitoring.component.js +2 -2
  55. package/dist/Actions/components/explorer/action-breadcrumb.component.js +2 -2
  56. package/dist/Actions/components/explorer/action-card.component.js +2 -2
  57. package/dist/Actions/components/explorer/action-explorer.component.js +2 -2
  58. package/dist/Actions/components/explorer/action-list-item.component.js +2 -2
  59. package/dist/Actions/components/explorer/action-toolbar.component.js +2 -2
  60. package/dist/Actions/components/explorer/action-tree-panel.component.js +2 -2
  61. package/dist/Actions/components/explorer/new-action-panel.component.js +2 -2
  62. package/dist/Actions/components/explorer/new-action-panel.component.js.map +1 -1
  63. package/dist/Actions/components/explorer/new-category-panel.component.js +2 -2
  64. package/dist/Actions/components/explorer/new-category-panel.component.js.map +1 -1
  65. package/dist/Communication/communication-dashboard.component.js +2 -2
  66. package/dist/Communication/communication-logs-resource.component.d.ts.map +1 -1
  67. package/dist/Communication/communication-logs-resource.component.js +3 -3
  68. package/dist/Communication/communication-logs-resource.component.js.map +1 -1
  69. package/dist/Communication/communication-monitor-resource.component.d.ts.map +1 -1
  70. package/dist/Communication/communication-monitor-resource.component.js +5 -5
  71. package/dist/Communication/communication-monitor-resource.component.js.map +1 -1
  72. package/dist/Communication/communication-providers-resource.component.d.ts.map +1 -1
  73. package/dist/Communication/communication-providers-resource.component.js +3 -3
  74. package/dist/Communication/communication-providers-resource.component.js.map +1 -1
  75. package/dist/Communication/communication-runs-resource.component.d.ts.map +1 -1
  76. package/dist/Communication/communication-runs-resource.component.js +3 -3
  77. package/dist/Communication/communication-runs-resource.component.js.map +1 -1
  78. package/dist/Communication/communication-templates-resource.component.js +2 -2
  79. package/dist/Communication/communication-templates-resource.component.js.map +1 -1
  80. package/dist/ComponentStudio/component-studio-dashboard.component.js +2 -2
  81. package/dist/ComponentStudio/components/ai-assistant/ai-assistant-panel.component.js +2 -2
  82. package/dist/ComponentStudio/components/artifact-load-dialog.component.js +2 -2
  83. package/dist/ComponentStudio/components/artifact-selection-dialog.component.js +2 -2
  84. package/dist/ComponentStudio/components/browser/component-browser.component.js +2 -2
  85. package/dist/ComponentStudio/components/editors/code-editor-panel.component.js +2 -2
  86. package/dist/ComponentStudio/components/editors/code-editor-panel.component.js.map +1 -1
  87. package/dist/ComponentStudio/components/editors/data-requirements-editor.component.js +2 -2
  88. package/dist/ComponentStudio/components/editors/data-requirements-editor.component.js.map +1 -1
  89. package/dist/ComponentStudio/components/editors/requirements-editor.component.js +2 -2
  90. package/dist/ComponentStudio/components/editors/requirements-editor.component.js.map +1 -1
  91. package/dist/ComponentStudio/components/editors/spec-editor.component.js +2 -2
  92. package/dist/ComponentStudio/components/editors/spec-editor.component.js.map +1 -1
  93. package/dist/ComponentStudio/components/new-component-dialog/new-component-dialog.component.js +2 -2
  94. package/dist/ComponentStudio/components/save-version-dialog/save-version-dialog.component.js +2 -2
  95. package/dist/ComponentStudio/components/save-version-dialog/save-version-dialog.component.js.map +1 -1
  96. package/dist/ComponentStudio/components/text-import-dialog.component.js +2 -2
  97. package/dist/ComponentStudio/components/text-import-dialog.component.js.map +1 -1
  98. package/dist/ComponentStudio/components/workspace/component-preview.component.js +2 -2
  99. package/dist/ComponentStudio/components/workspace/editor-tabs.component.js +2 -2
  100. package/dist/ComponentStudio/components/workspace/editor-tabs.component.js.map +1 -1
  101. package/dist/Credentials/components/credentials-audit-resource.component.js +9 -9
  102. package/dist/Credentials/components/credentials-audit-resource.component.js.map +1 -1
  103. package/dist/Credentials/components/credentials-categories-resource.component.d.ts.map +1 -1
  104. package/dist/Credentials/components/credentials-categories-resource.component.js +11 -3
  105. package/dist/Credentials/components/credentials-categories-resource.component.js.map +1 -1
  106. package/dist/Credentials/components/credentials-list-resource.component.js +2 -2
  107. package/dist/Credentials/components/credentials-overview-resource.component.d.ts.map +1 -1
  108. package/dist/Credentials/components/credentials-overview-resource.component.js +12 -11
  109. package/dist/Credentials/components/credentials-overview-resource.component.js.map +1 -1
  110. package/dist/Credentials/components/credentials-types-resource.component.js +9 -9
  111. package/dist/Credentials/components/credentials-types-resource.component.js.map +1 -1
  112. package/dist/Credentials/credentials-dashboard.component.js +2 -2
  113. package/dist/DashboardBrowser/dashboard-browser-resource.component.js +2 -2
  114. package/dist/DashboardBrowser/dashboard-share-dialog.component.js +2 -2
  115. package/dist/DataExplorer/components/filter-dialog/filter-dialog.component.js +2 -2
  116. package/dist/DataExplorer/components/navigation-panel/navigation-panel.component.js +2 -2
  117. package/dist/DataExplorer/components/view-selector/view-selector.component.js +2 -2
  118. package/dist/DataExplorer/data-explorer-dashboard.component.js +4 -4
  119. package/dist/DataExplorer/data-explorer-dashboard.component.js.map +1 -1
  120. package/dist/Home/home-dashboard.component.js +2 -2
  121. package/dist/Integration/components/activity/activity.component.d.ts +1 -1
  122. package/dist/Integration/components/activity/activity.component.d.ts.map +1 -1
  123. package/dist/Integration/components/activity/activity.component.js +5 -5
  124. package/dist/Integration/components/activity/activity.component.js.map +1 -1
  125. package/dist/Integration/components/connections/connections.component.d.ts +31 -2
  126. package/dist/Integration/components/connections/connections.component.d.ts.map +1 -1
  127. package/dist/Integration/components/connections/connections.component.js +753 -412
  128. package/dist/Integration/components/connections/connections.component.js.map +1 -1
  129. package/dist/Integration/components/mapping-workspace/mapping-workspace.component.js +3 -3
  130. package/dist/Integration/components/mapping-workspace/mapping-workspace.component.js.map +1 -1
  131. package/dist/Integration/components/overview/overview.component.d.ts +0 -1
  132. package/dist/Integration/components/overview/overview.component.d.ts.map +1 -1
  133. package/dist/Integration/components/overview/overview.component.js +3 -6
  134. package/dist/Integration/components/overview/overview.component.js.map +1 -1
  135. package/dist/Integration/components/pipelines/pipelines.component.js +3 -3
  136. package/dist/Integration/components/pipelines/pipelines.component.js.map +1 -1
  137. package/dist/Integration/components/schedules/schedules.component.d.ts +20 -0
  138. package/dist/Integration/components/schedules/schedules.component.d.ts.map +1 -1
  139. package/dist/Integration/components/schedules/schedules.component.js +97 -5
  140. package/dist/Integration/components/schedules/schedules.component.js.map +1 -1
  141. package/dist/Integration/components/visual-editor/visual-editor.component.js +2 -2
  142. package/dist/Integration/components/widgets/integration-card.component.d.ts.map +1 -1
  143. package/dist/Integration/components/widgets/integration-card.component.js +5 -1
  144. package/dist/Integration/components/widgets/integration-card.component.js.map +1 -1
  145. package/dist/Integration/components/widgets/run-history-panel.component.js +2 -2
  146. package/dist/Integration/components/widgets/run-history-panel.component.js.map +1 -1
  147. package/dist/Integration/integration.module.d.ts +2 -1
  148. package/dist/Integration/integration.module.d.ts.map +1 -1
  149. package/dist/Integration/integration.module.js +7 -3
  150. package/dist/Integration/integration.module.js.map +1 -1
  151. package/dist/Integration/services/integration-data.service.d.ts +27 -2
  152. package/dist/Integration/services/integration-data.service.d.ts.map +1 -1
  153. package/dist/Integration/services/integration-data.service.js +107 -4
  154. package/dist/Integration/services/integration-data.service.js.map +1 -1
  155. package/dist/Lists/components/lists-browse-resource.component.d.ts.map +1 -1
  156. package/dist/Lists/components/lists-browse-resource.component.js +25 -24
  157. package/dist/Lists/components/lists-browse-resource.component.js.map +1 -1
  158. package/dist/Lists/components/lists-categories-resource.component.js +2 -2
  159. package/dist/Lists/components/lists-categories-resource.component.js.map +1 -1
  160. package/dist/Lists/components/lists-my-lists-resource.component.d.ts.map +1 -1
  161. package/dist/Lists/components/lists-my-lists-resource.component.js +26 -25
  162. package/dist/Lists/components/lists-my-lists-resource.component.js.map +1 -1
  163. package/dist/Lists/components/lists-operations-resource.component.js +2 -2
  164. package/dist/Lists/components/lists-operations-resource.component.js.map +1 -1
  165. package/dist/Lists/components/venn-diagram/venn-diagram.component.js +3 -3
  166. package/dist/Lists/components/venn-diagram/venn-diagram.component.js.map +1 -1
  167. package/dist/MCP/components/mcp-connection-dialog.component.js +2 -2
  168. package/dist/MCP/components/mcp-log-detail-panel.component.js +2 -2
  169. package/dist/MCP/components/mcp-log-detail-panel.component.js.map +1 -1
  170. package/dist/MCP/components/mcp-server-dialog.component.js +2 -2
  171. package/dist/MCP/components/mcp-test-tool-dialog.component.js +2 -2
  172. package/dist/MCP/components/mcp-test-tool-dialog.component.js.map +1 -1
  173. package/dist/MCP/mcp-dashboard.component.js +2 -2
  174. package/dist/MCP/mcp-filter-panel.component.js +2 -2
  175. package/dist/QueryBrowser/query-browser-resource.component.js +7 -7
  176. package/dist/QueryBrowser/query-browser-resource.component.js.map +1 -1
  177. package/dist/Scheduling/components/index.d.ts +0 -1
  178. package/dist/Scheduling/components/index.d.ts.map +1 -1
  179. package/dist/Scheduling/components/index.js +0 -1
  180. package/dist/Scheduling/components/index.js.map +1 -1
  181. package/dist/Scheduling/components/scheduling-activity.component.js +2 -2
  182. package/dist/Scheduling/components/scheduling-jobs.component.d.ts +6 -9
  183. package/dist/Scheduling/components/scheduling-jobs.component.d.ts.map +1 -1
  184. package/dist/Scheduling/components/scheduling-jobs.component.js +118 -110
  185. package/dist/Scheduling/components/scheduling-jobs.component.js.map +1 -1
  186. package/dist/Scheduling/components/scheduling-overview.component.js +3 -3
  187. package/dist/Scheduling/components/scheduling-overview.component.js.map +1 -1
  188. package/dist/Scheduling/scheduling-dashboard.component.js +2 -2
  189. package/dist/SystemDiagnostics/system-diagnostics.component.js +4 -4
  190. package/dist/SystemDiagnostics/system-diagnostics.component.js.map +1 -1
  191. package/dist/Testing/components/testing-analytics.component.js +2 -2
  192. package/dist/Testing/components/testing-analytics.component.js.map +1 -1
  193. package/dist/Testing/components/testing-dashboard-tab.component.js +4 -4
  194. package/dist/Testing/components/testing-dashboard-tab.component.js.map +1 -1
  195. package/dist/Testing/components/testing-explorer.component.js +2 -2
  196. package/dist/Testing/components/testing-explorer.component.js.map +1 -1
  197. package/dist/Testing/components/testing-review.component.d.ts.map +1 -1
  198. package/dist/Testing/components/testing-review.component.js +5 -5
  199. package/dist/Testing/components/testing-review.component.js.map +1 -1
  200. package/dist/Testing/components/testing-runs.component.js +2 -2
  201. package/dist/Testing/components/testing-runs.component.js.map +1 -1
  202. package/dist/Testing/components/widgets/oracle-breakdown-table.component.js +2 -2
  203. package/dist/Testing/components/widgets/oracle-breakdown-table.component.js.map +1 -1
  204. package/dist/Testing/components/widgets/suite-tree.component.js +4 -4
  205. package/dist/Testing/components/widgets/suite-tree.component.js.map +1 -1
  206. package/dist/Testing/components/widgets/test-run-detail-panel.component.js +2 -2
  207. package/dist/Testing/components/widgets/test-run-detail-panel.component.js.map +1 -1
  208. package/dist/Testing/testing-dashboard.component.js +2 -2
  209. package/dist/VersionHistory/components/diff-resource.component.js +2 -2
  210. package/dist/VersionHistory/components/graph-resource.component.js +2 -2
  211. package/dist/VersionHistory/components/labels-resource.component.js +3 -3
  212. package/dist/VersionHistory/components/labels-resource.component.js.map +1 -1
  213. package/dist/VersionHistory/components/restore-resource.component.js +3 -3
  214. package/dist/VersionHistory/components/restore-resource.component.js.map +1 -1
  215. package/dist/__tests__/integration-data-service.test.js +1 -0
  216. package/dist/__tests__/integration-data-service.test.js.map +1 -1
  217. package/dist/module.d.ts +52 -49
  218. package/dist/module.d.ts.map +1 -1
  219. package/dist/module.js +25 -6
  220. package/dist/module.js.map +1 -1
  221. package/dist/public-api.d.ts +1 -1
  222. package/dist/public-api.d.ts.map +1 -1
  223. package/dist/public-api.js +1 -1
  224. package/dist/public-api.js.map +1 -1
  225. package/package.json +42 -40
  226. package/dist/Scheduling/components/job-slideout.component.d.ts +0 -45
  227. package/dist/Scheduling/components/job-slideout.component.d.ts.map +0 -1
  228. package/dist/Scheduling/components/job-slideout.component.js +0 -459
  229. package/dist/Scheduling/components/job-slideout.component.js.map +0 -1
@@ -16,6 +16,7 @@ interface ScheduleRow {
16
16
  LastScheduledRunAt: string | null;
17
17
  IsLocked: boolean;
18
18
  LockedAt: string | null;
19
+ ScheduledJobID: string | null;
19
20
  }
20
21
  interface TimelineMarker {
21
22
  Hour: number;
@@ -53,6 +54,8 @@ export declare class SchedulesComponent extends BaseResourceComponent implements
53
54
  private dataService;
54
55
  private cdr;
55
56
  private refreshTimer;
57
+ /** JobTypeID for "Integration Sync" scheduled job type (from MJ metadata) */
58
+ private readonly INTEGRATION_SYNC_JOB_TYPE_ID;
56
59
  ngOnInit(): Promise<void>;
57
60
  ngOnDestroy(): void;
58
61
  GetResourceDisplayName(_data: ResourceData): Promise<string>;
@@ -67,6 +70,23 @@ export declare class SchedulesComponent extends BaseResourceComponent implements
67
70
  SetCustomInterval(integrationID: string, event: Event): void;
68
71
  HasChanges(integrationID: string): boolean;
69
72
  SaveSchedule(integrationID: string): Promise<void>;
73
+ /**
74
+ * Creates, updates, or disables the ScheduledJob record linked to this integration.
75
+ * When enabled with a real schedule type, ensures an Active ScheduledJob exists.
76
+ * When disabled or set to Manual, marks the ScheduledJob as Disabled (if one exists).
77
+ */
78
+ private SyncScheduledJob;
79
+ /** Converts schedule type + config into a cron expression. */
80
+ private ResolveCronExpression;
81
+ /** Converts an interval in minutes to an equivalent cron expression. */
82
+ private IntervalToCron;
83
+ /**
84
+ * Creates a new ScheduledJob or updates the existing one.
85
+ * Returns the ID of the job (new or existing).
86
+ */
87
+ private UpsertScheduledJob;
88
+ /** Sets an existing ScheduledJob to Disabled so it stops firing. */
89
+ private DisableScheduledJob;
70
90
  RunNow(integrationID: string): Promise<void>;
71
91
  GetEffectiveScheduleEnabled(schedule: ScheduleRow): boolean;
72
92
  GetEffectiveScheduleType(schedule: ScheduleRow): 'Manual' | 'Interval' | 'Cron';
@@ -1 +1 @@
1
- {"version":3,"file":"schedules.component.d.ts","sourceRoot":"","sources":["../../../../src/Integration/components/schedules/schedules.component.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"schedules.component.d.ts","sourceRoot":"","sources":["../../../../src/Integration/components/schedules/schedules.component.ts"],"names":[],"mappings":"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"}
@@ -440,6 +440,8 @@ let SchedulesComponent = class SchedulesComponent extends BaseResourceComponent
440
440
  dataService = inject(IntegrationDataService);
441
441
  cdr = inject(ChangeDetectorRef);
442
442
  refreshTimer = null;
443
+ /** JobTypeID for "Integration Sync" scheduled job type (from MJ metadata) */
444
+ INTEGRATION_SYNC_JOB_TYPE_ID = '4CD34733-4751-4572-B946-17DF6CB3EC90';
443
445
  // ---------------------------------------------------------------------------
444
446
  // Lifecycle
445
447
  // ---------------------------------------------------------------------------
@@ -481,7 +483,7 @@ let SchedulesComponent = class SchedulesComponent extends BaseResourceComponent
481
483
  'ID', 'Name', 'Integration', 'Company', 'IsActive',
482
484
  'ScheduleEnabled', 'ScheduleType', 'ScheduleIntervalMinutes',
483
485
  'CronExpression', 'NextScheduledRunAt', 'LastScheduledRunAt',
484
- 'IsLocked', 'LockedAt'
486
+ 'IsLocked', 'LockedAt', 'ScheduledJobID'
485
487
  ],
486
488
  ResultType: 'simple'
487
489
  });
@@ -564,7 +566,12 @@ let SchedulesComponent = class SchedulesComponent extends BaseResourceComponent
564
566
  const md = new Metadata();
565
567
  const entity = await md.GetEntityObject('MJ: Company Integrations');
566
568
  await entity.Load(integrationID);
567
- // Use Set() method since scheduling fields are not in the generated class yet
569
+ // Resolve effective values (pending change wins over stored value)
570
+ const scheduleEnabled = changes.ScheduleEnabled ?? entity.Get('ScheduleEnabled');
571
+ const scheduleType = (changes.ScheduleType ?? entity.Get('ScheduleType'));
572
+ const cronExpression = changes.CronExpression ?? entity.Get('CronExpression');
573
+ const intervalMinutes = changes.ScheduleIntervalMinutes ?? entity.Get('ScheduleIntervalMinutes');
574
+ // Apply schedule fields to the entity
568
575
  if (changes.ScheduleEnabled !== undefined)
569
576
  entity.Set('ScheduleEnabled', changes.ScheduleEnabled);
570
577
  if (changes.ScheduleType !== undefined)
@@ -573,6 +580,8 @@ let SchedulesComponent = class SchedulesComponent extends BaseResourceComponent
573
580
  entity.Set('ScheduleIntervalMinutes', changes.ScheduleIntervalMinutes);
574
581
  if (changes.CronExpression !== undefined)
575
582
  entity.Set('CronExpression', changes.CronExpression);
583
+ // Create/update/disable the linked ScheduledJob so the scheduler actually fires
584
+ await this.SyncScheduledJob(entity, scheduleEnabled, scheduleType, cronExpression, intervalMinutes, integrationID);
576
585
  const saved = await entity.Save();
577
586
  if (saved) {
578
587
  this.PendingChanges.delete(integrationID);
@@ -590,6 +599,89 @@ let SchedulesComponent = class SchedulesComponent extends BaseResourceComponent
590
599
  this.cdr.detectChanges();
591
600
  }
592
601
  }
602
+ // ---------------------------------------------------------------------------
603
+ // Scheduled job sync helpers
604
+ // ---------------------------------------------------------------------------
605
+ /**
606
+ * Creates, updates, or disables the ScheduledJob record linked to this integration.
607
+ * When enabled with a real schedule type, ensures an Active ScheduledJob exists.
608
+ * When disabled or set to Manual, marks the ScheduledJob as Disabled (if one exists).
609
+ */
610
+ async SyncScheduledJob(entity, enabled, scheduleType, cronExpression, intervalMinutes, integrationID) {
611
+ const effectiveCron = this.ResolveCronExpression(scheduleType, cronExpression, intervalMinutes);
612
+ const existingJobID = entity.Get('ScheduledJobID');
613
+ const shouldBeActive = enabled && scheduleType !== 'Manual' && !!effectiveCron;
614
+ if (shouldBeActive) {
615
+ const jobID = await this.UpsertScheduledJob(existingJobID, entity, effectiveCron, integrationID);
616
+ if (jobID !== existingJobID) {
617
+ entity.Set('ScheduledJobID', jobID);
618
+ }
619
+ }
620
+ else if (existingJobID) {
621
+ await this.DisableScheduledJob(existingJobID);
622
+ }
623
+ }
624
+ /** Converts schedule type + config into a cron expression. */
625
+ ResolveCronExpression(scheduleType, cronExpression, intervalMinutes) {
626
+ if (scheduleType === 'Cron')
627
+ return cronExpression;
628
+ if (scheduleType === 'Interval' && intervalMinutes)
629
+ return this.IntervalToCron(intervalMinutes);
630
+ return null;
631
+ }
632
+ /** Converts an interval in minutes to an equivalent cron expression. */
633
+ IntervalToCron(minutes) {
634
+ if (minutes < 60)
635
+ return `*/${minutes} * * * *`;
636
+ const hours = minutes / 60;
637
+ if (Number.isInteger(hours))
638
+ return hours === 1 ? '0 * * * *' : `0 */${hours} * * *`;
639
+ return `*/${minutes} * * * *`;
640
+ }
641
+ /**
642
+ * Creates a new ScheduledJob or updates the existing one.
643
+ * Returns the ID of the job (new or existing).
644
+ */
645
+ async UpsertScheduledJob(existingJobID, entity, cronExpression, integrationID) {
646
+ const md = new Metadata();
647
+ if (existingJobID) {
648
+ const job = await md.GetEntityObject('MJ: Scheduled Jobs');
649
+ const loaded = await job.Load(existingJobID);
650
+ if (loaded) {
651
+ job.Set('CronExpression', cronExpression);
652
+ job.Set('Status', 'Active');
653
+ // Reset NextRunAt so the scheduler picks up the new cron immediately
654
+ job.Set('NextRunAt', new Date());
655
+ await job.Save();
656
+ return existingJobID;
657
+ }
658
+ }
659
+ // No existing job (or failed to load) — create a new one
660
+ const integrationName = entity.Get('Name') ?? entity.Get('Integration') ?? 'Integration';
661
+ const job = await md.GetEntityObject('MJ: Scheduled Jobs');
662
+ job.NewRecord();
663
+ job.Set('Name', `${integrationName} Sync`);
664
+ job.Set('JobTypeID', this.INTEGRATION_SYNC_JOB_TYPE_ID);
665
+ job.Set('CronExpression', cronExpression);
666
+ job.Set('Timezone', 'UTC');
667
+ job.Set('Status', 'Active');
668
+ job.Set('ConcurrencyMode', 'Skip');
669
+ job.Set('Configuration', JSON.stringify({ CompanyIntegrationID: integrationID }));
670
+ // Set NextRunAt to now so the scheduler fires it on next poll without needing a restart
671
+ job.Set('NextRunAt', new Date());
672
+ await job.Save();
673
+ return job.Get('ID');
674
+ }
675
+ /** Sets an existing ScheduledJob to Disabled so it stops firing. */
676
+ async DisableScheduledJob(jobID) {
677
+ const md = new Metadata();
678
+ const job = await md.GetEntityObject('MJ: Scheduled Jobs');
679
+ const loaded = await job.Load(jobID);
680
+ if (loaded) {
681
+ job.Set('Status', 'Disabled');
682
+ await job.Save();
683
+ }
684
+ }
593
685
  async RunNow(integrationID) {
594
686
  if (this.RunningID)
595
687
  return;
@@ -824,7 +916,7 @@ let SchedulesComponent = class SchedulesComponent extends BaseResourceComponent
824
916
  i0.ɵɵconditionalCreate(0, SchedulesComponent_Conditional_0_Template, 2, 0, "div", 0)(1, SchedulesComponent_Conditional_1_Template, 19, 6, "div", 1);
825
917
  } if (rf & 2) {
826
918
  i0.ɵɵconditional(ctx.IsLoading ? 0 : 1);
827
- } }, dependencies: [i1.LoadingComponent], styles: ["\n\n\n\n\n[_nghost-%COMP%] {\n display: block;\n height: 100%;\n overflow-y: auto;\n background: var(--mj-bg-page);\n}\n\n.schedules-root[_ngcontent-%COMP%] {\n padding: 24px;\n max-width: 1400px;\n margin: 0 auto;\n}\n\n.loading-container[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n justify-content: center;\n height: 300px;\n}\n\n\n\n\n\n\n.section-header[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n justify-content: space-between;\n margin-bottom: 24px;\n}\n\n.header-left[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 16px;\n flex-wrap: wrap;\n}\n\n.section-title[_ngcontent-%COMP%] {\n font-size: 20px;\n font-weight: 600;\n color: var(--mj-text-primary);\n margin: 0;\n display: flex;\n align-items: center;\n gap: 8px;\n}\n\n.section-title[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n color: var(--mj-brand-primary);\n}\n\n.header-meta[_ngcontent-%COMP%] {\n font-size: 13px;\n color: var(--mj-text-muted);\n padding: 2px 10px;\n background: var(--mj-bg-surface-active);\n border-radius: 12px;\n}\n\n.running-badge[_ngcontent-%COMP%] {\n background: var(--mj-color-warning-100);\n color: var(--mj-color-warning-600);\n}\n\n.running-badge[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n margin-right: 4px;\n}\n\n\n\n\n\n\n.btn[_ngcontent-%COMP%] {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n padding: 8px 16px;\n border-radius: 8px;\n font-size: 13px;\n font-weight: 500;\n cursor: pointer;\n border: none;\n transition: all 0.15s ease;\n white-space: nowrap;\n}\n\n.btn[_ngcontent-%COMP%]:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n}\n\n.btn-primary[_ngcontent-%COMP%] {\n background: var(--mj-brand-primary);\n color: var(--mj-bg-surface);\n}\n\n.btn-primary[_ngcontent-%COMP%]:hover:not(:disabled) {\n background: var(--mj-color-info-700);\n}\n\n.btn-outline[_ngcontent-%COMP%] {\n background: var(--mj-bg-surface);\n color: var(--mj-text-secondary);\n border: 1px solid var(--mj-border-strong);\n}\n\n.btn-outline[_ngcontent-%COMP%]:hover:not(:disabled) {\n background: var(--mj-bg-surface-hover);\n border-color: var(--mj-text-disabled);\n}\n\n\n\n\n\n\n.timeline-section[_ngcontent-%COMP%] {\n background: var(--mj-bg-surface);\n border-radius: 12px;\n padding: 20px 24px;\n margin-bottom: 24px;\n box-shadow: 0 1px 3px rgba(0, 0, 0, 0.06);\n border: 1px solid var(--mj-border-default);\n}\n\n.timeline-title[_ngcontent-%COMP%] {\n font-size: 14px;\n font-weight: 600;\n color: var(--mj-color-neutral-700);\n margin-bottom: 16px;\n display: flex;\n align-items: center;\n gap: 8px;\n}\n\n.timeline-title[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n color: var(--mj-text-muted);\n}\n\n.timeline-container[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n gap: 8px;\n}\n\n\n\n.timeline-grid-labels[_ngcontent-%COMP%] {\n display: flex;\n align-items: flex-end;\n height: 20px;\n margin-bottom: 4px;\n}\n\n.timeline-label-spacer[_ngcontent-%COMP%] {\n width: 200px;\n min-width: 200px;\n flex-shrink: 0;\n}\n\n.timeline-grid-track[_ngcontent-%COMP%] {\n flex: 1;\n position: relative;\n height: 20px;\n}\n\n.timeline-hour-label[_ngcontent-%COMP%] {\n position: absolute;\n font-size: 10px;\n color: var(--mj-text-disabled);\n transform: translateX(-50%);\n white-space: nowrap;\n}\n\n\n\n.timeline-row[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n height: 28px;\n}\n\n.timeline-row-label[_ngcontent-%COMP%] {\n width: 200px;\n min-width: 200px;\n flex-shrink: 0;\n font-size: 12px;\n font-weight: 500;\n color: var(--mj-text-secondary);\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n padding-right: 16px;\n}\n\n.timeline-track-container[_ngcontent-%COMP%] {\n flex: 1;\n position: relative;\n height: 28px;\n display: flex;\n align-items: center;\n}\n\n.timeline-track[_ngcontent-%COMP%] {\n position: absolute;\n left: 0;\n right: 0;\n height: 4px;\n background: var(--mj-border-default);\n border-radius: 2px;\n}\n\n.timeline-grid-line[_ngcontent-%COMP%] {\n position: absolute;\n top: 0;\n bottom: 0;\n width: 1px;\n background: var(--mj-border-subtle);\n z-index: 0;\n}\n\n\n\n.timeline-marker[_ngcontent-%COMP%] {\n position: absolute;\n width: 8px;\n height: 8px;\n border-radius: 50%;\n background: var(--mj-text-disabled);\n transform: translate(-50%, 0);\n z-index: 2;\n transition: all 0.2s ease;\n}\n\n.timeline-marker[_ngcontent-%COMP%]:hover {\n transform: translate(-50%, 0) scale(1.4);\n}\n\n.timeline-marker-next[_ngcontent-%COMP%] {\n width: 12px;\n height: 12px;\n background: var(--mj-brand-primary);\n box-shadow: 0 0 0 3px rgba(37, 99, 235, 0.2);\n}\n\n\n\n.timeline-now-line[_ngcontent-%COMP%] {\n position: absolute;\n left: 0;\n top: -2px;\n bottom: -2px;\n width: 2px;\n background: var(--mj-status-error);\n z-index: 3;\n border-radius: 1px;\n}\n\n\n\n.timeline-locked-indicator[_ngcontent-%COMP%] {\n position: absolute;\n left: -4px;\n font-size: 12px;\n color: var(--mj-color-warning-600);\n z-index: 4;\n animation: _ngcontent-%COMP%_pulse-opacity 1.5s ease-in-out infinite;\n}\n\n@keyframes _ngcontent-%COMP%_pulse-opacity {\n 0%, 100% { opacity: 1; }\n 50% { opacity: 0.4; }\n}\n\n\n\n\n\n\n.cards-section[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n gap: 16px;\n}\n\n.schedule-card[_ngcontent-%COMP%] {\n display: flex;\n align-items: flex-start;\n background: var(--mj-bg-surface);\n border-radius: 12px;\n padding: 24px;\n box-shadow: 0 1px 3px rgba(0, 0, 0, 0.06);\n border: 1px solid var(--mj-border-default);\n transition: box-shadow 0.2s ease;\n gap: 32px;\n}\n\n.schedule-card[_ngcontent-%COMP%]:hover {\n box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08);\n}\n\n.schedule-card-disabled[_ngcontent-%COMP%] {\n opacity: 0.6;\n}\n\n\n\n.card-info[_ngcontent-%COMP%] {\n display: flex;\n align-items: flex-start;\n gap: 16px;\n min-width: 220px;\n flex-shrink: 0;\n}\n\n.card-icon[_ngcontent-%COMP%] {\n width: 48px;\n height: 48px;\n border-radius: 12px;\n background: var(--mj-status-info-bg);\n display: flex;\n align-items: center;\n justify-content: center;\n flex-shrink: 0;\n}\n\n.card-icon[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n font-size: 20px;\n color: var(--mj-brand-primary);\n}\n\n.card-details[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n gap: 4px;\n}\n\n.card-name[_ngcontent-%COMP%] {\n font-size: 15px;\n font-weight: 600;\n color: var(--mj-text-primary);\n}\n\n.card-company[_ngcontent-%COMP%] {\n font-size: 12px;\n color: var(--mj-text-disabled);\n}\n\n.card-badges[_ngcontent-%COMP%] {\n display: flex;\n gap: 6px;\n margin-top: 4px;\n flex-wrap: wrap;\n}\n\n.badge[_ngcontent-%COMP%] {\n font-size: 11px;\n font-weight: 500;\n padding: 2px 8px;\n border-radius: 10px;\n display: inline-flex;\n align-items: center;\n gap: 4px;\n}\n\n.badge-success[_ngcontent-%COMP%] {\n background: var(--mj-status-success-bg);\n color: var(--mj-color-success-600);\n}\n\n.badge-gray[_ngcontent-%COMP%] {\n background: var(--mj-bg-surface-hover);\n color: var(--mj-text-muted);\n}\n\n.badge-running[_ngcontent-%COMP%] {\n background: var(--mj-color-warning-100);\n color: var(--mj-color-warning-600);\n}\n\n\n\n.card-config[_ngcontent-%COMP%] {\n flex: 1;\n min-width: 0;\n display: flex;\n flex-direction: column;\n gap: 12px;\n}\n\n.config-row[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n gap: 8px;\n}\n\n\n\n.toggle-label[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 10px;\n font-size: 13px;\n font-weight: 500;\n color: var(--mj-text-secondary);\n cursor: pointer;\n}\n\n.toggle-switch[_ngcontent-%COMP%] {\n position: relative;\n width: 40px;\n height: 22px;\n border-radius: 11px;\n background: var(--mj-color-neutral-300);\n border: none;\n cursor: pointer;\n transition: background 0.2s ease;\n padding: 0;\n flex-shrink: 0;\n}\n\n.toggle-switch.toggle-on[_ngcontent-%COMP%] {\n background: var(--mj-brand-primary);\n}\n\n.toggle-knob[_ngcontent-%COMP%] {\n position: absolute;\n top: 2px;\n left: 2px;\n width: 18px;\n height: 18px;\n border-radius: 50%;\n background: var(--mj-bg-surface);\n box-shadow: 0 1px 3px rgba(0, 0, 0, 0.15);\n transition: transform 0.2s ease;\n}\n\n.toggle-on[_ngcontent-%COMP%] .toggle-knob[_ngcontent-%COMP%] {\n transform: translateX(18px);\n}\n\n\n\n.type-pills[_ngcontent-%COMP%] {\n display: flex;\n gap: 4px;\n background: var(--mj-bg-surface-hover);\n border-radius: 8px;\n padding: 3px;\n width: fit-content;\n}\n\n.type-pill[_ngcontent-%COMP%] {\n padding: 5px 14px;\n border-radius: 6px;\n font-size: 12px;\n font-weight: 500;\n color: var(--mj-text-muted);\n background: transparent;\n border: none;\n cursor: pointer;\n transition: all 0.15s ease;\n}\n\n.type-pill[_ngcontent-%COMP%]:hover {\n color: var(--mj-color-neutral-700);\n}\n\n.type-pill-active[_ngcontent-%COMP%] {\n background: var(--mj-bg-surface);\n color: var(--mj-brand-primary);\n box-shadow: 0 1px 3px rgba(0, 0, 0, 0.08);\n}\n\n\n\n.interval-presets[_ngcontent-%COMP%] {\n display: flex;\n flex-wrap: wrap;\n gap: 6px;\n}\n\n.preset-btn[_ngcontent-%COMP%] {\n padding: 4px 12px;\n border-radius: 6px;\n font-size: 12px;\n font-weight: 500;\n color: var(--mj-text-muted);\n background: var(--mj-bg-surface-hover);\n border: 1px solid var(--mj-border-default);\n cursor: pointer;\n transition: all 0.15s ease;\n}\n\n.preset-btn[_ngcontent-%COMP%]:hover {\n border-color: var(--mj-text-disabled);\n color: var(--mj-color-neutral-700);\n}\n\n.preset-btn-active[_ngcontent-%COMP%] {\n background: var(--mj-status-info-bg);\n border-color: var(--mj-brand-primary);\n color: var(--mj-brand-primary);\n}\n\n\n\n.custom-interval[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 6px;\n margin-top: 4px;\n}\n\n.interval-input[_ngcontent-%COMP%] {\n width: 100px;\n padding: 5px 10px;\n border: 1px solid var(--mj-border-default);\n border-radius: 6px;\n font-size: 12px;\n color: var(--mj-color-neutral-700);\n outline: none;\n transition: border-color 0.15s ease;\n}\n\n.interval-input[_ngcontent-%COMP%]:focus {\n border-color: var(--mj-brand-primary);\n box-shadow: 0 0 0 2px rgba(37, 99, 235, 0.1);\n}\n\n.interval-unit[_ngcontent-%COMP%] {\n font-size: 12px;\n color: var(--mj-text-disabled);\n}\n\n\n\n.cron-presets[_ngcontent-%COMP%] {\n display: flex;\n flex-wrap: wrap;\n gap: 6px;\n}\n\n.cron-input-row[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 12px;\n margin-top: 4px;\n flex-wrap: wrap;\n}\n\n.cron-input[_ngcontent-%COMP%] {\n width: 180px;\n padding: 5px 10px;\n border: 1px solid var(--mj-border-default);\n border-radius: 6px;\n font-size: 12px;\n font-family: 'SF Mono', 'Fira Code', 'Cascadia Code', monospace;\n color: var(--mj-color-neutral-700);\n outline: none;\n transition: border-color 0.15s ease;\n}\n\n.cron-input[_ngcontent-%COMP%]:focus {\n border-color: var(--mj-brand-primary);\n box-shadow: 0 0 0 2px rgba(37, 99, 235, 0.1);\n}\n\n.cron-description[_ngcontent-%COMP%] {\n font-size: 12px;\n color: var(--mj-text-muted);\n font-style: italic;\n}\n\n\n\n.card-status[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n align-items: flex-end;\n gap: 16px;\n min-width: 180px;\n flex-shrink: 0;\n}\n\n.status-items[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n gap: 8px;\n text-align: right;\n}\n\n.status-item[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n gap: 2px;\n}\n\n.status-label[_ngcontent-%COMP%] {\n font-size: 11px;\n color: var(--mj-text-disabled);\n text-transform: uppercase;\n letter-spacing: 0.5px;\n}\n\n.status-value[_ngcontent-%COMP%] {\n font-size: 13px;\n font-weight: 500;\n color: var(--mj-color-neutral-700);\n}\n\n.status-running[_ngcontent-%COMP%] {\n color: var(--mj-color-warning-600);\n display: flex;\n align-items: center;\n justify-content: flex-end;\n gap: 6px;\n}\n\n.status-idle[_ngcontent-%COMP%] {\n color: var(--mj-color-success-600);\n}\n\n\n\n.card-actions[_ngcontent-%COMP%] {\n display: flex;\n gap: 8px;\n}\n\n.btn-save[_ngcontent-%COMP%] {\n animation: _ngcontent-%COMP%_slideIn 0.2s ease;\n}\n\n@keyframes _ngcontent-%COMP%_slideIn {\n from {\n opacity: 0;\n transform: translateX(8px);\n }\n to {\n opacity: 1;\n transform: translateX(0);\n }\n}\n\n.btn-run[_ngcontent-%COMP%] {\n font-size: 12px;\n padding: 6px 12px;\n}\n\n\n\n\n\n\n.empty-state[_ngcontent-%COMP%] {\n text-align: center;\n padding: 60px 24px;\n color: var(--mj-text-disabled);\n}\n\n.empty-state[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n font-size: 48px;\n margin-bottom: 16px;\n display: block;\n}\n\n.empty-state[_ngcontent-%COMP%] p[_ngcontent-%COMP%] {\n font-size: 15px;\n margin: 0;\n}\n\n\n\n\n\n\n@media (max-width: 900px) {\n .schedule-card[_ngcontent-%COMP%] {\n flex-direction: column;\n gap: 20px;\n }\n\n .card-info[_ngcontent-%COMP%] {\n min-width: 0;\n width: 100%;\n }\n\n .card-config[_ngcontent-%COMP%] {\n width: 100%;\n }\n\n .card-status[_ngcontent-%COMP%] {\n align-items: flex-start;\n width: 100%;\n min-width: 0;\n }\n\n .status-items[_ngcontent-%COMP%] {\n text-align: left;\n flex-direction: row;\n gap: 20px;\n flex-wrap: wrap;\n }\n\n .status-running[_ngcontent-%COMP%] {\n justify-content: flex-start;\n }\n\n .card-actions[_ngcontent-%COMP%] {\n width: 100%;\n }\n\n .card-actions[_ngcontent-%COMP%] .btn[_ngcontent-%COMP%] {\n flex: 1;\n justify-content: center;\n }\n}\n\n@media (max-width: 768px) {\n .timeline-section[_ngcontent-%COMP%] {\n display: none;\n }\n\n .schedules-root[_ngcontent-%COMP%] {\n padding: 16px;\n }\n\n .section-header[_ngcontent-%COMP%] {\n flex-direction: column;\n align-items: flex-start;\n gap: 12px;\n }\n\n .header-left[_ngcontent-%COMP%] {\n gap: 8px;\n }\n}"] });
919
+ } }, dependencies: [i1.LoadingComponent], styles: ["\n\n\n\n\n[_nghost-%COMP%] {\n display: block;\n height: 100%;\n overflow-y: auto;\n background: var(--mj-bg-page);\n}\n\n.schedules-root[_ngcontent-%COMP%] {\n padding: 24px;\n max-width: 1400px;\n margin: 0 auto;\n}\n\n.loading-container[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n justify-content: center;\n height: 300px;\n}\n\n\n\n\n\n\n.section-header[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n justify-content: space-between;\n margin-bottom: 24px;\n}\n\n.header-left[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 16px;\n flex-wrap: wrap;\n}\n\n.section-title[_ngcontent-%COMP%] {\n font-size: 20px;\n font-weight: 600;\n color: var(--mj-text-primary);\n margin: 0;\n display: flex;\n align-items: center;\n gap: 8px;\n}\n\n.section-title[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n color: var(--mj-brand-primary);\n}\n\n.header-meta[_ngcontent-%COMP%] {\n font-size: 13px;\n color: var(--mj-text-muted);\n padding: 2px 10px;\n background: var(--mj-bg-surface-active);\n border-radius: 12px;\n}\n\n.running-badge[_ngcontent-%COMP%] {\n background: var(--mj-color-warning-100);\n color: var(--mj-color-warning-600);\n}\n\n.running-badge[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n margin-right: 4px;\n}\n\n\n\n\n\n\n.btn[_ngcontent-%COMP%] {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n padding: 8px 16px;\n border-radius: 8px;\n font-size: 13px;\n font-weight: 500;\n cursor: pointer;\n border: none;\n transition: all 0.15s ease;\n white-space: nowrap;\n}\n\n.btn[_ngcontent-%COMP%]:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n}\n\n.btn-primary[_ngcontent-%COMP%] {\n background: var(--mj-brand-primary);\n color: var(--mj-bg-surface);\n}\n\n.btn-primary[_ngcontent-%COMP%]:hover:not(:disabled) {\n background: var(--mj-color-info-700);\n}\n\n.btn-outline[_ngcontent-%COMP%] {\n background: var(--mj-bg-surface);\n color: var(--mj-text-secondary);\n border: 1px solid var(--mj-border-strong);\n}\n\n.btn-outline[_ngcontent-%COMP%]:hover:not(:disabled) {\n background: var(--mj-bg-surface-hover);\n border-color: var(--mj-text-disabled);\n}\n\n\n\n\n\n\n.timeline-section[_ngcontent-%COMP%] {\n background: var(--mj-bg-surface);\n border-radius: 12px;\n padding: 20px 24px;\n margin-bottom: 24px;\n box-shadow: 0 1px 3px rgba(0, 0, 0, 0.06);\n border: 1px solid var(--mj-border-default);\n}\n\n.timeline-title[_ngcontent-%COMP%] {\n font-size: 14px;\n font-weight: 600;\n color: var(--mj-text-secondary);\n margin-bottom: 16px;\n display: flex;\n align-items: center;\n gap: 8px;\n}\n\n.timeline-title[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n color: var(--mj-text-muted);\n}\n\n.timeline-container[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n gap: 8px;\n}\n\n\n\n.timeline-grid-labels[_ngcontent-%COMP%] {\n display: flex;\n align-items: flex-end;\n height: 20px;\n margin-bottom: 4px;\n}\n\n.timeline-label-spacer[_ngcontent-%COMP%] {\n width: 200px;\n min-width: 200px;\n flex-shrink: 0;\n}\n\n.timeline-grid-track[_ngcontent-%COMP%] {\n flex: 1;\n position: relative;\n height: 20px;\n}\n\n.timeline-hour-label[_ngcontent-%COMP%] {\n position: absolute;\n font-size: 10px;\n color: var(--mj-text-disabled);\n transform: translateX(-50%);\n white-space: nowrap;\n}\n\n\n\n.timeline-row[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n height: 28px;\n}\n\n.timeline-row-label[_ngcontent-%COMP%] {\n width: 200px;\n min-width: 200px;\n flex-shrink: 0;\n font-size: 12px;\n font-weight: 500;\n color: var(--mj-text-secondary);\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n padding-right: 16px;\n}\n\n.timeline-track-container[_ngcontent-%COMP%] {\n flex: 1;\n position: relative;\n height: 28px;\n display: flex;\n align-items: center;\n}\n\n.timeline-track[_ngcontent-%COMP%] {\n position: absolute;\n left: 0;\n right: 0;\n height: 4px;\n background: var(--mj-border-default);\n border-radius: 2px;\n}\n\n.timeline-grid-line[_ngcontent-%COMP%] {\n position: absolute;\n top: 0;\n bottom: 0;\n width: 1px;\n background: var(--mj-border-subtle);\n z-index: 0;\n}\n\n\n\n.timeline-marker[_ngcontent-%COMP%] {\n position: absolute;\n width: 8px;\n height: 8px;\n border-radius: 50%;\n background: var(--mj-text-disabled);\n transform: translate(-50%, 0);\n z-index: 2;\n transition: all 0.2s ease;\n}\n\n.timeline-marker[_ngcontent-%COMP%]:hover {\n transform: translate(-50%, 0) scale(1.4);\n}\n\n.timeline-marker-next[_ngcontent-%COMP%] {\n width: 12px;\n height: 12px;\n background: var(--mj-brand-primary);\n box-shadow: 0 0 0 3px rgba(37, 99, 235, 0.2);\n}\n\n\n\n.timeline-now-line[_ngcontent-%COMP%] {\n position: absolute;\n left: 0;\n top: -2px;\n bottom: -2px;\n width: 2px;\n background: var(--mj-status-error);\n z-index: 3;\n border-radius: 1px;\n}\n\n\n\n.timeline-locked-indicator[_ngcontent-%COMP%] {\n position: absolute;\n left: -4px;\n font-size: 12px;\n color: var(--mj-color-warning-600);\n z-index: 4;\n animation: _ngcontent-%COMP%_pulse-opacity 1.5s ease-in-out infinite;\n}\n\n@keyframes _ngcontent-%COMP%_pulse-opacity {\n 0%, 100% { opacity: 1; }\n 50% { opacity: 0.4; }\n}\n\n\n\n\n\n\n.cards-section[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n gap: 16px;\n}\n\n.schedule-card[_ngcontent-%COMP%] {\n display: flex;\n align-items: flex-start;\n background: var(--mj-bg-surface);\n border-radius: 12px;\n padding: 24px;\n box-shadow: 0 1px 3px rgba(0, 0, 0, 0.06);\n border: 1px solid var(--mj-border-default);\n transition: box-shadow 0.2s ease;\n gap: 32px;\n}\n\n.schedule-card[_ngcontent-%COMP%]:hover {\n box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08);\n}\n\n.schedule-card-disabled[_ngcontent-%COMP%] {\n opacity: 0.6;\n}\n\n\n\n.card-info[_ngcontent-%COMP%] {\n display: flex;\n align-items: flex-start;\n gap: 16px;\n min-width: 220px;\n flex-shrink: 0;\n}\n\n.card-icon[_ngcontent-%COMP%] {\n width: 48px;\n height: 48px;\n border-radius: 12px;\n background: var(--mj-status-info-bg);\n display: flex;\n align-items: center;\n justify-content: center;\n flex-shrink: 0;\n}\n\n.card-icon[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n font-size: 20px;\n color: var(--mj-brand-primary);\n}\n\n.card-details[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n gap: 4px;\n}\n\n.card-name[_ngcontent-%COMP%] {\n font-size: 15px;\n font-weight: 600;\n color: var(--mj-text-primary);\n}\n\n.card-company[_ngcontent-%COMP%] {\n font-size: 12px;\n color: var(--mj-text-disabled);\n}\n\n.card-badges[_ngcontent-%COMP%] {\n display: flex;\n gap: 6px;\n margin-top: 4px;\n flex-wrap: wrap;\n}\n\n.badge[_ngcontent-%COMP%] {\n font-size: 11px;\n font-weight: 500;\n padding: 2px 8px;\n border-radius: 10px;\n display: inline-flex;\n align-items: center;\n gap: 4px;\n}\n\n.badge-success[_ngcontent-%COMP%] {\n background: var(--mj-status-success-bg);\n color: var(--mj-color-success-600);\n}\n\n.badge-gray[_ngcontent-%COMP%] {\n background: var(--mj-bg-surface-hover);\n color: var(--mj-text-muted);\n}\n\n.badge-running[_ngcontent-%COMP%] {\n background: var(--mj-color-warning-100);\n color: var(--mj-color-warning-600);\n}\n\n\n\n.card-config[_ngcontent-%COMP%] {\n flex: 1;\n min-width: 0;\n display: flex;\n flex-direction: column;\n gap: 12px;\n}\n\n.config-row[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n gap: 8px;\n}\n\n\n\n.toggle-label[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 10px;\n font-size: 13px;\n font-weight: 500;\n color: var(--mj-text-secondary);\n cursor: pointer;\n}\n\n.toggle-switch[_ngcontent-%COMP%] {\n position: relative;\n width: 40px;\n height: 22px;\n border-radius: 11px;\n background: var(--mj-color-neutral-300);\n border: none;\n cursor: pointer;\n transition: background 0.2s ease;\n padding: 0;\n flex-shrink: 0;\n}\n\n.toggle-switch.toggle-on[_ngcontent-%COMP%] {\n background: var(--mj-brand-primary);\n}\n\n.toggle-knob[_ngcontent-%COMP%] {\n position: absolute;\n top: 2px;\n left: 2px;\n width: 18px;\n height: 18px;\n border-radius: 50%;\n background: var(--mj-bg-surface);\n box-shadow: 0 1px 3px rgba(0, 0, 0, 0.15);\n transition: transform 0.2s ease;\n}\n\n.toggle-on[_ngcontent-%COMP%] .toggle-knob[_ngcontent-%COMP%] {\n transform: translateX(18px);\n}\n\n\n\n.type-pills[_ngcontent-%COMP%] {\n display: flex;\n gap: 4px;\n background: var(--mj-bg-surface-hover);\n border-radius: 8px;\n padding: 3px;\n width: fit-content;\n}\n\n.type-pill[_ngcontent-%COMP%] {\n padding: 5px 14px;\n border-radius: 6px;\n font-size: 12px;\n font-weight: 500;\n color: var(--mj-text-muted);\n background: transparent;\n border: none;\n cursor: pointer;\n transition: all 0.15s ease;\n}\n\n.type-pill[_ngcontent-%COMP%]:hover {\n color: var(--mj-text-secondary);\n}\n\n.type-pill-active[_ngcontent-%COMP%] {\n background: var(--mj-bg-surface);\n color: var(--mj-brand-primary);\n box-shadow: 0 1px 3px rgba(0, 0, 0, 0.08);\n}\n\n\n\n.interval-presets[_ngcontent-%COMP%] {\n display: flex;\n flex-wrap: wrap;\n gap: 6px;\n}\n\n.preset-btn[_ngcontent-%COMP%] {\n padding: 4px 12px;\n border-radius: 6px;\n font-size: 12px;\n font-weight: 500;\n color: var(--mj-text-muted);\n background: var(--mj-bg-surface-hover);\n border: 1px solid var(--mj-border-default);\n cursor: pointer;\n transition: all 0.15s ease;\n}\n\n.preset-btn[_ngcontent-%COMP%]:hover {\n border-color: var(--mj-text-disabled);\n color: var(--mj-text-secondary);\n}\n\n.preset-btn-active[_ngcontent-%COMP%] {\n background: var(--mj-status-info-bg);\n border-color: var(--mj-brand-primary);\n color: var(--mj-brand-primary);\n}\n\n\n\n.custom-interval[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 6px;\n margin-top: 4px;\n}\n\n.interval-input[_ngcontent-%COMP%] {\n width: 100px;\n padding: 5px 10px;\n border: 1px solid var(--mj-border-default);\n border-radius: 6px;\n font-size: 12px;\n color: var(--mj-text-secondary);\n outline: none;\n transition: border-color 0.15s ease;\n}\n\n.interval-input[_ngcontent-%COMP%]:focus {\n border-color: var(--mj-brand-primary);\n box-shadow: 0 0 0 2px rgba(37, 99, 235, 0.1);\n}\n\n.interval-unit[_ngcontent-%COMP%] {\n font-size: 12px;\n color: var(--mj-text-disabled);\n}\n\n\n\n.cron-presets[_ngcontent-%COMP%] {\n display: flex;\n flex-wrap: wrap;\n gap: 6px;\n}\n\n.cron-input-row[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 12px;\n margin-top: 4px;\n flex-wrap: wrap;\n}\n\n.cron-input[_ngcontent-%COMP%] {\n width: 180px;\n padding: 5px 10px;\n border: 1px solid var(--mj-border-default);\n border-radius: 6px;\n font-size: 12px;\n font-family: 'SF Mono', 'Fira Code', 'Cascadia Code', monospace;\n color: var(--mj-text-secondary);\n outline: none;\n transition: border-color 0.15s ease;\n}\n\n.cron-input[_ngcontent-%COMP%]:focus {\n border-color: var(--mj-brand-primary);\n box-shadow: 0 0 0 2px rgba(37, 99, 235, 0.1);\n}\n\n.cron-description[_ngcontent-%COMP%] {\n font-size: 12px;\n color: var(--mj-text-muted);\n font-style: italic;\n}\n\n\n\n.card-status[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n align-items: flex-end;\n gap: 16px;\n min-width: 180px;\n flex-shrink: 0;\n}\n\n.status-items[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n gap: 8px;\n text-align: right;\n}\n\n.status-item[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n gap: 2px;\n}\n\n.status-label[_ngcontent-%COMP%] {\n font-size: 11px;\n color: var(--mj-text-disabled);\n text-transform: uppercase;\n letter-spacing: 0.5px;\n}\n\n.status-value[_ngcontent-%COMP%] {\n font-size: 13px;\n font-weight: 500;\n color: var(--mj-text-secondary);\n}\n\n.status-running[_ngcontent-%COMP%] {\n color: var(--mj-color-warning-600);\n display: flex;\n align-items: center;\n justify-content: flex-end;\n gap: 6px;\n}\n\n.status-idle[_ngcontent-%COMP%] {\n color: var(--mj-color-success-600);\n}\n\n\n\n.card-actions[_ngcontent-%COMP%] {\n display: flex;\n gap: 8px;\n}\n\n.btn-save[_ngcontent-%COMP%] {\n animation: _ngcontent-%COMP%_slideIn 0.2s ease;\n}\n\n@keyframes _ngcontent-%COMP%_slideIn {\n from {\n opacity: 0;\n transform: translateX(8px);\n }\n to {\n opacity: 1;\n transform: translateX(0);\n }\n}\n\n.btn-run[_ngcontent-%COMP%] {\n font-size: 12px;\n padding: 6px 12px;\n}\n\n\n\n\n\n\n.empty-state[_ngcontent-%COMP%] {\n text-align: center;\n padding: 60px 24px;\n color: var(--mj-text-disabled);\n}\n\n.empty-state[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n font-size: 48px;\n margin-bottom: 16px;\n display: block;\n}\n\n.empty-state[_ngcontent-%COMP%] p[_ngcontent-%COMP%] {\n font-size: 15px;\n margin: 0;\n}\n\n\n\n\n\n\n@media (max-width: 900px) {\n .schedule-card[_ngcontent-%COMP%] {\n flex-direction: column;\n gap: 20px;\n }\n\n .card-info[_ngcontent-%COMP%] {\n min-width: 0;\n width: 100%;\n }\n\n .card-config[_ngcontent-%COMP%] {\n width: 100%;\n }\n\n .card-status[_ngcontent-%COMP%] {\n align-items: flex-start;\n width: 100%;\n min-width: 0;\n }\n\n .status-items[_ngcontent-%COMP%] {\n text-align: left;\n flex-direction: row;\n gap: 20px;\n flex-wrap: wrap;\n }\n\n .status-running[_ngcontent-%COMP%] {\n justify-content: flex-start;\n }\n\n .card-actions[_ngcontent-%COMP%] {\n width: 100%;\n }\n\n .card-actions[_ngcontent-%COMP%] .btn[_ngcontent-%COMP%] {\n flex: 1;\n justify-content: center;\n }\n}\n\n@media (max-width: 768px) {\n .timeline-section[_ngcontent-%COMP%] {\n display: none;\n }\n\n .schedules-root[_ngcontent-%COMP%] {\n padding: 16px;\n }\n\n .section-header[_ngcontent-%COMP%] {\n flex-direction: column;\n align-items: flex-start;\n gap: 12px;\n }\n\n .header-left[_ngcontent-%COMP%] {\n gap: 8px;\n }\n}"] });
828
920
  };
829
921
  SchedulesComponent = __decorate([
830
922
  RegisterClass(BaseResourceComponent, 'IntegrationSchedules')
@@ -832,9 +924,9 @@ SchedulesComponent = __decorate([
832
924
  export { SchedulesComponent };
833
925
  (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(SchedulesComponent, [{
834
926
  type: Component,
835
- args: [{ standalone: false, selector: 'app-integration-schedules', template: "<!-- Loading state -->\n@if (IsLoading) {\n <div class=\"loading-container\">\n <mj-loading text=\"Loading schedules...\"></mj-loading>\n </div>\n} @else {\n <div class=\"schedules-root\">\n\n <!-- ================================================================= -->\n <!-- HEADER -->\n <!-- ================================================================= -->\n <div class=\"section-header\">\n <div class=\"header-left\">\n <h2 class=\"section-title\">\n <i class=\"fa-solid fa-calendar-check\"></i>\n Sync Schedules\n </h2>\n <span class=\"header-meta\">{{ Schedules.length }} integration{{ Schedules.length === 1 ? '' : 's' }}</span>\n <span class=\"header-meta\">{{ ScheduledCount }} scheduled</span>\n @if (LockedCount > 0) {\n <span class=\"header-meta running-badge\">\n <i class=\"fa-solid fa-spinner fa-spin\"></i>\n {{ LockedCount }} running\n </span>\n }\n </div>\n <button class=\"btn btn-outline\" (click)=\"LoadData()\">\n <i class=\"fa-solid fa-arrows-rotate\"></i>\n Refresh\n </button>\n </div>\n\n <!-- ================================================================= -->\n <!-- TIMELINE (24-hour visual) -->\n <!-- ================================================================= -->\n @if (TimelineMarkers.length > 0) {\n <div class=\"timeline-section\">\n <div class=\"timeline-title\">\n <i class=\"fa-regular fa-clock\"></i>\n Next 24 Hours\n </div>\n <div class=\"timeline-container\">\n <!-- Hour grid labels -->\n <div class=\"timeline-grid-labels\">\n <div class=\"timeline-label-spacer\"></div>\n <div class=\"timeline-grid-track\">\n @for (hour of TimelineHours; track TrackHour($index, hour)) {\n @if (hour % 4 === 0) {\n <span class=\"timeline-hour-label\"\n [style.left.%]=\"ComputeTimelinePosition(hour, 0)\">\n {{ hour }}:00\n </span>\n }\n }\n </div>\n </div>\n\n <!-- Integration rows -->\n @for (row of TimelineMarkers; track TrackTimelineByID($index, row)) {\n <div class=\"timeline-row\">\n <div class=\"timeline-row-label\" [title]=\"row.IntegrationName\">\n {{ row.IntegrationName }}\n </div>\n <div class=\"timeline-track-container\">\n <!-- Hour grid lines -->\n @for (hour of TimelineHours; track TrackHour($index, hour)) {\n @if (hour % 4 === 0) {\n <div class=\"timeline-grid-line\"\n [style.left.%]=\"ComputeTimelinePosition(hour, 0)\">\n </div>\n }\n }\n\n <!-- Track bar -->\n <div class=\"timeline-track\"></div>\n\n <!-- Markers -->\n @for (marker of row.Markers; track TrackMarker($index, marker)) {\n <div class=\"timeline-marker\"\n [class.timeline-marker-next]=\"marker.IsNext\"\n [style.left.%]=\"ComputeTimelinePosition(marker.Hour, marker.Minute)\"\n [title]=\"marker.Hour + ':' + ('' + marker.Minute).padStart(2, '0')\">\n </div>\n }\n\n <!-- Locked indicator -->\n @if (row.IsLocked) {\n <div class=\"timeline-locked-indicator\">\n <i class=\"fa-solid fa-spinner fa-spin\"></i>\n </div>\n }\n\n <!-- Current time line -->\n <div class=\"timeline-now-line\"></div>\n </div>\n </div>\n }\n </div>\n </div>\n }\n\n <!-- ================================================================= -->\n <!-- SCHEDULE CARDS -->\n <!-- ================================================================= -->\n <div class=\"cards-section\">\n @for (schedule of Schedules; track TrackByID($index, schedule)) {\n <div class=\"schedule-card\" [class.schedule-card-disabled]=\"!schedule.IsActive\">\n\n <!-- LEFT: Icon + Info -->\n <div class=\"card-info\">\n <div class=\"card-icon\">\n <i [class]=\"GetIntegrationIcon(schedule.Integration || schedule.Name)\"></i>\n </div>\n <div class=\"card-details\">\n <div class=\"card-name\">{{ schedule.Integration || schedule.Name }}</div>\n <div class=\"card-company\">{{ schedule.Company }}</div>\n <div class=\"card-badges\">\n @if (schedule.IsActive) {\n <span class=\"badge badge-success\">Active</span>\n } @else {\n <span class=\"badge badge-gray\">Inactive</span>\n }\n @if (schedule.IsLocked) {\n <span class=\"badge badge-running\">\n <i class=\"fa-solid fa-spinner fa-spin\"></i>\n Running\n </span>\n }\n </div>\n </div>\n </div>\n\n <!-- CENTER: Schedule Configuration -->\n <div class=\"card-config\">\n <!-- Schedule toggle -->\n <div class=\"config-row\">\n <label class=\"toggle-label\">\n Auto-sync\n <button class=\"toggle-switch\"\n [class.toggle-on]=\"GetEffectiveScheduleEnabled(schedule)\"\n (click)=\"ToggleScheduleEnabled(schedule.ID)\"\n [attr.aria-checked]=\"GetEffectiveScheduleEnabled(schedule)\"\n role=\"switch\">\n <span class=\"toggle-knob\"></span>\n </button>\n </label>\n </div>\n\n @if (GetEffectiveScheduleEnabled(schedule)) {\n <!-- Schedule type pills -->\n <div class=\"config-row\">\n <div class=\"type-pills\">\n <button class=\"type-pill\"\n [class.type-pill-active]=\"GetEffectiveScheduleType(schedule) === 'Manual'\"\n (click)=\"SetScheduleType(schedule.ID, 'Manual')\">\n Manual\n </button>\n <button class=\"type-pill\"\n [class.type-pill-active]=\"GetEffectiveScheduleType(schedule) === 'Interval'\"\n (click)=\"SetScheduleType(schedule.ID, 'Interval')\">\n Interval\n </button>\n <button class=\"type-pill\"\n [class.type-pill-active]=\"GetEffectiveScheduleType(schedule) === 'Cron'\"\n (click)=\"SetScheduleType(schedule.ID, 'Cron')\">\n Cron\n </button>\n </div>\n </div>\n\n <!-- Interval config -->\n @if (GetEffectiveScheduleType(schedule) === 'Interval') {\n <div class=\"config-row\">\n <div class=\"interval-presets\">\n @for (preset of IntervalPresets; track TrackPresetByMinutes($index, preset)) {\n <button class=\"preset-btn\"\n [class.preset-btn-active]=\"IsIntervalSelected(schedule, preset.Minutes)\"\n (click)=\"SetInterval(schedule.ID, preset.Minutes)\">\n {{ preset.Label }}\n </button>\n }\n </div>\n <div class=\"custom-interval\">\n <input type=\"number\"\n class=\"interval-input\"\n placeholder=\"Custom min\"\n min=\"1\"\n [value]=\"GetEffectiveInterval(schedule)\"\n (change)=\"SetCustomInterval(schedule.ID, $event)\" />\n <span class=\"interval-unit\">min</span>\n </div>\n </div>\n }\n\n <!-- Cron config -->\n @if (GetEffectiveScheduleType(schedule) === 'Cron') {\n <div class=\"config-row\">\n <div class=\"cron-presets\">\n @for (preset of CronPresets; track TrackCronPresetByExpr($index, preset)) {\n <button class=\"preset-btn\"\n [class.preset-btn-active]=\"GetEffectiveCron(schedule) === preset.Expression\"\n (click)=\"SetCronExpression(schedule.ID, preset.Expression)\">\n {{ preset.Label }}\n </button>\n }\n </div>\n <div class=\"cron-input-row\">\n <input type=\"text\"\n class=\"cron-input\"\n placeholder=\"* * * * *\"\n [value]=\"GetEffectiveCron(schedule) ?? ''\"\n (change)=\"OnCronInputChange(schedule.ID, $event)\" />\n @if (GetEffectiveCron(schedule)) {\n <span class=\"cron-description\">\n {{ GetCronDescription(GetEffectiveCron(schedule)) }}\n </span>\n }\n </div>\n </div>\n }\n }\n </div>\n\n <!-- RIGHT: Status + Actions -->\n <div class=\"card-status\">\n <div class=\"status-items\">\n <div class=\"status-item\">\n <span class=\"status-label\">Next run</span>\n <span class=\"status-value\">{{ GetNextRunRelative(schedule) }}</span>\n </div>\n <div class=\"status-item\">\n <span class=\"status-label\">Last run</span>\n <span class=\"status-value\">{{ GetLastRunRelative(schedule) }}</span>\n </div>\n <div class=\"status-item\">\n <span class=\"status-label\">Status</span>\n @if (schedule.IsLocked) {\n <span class=\"status-value status-running\">\n <i class=\"fa-solid fa-spinner fa-spin\"></i>\n Running\n </span>\n } @else {\n <span class=\"status-value status-idle\">Idle</span>\n }\n </div>\n </div>\n\n <div class=\"card-actions\">\n @if (HasChanges(schedule.ID)) {\n <button class=\"btn btn-primary btn-save\"\n [disabled]=\"IsSaving(schedule.ID)\"\n (click)=\"SaveSchedule(schedule.ID)\">\n @if (IsSaving(schedule.ID)) {\n <i class=\"fa-solid fa-spinner fa-spin\"></i>\n Saving...\n } @else {\n <i class=\"fa-solid fa-check\"></i>\n Save\n }\n </button>\n }\n <button class=\"btn btn-outline btn-run\"\n [disabled]=\"IsRunning(schedule.ID) || schedule.IsLocked\"\n (click)=\"RunNow(schedule.ID)\">\n @if (IsRunning(schedule.ID)) {\n <i class=\"fa-solid fa-spinner fa-spin\"></i>\n Running...\n } @else {\n <i class=\"fa-solid fa-play\"></i>\n Run Now\n }\n </button>\n </div>\n </div>\n </div>\n }\n\n @if (Schedules.length === 0) {\n <div class=\"empty-state\">\n <i class=\"fa-solid fa-calendar-xmark\"></i>\n <p>No integrations found</p>\n </div>\n }\n </div>\n </div>\n}\n", styles: ["/* ==========================================================================\n Schedules Component\n ========================================================================== */\n\n:host {\n display: block;\n height: 100%;\n overflow-y: auto;\n background: var(--mj-bg-page);\n}\n\n.schedules-root {\n padding: 24px;\n max-width: 1400px;\n margin: 0 auto;\n}\n\n.loading-container {\n display: flex;\n align-items: center;\n justify-content: center;\n height: 300px;\n}\n\n/* --------------------------------------------------------------------------\n Header\n -------------------------------------------------------------------------- */\n\n.section-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n margin-bottom: 24px;\n}\n\n.header-left {\n display: flex;\n align-items: center;\n gap: 16px;\n flex-wrap: wrap;\n}\n\n.section-title {\n font-size: 20px;\n font-weight: 600;\n color: var(--mj-text-primary);\n margin: 0;\n display: flex;\n align-items: center;\n gap: 8px;\n}\n\n.section-title i {\n color: var(--mj-brand-primary);\n}\n\n.header-meta {\n font-size: 13px;\n color: var(--mj-text-muted);\n padding: 2px 10px;\n background: var(--mj-bg-surface-active);\n border-radius: 12px;\n}\n\n.running-badge {\n background: var(--mj-color-warning-100);\n color: var(--mj-color-warning-600);\n}\n\n.running-badge i {\n margin-right: 4px;\n}\n\n/* --------------------------------------------------------------------------\n Buttons (shared)\n -------------------------------------------------------------------------- */\n\n.btn {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n padding: 8px 16px;\n border-radius: 8px;\n font-size: 13px;\n font-weight: 500;\n cursor: pointer;\n border: none;\n transition: all 0.15s ease;\n white-space: nowrap;\n}\n\n.btn:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n}\n\n.btn-primary {\n background: var(--mj-brand-primary);\n color: var(--mj-bg-surface);\n}\n\n.btn-primary:hover:not(:disabled) {\n background: var(--mj-color-info-700);\n}\n\n.btn-outline {\n background: var(--mj-bg-surface);\n color: var(--mj-text-secondary);\n border: 1px solid var(--mj-border-strong);\n}\n\n.btn-outline:hover:not(:disabled) {\n background: var(--mj-bg-surface-hover);\n border-color: var(--mj-text-disabled);\n}\n\n/* --------------------------------------------------------------------------\n Timeline Section\n -------------------------------------------------------------------------- */\n\n.timeline-section {\n background: var(--mj-bg-surface);\n border-radius: 12px;\n padding: 20px 24px;\n margin-bottom: 24px;\n box-shadow: 0 1px 3px rgba(0, 0, 0, 0.06);\n border: 1px solid var(--mj-border-default);\n}\n\n.timeline-title {\n font-size: 14px;\n font-weight: 600;\n color: var(--mj-color-neutral-700);\n margin-bottom: 16px;\n display: flex;\n align-items: center;\n gap: 8px;\n}\n\n.timeline-title i {\n color: var(--mj-text-muted);\n}\n\n.timeline-container {\n display: flex;\n flex-direction: column;\n gap: 8px;\n}\n\n/* Grid labels row */\n.timeline-grid-labels {\n display: flex;\n align-items: flex-end;\n height: 20px;\n margin-bottom: 4px;\n}\n\n.timeline-label-spacer {\n width: 200px;\n min-width: 200px;\n flex-shrink: 0;\n}\n\n.timeline-grid-track {\n flex: 1;\n position: relative;\n height: 20px;\n}\n\n.timeline-hour-label {\n position: absolute;\n font-size: 10px;\n color: var(--mj-text-disabled);\n transform: translateX(-50%);\n white-space: nowrap;\n}\n\n/* Individual timeline rows */\n.timeline-row {\n display: flex;\n align-items: center;\n height: 28px;\n}\n\n.timeline-row-label {\n width: 200px;\n min-width: 200px;\n flex-shrink: 0;\n font-size: 12px;\n font-weight: 500;\n color: var(--mj-text-secondary);\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n padding-right: 16px;\n}\n\n.timeline-track-container {\n flex: 1;\n position: relative;\n height: 28px;\n display: flex;\n align-items: center;\n}\n\n.timeline-track {\n position: absolute;\n left: 0;\n right: 0;\n height: 4px;\n background: var(--mj-border-default);\n border-radius: 2px;\n}\n\n.timeline-grid-line {\n position: absolute;\n top: 0;\n bottom: 0;\n width: 1px;\n background: var(--mj-border-subtle);\n z-index: 0;\n}\n\n/* Markers */\n.timeline-marker {\n position: absolute;\n width: 8px;\n height: 8px;\n border-radius: 50%;\n background: var(--mj-text-disabled);\n transform: translate(-50%, 0);\n z-index: 2;\n transition: all 0.2s ease;\n}\n\n.timeline-marker:hover {\n transform: translate(-50%, 0) scale(1.4);\n}\n\n.timeline-marker-next {\n width: 12px;\n height: 12px;\n background: var(--mj-brand-primary);\n box-shadow: 0 0 0 3px rgba(37, 99, 235, 0.2);\n}\n\n/* Now line */\n.timeline-now-line {\n position: absolute;\n left: 0;\n top: -2px;\n bottom: -2px;\n width: 2px;\n background: var(--mj-status-error);\n z-index: 3;\n border-radius: 1px;\n}\n\n/* Locked indicator */\n.timeline-locked-indicator {\n position: absolute;\n left: -4px;\n font-size: 12px;\n color: var(--mj-color-warning-600);\n z-index: 4;\n animation: pulse-opacity 1.5s ease-in-out infinite;\n}\n\n@keyframes pulse-opacity {\n 0%, 100% { opacity: 1; }\n 50% { opacity: 0.4; }\n}\n\n/* --------------------------------------------------------------------------\n Schedule Cards\n -------------------------------------------------------------------------- */\n\n.cards-section {\n display: flex;\n flex-direction: column;\n gap: 16px;\n}\n\n.schedule-card {\n display: flex;\n align-items: flex-start;\n background: var(--mj-bg-surface);\n border-radius: 12px;\n padding: 24px;\n box-shadow: 0 1px 3px rgba(0, 0, 0, 0.06);\n border: 1px solid var(--mj-border-default);\n transition: box-shadow 0.2s ease;\n gap: 32px;\n}\n\n.schedule-card:hover {\n box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08);\n}\n\n.schedule-card-disabled {\n opacity: 0.6;\n}\n\n/* Card: Left (info) */\n.card-info {\n display: flex;\n align-items: flex-start;\n gap: 16px;\n min-width: 220px;\n flex-shrink: 0;\n}\n\n.card-icon {\n width: 48px;\n height: 48px;\n border-radius: 12px;\n background: var(--mj-status-info-bg);\n display: flex;\n align-items: center;\n justify-content: center;\n flex-shrink: 0;\n}\n\n.card-icon i {\n font-size: 20px;\n color: var(--mj-brand-primary);\n}\n\n.card-details {\n display: flex;\n flex-direction: column;\n gap: 4px;\n}\n\n.card-name {\n font-size: 15px;\n font-weight: 600;\n color: var(--mj-text-primary);\n}\n\n.card-company {\n font-size: 12px;\n color: var(--mj-text-disabled);\n}\n\n.card-badges {\n display: flex;\n gap: 6px;\n margin-top: 4px;\n flex-wrap: wrap;\n}\n\n.badge {\n font-size: 11px;\n font-weight: 500;\n padding: 2px 8px;\n border-radius: 10px;\n display: inline-flex;\n align-items: center;\n gap: 4px;\n}\n\n.badge-success {\n background: var(--mj-status-success-bg);\n color: var(--mj-color-success-600);\n}\n\n.badge-gray {\n background: var(--mj-bg-surface-hover);\n color: var(--mj-text-muted);\n}\n\n.badge-running {\n background: var(--mj-color-warning-100);\n color: var(--mj-color-warning-600);\n}\n\n/* Card: Center (config) */\n.card-config {\n flex: 1;\n min-width: 0;\n display: flex;\n flex-direction: column;\n gap: 12px;\n}\n\n.config-row {\n display: flex;\n flex-direction: column;\n gap: 8px;\n}\n\n/* Toggle switch */\n.toggle-label {\n display: flex;\n align-items: center;\n gap: 10px;\n font-size: 13px;\n font-weight: 500;\n color: var(--mj-text-secondary);\n cursor: pointer;\n}\n\n.toggle-switch {\n position: relative;\n width: 40px;\n height: 22px;\n border-radius: 11px;\n background: var(--mj-color-neutral-300);\n border: none;\n cursor: pointer;\n transition: background 0.2s ease;\n padding: 0;\n flex-shrink: 0;\n}\n\n.toggle-switch.toggle-on {\n background: var(--mj-brand-primary);\n}\n\n.toggle-knob {\n position: absolute;\n top: 2px;\n left: 2px;\n width: 18px;\n height: 18px;\n border-radius: 50%;\n background: var(--mj-bg-surface);\n box-shadow: 0 1px 3px rgba(0, 0, 0, 0.15);\n transition: transform 0.2s ease;\n}\n\n.toggle-on .toggle-knob {\n transform: translateX(18px);\n}\n\n/* Type pills */\n.type-pills {\n display: flex;\n gap: 4px;\n background: var(--mj-bg-surface-hover);\n border-radius: 8px;\n padding: 3px;\n width: fit-content;\n}\n\n.type-pill {\n padding: 5px 14px;\n border-radius: 6px;\n font-size: 12px;\n font-weight: 500;\n color: var(--mj-text-muted);\n background: transparent;\n border: none;\n cursor: pointer;\n transition: all 0.15s ease;\n}\n\n.type-pill:hover {\n color: var(--mj-color-neutral-700);\n}\n\n.type-pill-active {\n background: var(--mj-bg-surface);\n color: var(--mj-brand-primary);\n box-shadow: 0 1px 3px rgba(0, 0, 0, 0.08);\n}\n\n/* Interval presets */\n.interval-presets {\n display: flex;\n flex-wrap: wrap;\n gap: 6px;\n}\n\n.preset-btn {\n padding: 4px 12px;\n border-radius: 6px;\n font-size: 12px;\n font-weight: 500;\n color: var(--mj-text-muted);\n background: var(--mj-bg-surface-hover);\n border: 1px solid var(--mj-border-default);\n cursor: pointer;\n transition: all 0.15s ease;\n}\n\n.preset-btn:hover {\n border-color: var(--mj-text-disabled);\n color: var(--mj-color-neutral-700);\n}\n\n.preset-btn-active {\n background: var(--mj-status-info-bg);\n border-color: var(--mj-brand-primary);\n color: var(--mj-brand-primary);\n}\n\n/* Custom interval input */\n.custom-interval {\n display: flex;\n align-items: center;\n gap: 6px;\n margin-top: 4px;\n}\n\n.interval-input {\n width: 100px;\n padding: 5px 10px;\n border: 1px solid var(--mj-border-default);\n border-radius: 6px;\n font-size: 12px;\n color: var(--mj-color-neutral-700);\n outline: none;\n transition: border-color 0.15s ease;\n}\n\n.interval-input:focus {\n border-color: var(--mj-brand-primary);\n box-shadow: 0 0 0 2px rgba(37, 99, 235, 0.1);\n}\n\n.interval-unit {\n font-size: 12px;\n color: var(--mj-text-disabled);\n}\n\n/* Cron config */\n.cron-presets {\n display: flex;\n flex-wrap: wrap;\n gap: 6px;\n}\n\n.cron-input-row {\n display: flex;\n align-items: center;\n gap: 12px;\n margin-top: 4px;\n flex-wrap: wrap;\n}\n\n.cron-input {\n width: 180px;\n padding: 5px 10px;\n border: 1px solid var(--mj-border-default);\n border-radius: 6px;\n font-size: 12px;\n font-family: 'SF Mono', 'Fira Code', 'Cascadia Code', monospace;\n color: var(--mj-color-neutral-700);\n outline: none;\n transition: border-color 0.15s ease;\n}\n\n.cron-input:focus {\n border-color: var(--mj-brand-primary);\n box-shadow: 0 0 0 2px rgba(37, 99, 235, 0.1);\n}\n\n.cron-description {\n font-size: 12px;\n color: var(--mj-text-muted);\n font-style: italic;\n}\n\n/* Card: Right (status) */\n.card-status {\n display: flex;\n flex-direction: column;\n align-items: flex-end;\n gap: 16px;\n min-width: 180px;\n flex-shrink: 0;\n}\n\n.status-items {\n display: flex;\n flex-direction: column;\n gap: 8px;\n text-align: right;\n}\n\n.status-item {\n display: flex;\n flex-direction: column;\n gap: 2px;\n}\n\n.status-label {\n font-size: 11px;\n color: var(--mj-text-disabled);\n text-transform: uppercase;\n letter-spacing: 0.5px;\n}\n\n.status-value {\n font-size: 13px;\n font-weight: 500;\n color: var(--mj-color-neutral-700);\n}\n\n.status-running {\n color: var(--mj-color-warning-600);\n display: flex;\n align-items: center;\n justify-content: flex-end;\n gap: 6px;\n}\n\n.status-idle {\n color: var(--mj-color-success-600);\n}\n\n/* Card actions */\n.card-actions {\n display: flex;\n gap: 8px;\n}\n\n.btn-save {\n animation: slideIn 0.2s ease;\n}\n\n@keyframes slideIn {\n from {\n opacity: 0;\n transform: translateX(8px);\n }\n to {\n opacity: 1;\n transform: translateX(0);\n }\n}\n\n.btn-run {\n font-size: 12px;\n padding: 6px 12px;\n}\n\n/* --------------------------------------------------------------------------\n Empty State\n -------------------------------------------------------------------------- */\n\n.empty-state {\n text-align: center;\n padding: 60px 24px;\n color: var(--mj-text-disabled);\n}\n\n.empty-state i {\n font-size: 48px;\n margin-bottom: 16px;\n display: block;\n}\n\n.empty-state p {\n font-size: 15px;\n margin: 0;\n}\n\n/* --------------------------------------------------------------------------\n Responsive\n -------------------------------------------------------------------------- */\n\n@media (max-width: 900px) {\n .schedule-card {\n flex-direction: column;\n gap: 20px;\n }\n\n .card-info {\n min-width: 0;\n width: 100%;\n }\n\n .card-config {\n width: 100%;\n }\n\n .card-status {\n align-items: flex-start;\n width: 100%;\n min-width: 0;\n }\n\n .status-items {\n text-align: left;\n flex-direction: row;\n gap: 20px;\n flex-wrap: wrap;\n }\n\n .status-running {\n justify-content: flex-start;\n }\n\n .card-actions {\n width: 100%;\n }\n\n .card-actions .btn {\n flex: 1;\n justify-content: center;\n }\n}\n\n@media (max-width: 768px) {\n .timeline-section {\n display: none;\n }\n\n .schedules-root {\n padding: 16px;\n }\n\n .section-header {\n flex-direction: column;\n align-items: flex-start;\n gap: 12px;\n }\n\n .header-left {\n gap: 8px;\n }\n}\n"] }]
927
+ args: [{ standalone: false, selector: 'app-integration-schedules', template: "<!-- Loading state -->\n@if (IsLoading) {\n <div class=\"loading-container\">\n <mj-loading text=\"Loading schedules...\"></mj-loading>\n </div>\n} @else {\n <div class=\"schedules-root\">\n\n <!-- ================================================================= -->\n <!-- HEADER -->\n <!-- ================================================================= -->\n <div class=\"section-header\">\n <div class=\"header-left\">\n <h2 class=\"section-title\">\n <i class=\"fa-solid fa-calendar-check\"></i>\n Sync Schedules\n </h2>\n <span class=\"header-meta\">{{ Schedules.length }} integration{{ Schedules.length === 1 ? '' : 's' }}</span>\n <span class=\"header-meta\">{{ ScheduledCount }} scheduled</span>\n @if (LockedCount > 0) {\n <span class=\"header-meta running-badge\">\n <i class=\"fa-solid fa-spinner fa-spin\"></i>\n {{ LockedCount }} running\n </span>\n }\n </div>\n <button class=\"btn btn-outline\" (click)=\"LoadData()\">\n <i class=\"fa-solid fa-arrows-rotate\"></i>\n Refresh\n </button>\n </div>\n\n <!-- ================================================================= -->\n <!-- TIMELINE (24-hour visual) -->\n <!-- ================================================================= -->\n @if (TimelineMarkers.length > 0) {\n <div class=\"timeline-section\">\n <div class=\"timeline-title\">\n <i class=\"fa-regular fa-clock\"></i>\n Next 24 Hours\n </div>\n <div class=\"timeline-container\">\n <!-- Hour grid labels -->\n <div class=\"timeline-grid-labels\">\n <div class=\"timeline-label-spacer\"></div>\n <div class=\"timeline-grid-track\">\n @for (hour of TimelineHours; track TrackHour($index, hour)) {\n @if (hour % 4 === 0) {\n <span class=\"timeline-hour-label\"\n [style.left.%]=\"ComputeTimelinePosition(hour, 0)\">\n {{ hour }}:00\n </span>\n }\n }\n </div>\n </div>\n\n <!-- Integration rows -->\n @for (row of TimelineMarkers; track TrackTimelineByID($index, row)) {\n <div class=\"timeline-row\">\n <div class=\"timeline-row-label\" [title]=\"row.IntegrationName\">\n {{ row.IntegrationName }}\n </div>\n <div class=\"timeline-track-container\">\n <!-- Hour grid lines -->\n @for (hour of TimelineHours; track TrackHour($index, hour)) {\n @if (hour % 4 === 0) {\n <div class=\"timeline-grid-line\"\n [style.left.%]=\"ComputeTimelinePosition(hour, 0)\">\n </div>\n }\n }\n\n <!-- Track bar -->\n <div class=\"timeline-track\"></div>\n\n <!-- Markers -->\n @for (marker of row.Markers; track TrackMarker($index, marker)) {\n <div class=\"timeline-marker\"\n [class.timeline-marker-next]=\"marker.IsNext\"\n [style.left.%]=\"ComputeTimelinePosition(marker.Hour, marker.Minute)\"\n [title]=\"marker.Hour + ':' + ('' + marker.Minute).padStart(2, '0')\">\n </div>\n }\n\n <!-- Locked indicator -->\n @if (row.IsLocked) {\n <div class=\"timeline-locked-indicator\">\n <i class=\"fa-solid fa-spinner fa-spin\"></i>\n </div>\n }\n\n <!-- Current time line -->\n <div class=\"timeline-now-line\"></div>\n </div>\n </div>\n }\n </div>\n </div>\n }\n\n <!-- ================================================================= -->\n <!-- SCHEDULE CARDS -->\n <!-- ================================================================= -->\n <div class=\"cards-section\">\n @for (schedule of Schedules; track TrackByID($index, schedule)) {\n <div class=\"schedule-card\" [class.schedule-card-disabled]=\"!schedule.IsActive\">\n\n <!-- LEFT: Icon + Info -->\n <div class=\"card-info\">\n <div class=\"card-icon\">\n <i [class]=\"GetIntegrationIcon(schedule.Integration || schedule.Name)\"></i>\n </div>\n <div class=\"card-details\">\n <div class=\"card-name\">{{ schedule.Integration || schedule.Name }}</div>\n <div class=\"card-company\">{{ schedule.Company }}</div>\n <div class=\"card-badges\">\n @if (schedule.IsActive) {\n <span class=\"badge badge-success\">Active</span>\n } @else {\n <span class=\"badge badge-gray\">Inactive</span>\n }\n @if (schedule.IsLocked) {\n <span class=\"badge badge-running\">\n <i class=\"fa-solid fa-spinner fa-spin\"></i>\n Running\n </span>\n }\n </div>\n </div>\n </div>\n\n <!-- CENTER: Schedule Configuration -->\n <div class=\"card-config\">\n <!-- Schedule toggle -->\n <div class=\"config-row\">\n <label class=\"toggle-label\">\n Auto-sync\n <button class=\"toggle-switch\"\n [class.toggle-on]=\"GetEffectiveScheduleEnabled(schedule)\"\n (click)=\"ToggleScheduleEnabled(schedule.ID)\"\n [attr.aria-checked]=\"GetEffectiveScheduleEnabled(schedule)\"\n role=\"switch\">\n <span class=\"toggle-knob\"></span>\n </button>\n </label>\n </div>\n\n @if (GetEffectiveScheduleEnabled(schedule)) {\n <!-- Schedule type pills -->\n <div class=\"config-row\">\n <div class=\"type-pills\">\n <button class=\"type-pill\"\n [class.type-pill-active]=\"GetEffectiveScheduleType(schedule) === 'Manual'\"\n (click)=\"SetScheduleType(schedule.ID, 'Manual')\">\n Manual\n </button>\n <button class=\"type-pill\"\n [class.type-pill-active]=\"GetEffectiveScheduleType(schedule) === 'Interval'\"\n (click)=\"SetScheduleType(schedule.ID, 'Interval')\">\n Interval\n </button>\n <button class=\"type-pill\"\n [class.type-pill-active]=\"GetEffectiveScheduleType(schedule) === 'Cron'\"\n (click)=\"SetScheduleType(schedule.ID, 'Cron')\">\n Cron\n </button>\n </div>\n </div>\n\n <!-- Interval config -->\n @if (GetEffectiveScheduleType(schedule) === 'Interval') {\n <div class=\"config-row\">\n <div class=\"interval-presets\">\n @for (preset of IntervalPresets; track TrackPresetByMinutes($index, preset)) {\n <button class=\"preset-btn\"\n [class.preset-btn-active]=\"IsIntervalSelected(schedule, preset.Minutes)\"\n (click)=\"SetInterval(schedule.ID, preset.Minutes)\">\n {{ preset.Label }}\n </button>\n }\n </div>\n <div class=\"custom-interval\">\n <input type=\"number\"\n class=\"interval-input\"\n placeholder=\"Custom min\"\n min=\"1\"\n [value]=\"GetEffectiveInterval(schedule)\"\n (change)=\"SetCustomInterval(schedule.ID, $event)\" />\n <span class=\"interval-unit\">min</span>\n </div>\n </div>\n }\n\n <!-- Cron config -->\n @if (GetEffectiveScheduleType(schedule) === 'Cron') {\n <div class=\"config-row\">\n <div class=\"cron-presets\">\n @for (preset of CronPresets; track TrackCronPresetByExpr($index, preset)) {\n <button class=\"preset-btn\"\n [class.preset-btn-active]=\"GetEffectiveCron(schedule) === preset.Expression\"\n (click)=\"SetCronExpression(schedule.ID, preset.Expression)\">\n {{ preset.Label }}\n </button>\n }\n </div>\n <div class=\"cron-input-row\">\n <input type=\"text\"\n class=\"cron-input\"\n placeholder=\"* * * * *\"\n [value]=\"GetEffectiveCron(schedule) ?? ''\"\n (change)=\"OnCronInputChange(schedule.ID, $event)\" />\n @if (GetEffectiveCron(schedule)) {\n <span class=\"cron-description\">\n {{ GetCronDescription(GetEffectiveCron(schedule)) }}\n </span>\n }\n </div>\n </div>\n }\n }\n </div>\n\n <!-- RIGHT: Status + Actions -->\n <div class=\"card-status\">\n <div class=\"status-items\">\n <div class=\"status-item\">\n <span class=\"status-label\">Next run</span>\n <span class=\"status-value\">{{ GetNextRunRelative(schedule) }}</span>\n </div>\n <div class=\"status-item\">\n <span class=\"status-label\">Last run</span>\n <span class=\"status-value\">{{ GetLastRunRelative(schedule) }}</span>\n </div>\n <div class=\"status-item\">\n <span class=\"status-label\">Status</span>\n @if (schedule.IsLocked) {\n <span class=\"status-value status-running\">\n <i class=\"fa-solid fa-spinner fa-spin\"></i>\n Running\n </span>\n } @else {\n <span class=\"status-value status-idle\">Idle</span>\n }\n </div>\n </div>\n\n <div class=\"card-actions\">\n @if (HasChanges(schedule.ID)) {\n <button class=\"btn btn-primary btn-save\"\n [disabled]=\"IsSaving(schedule.ID)\"\n (click)=\"SaveSchedule(schedule.ID)\">\n @if (IsSaving(schedule.ID)) {\n <i class=\"fa-solid fa-spinner fa-spin\"></i>\n Saving...\n } @else {\n <i class=\"fa-solid fa-check\"></i>\n Save\n }\n </button>\n }\n <button class=\"btn btn-outline btn-run\"\n [disabled]=\"IsRunning(schedule.ID) || schedule.IsLocked\"\n (click)=\"RunNow(schedule.ID)\">\n @if (IsRunning(schedule.ID)) {\n <i class=\"fa-solid fa-spinner fa-spin\"></i>\n Running...\n } @else {\n <i class=\"fa-solid fa-play\"></i>\n Run Now\n }\n </button>\n </div>\n </div>\n </div>\n }\n\n @if (Schedules.length === 0) {\n <div class=\"empty-state\">\n <i class=\"fa-solid fa-calendar-xmark\"></i>\n <p>No integrations found</p>\n </div>\n }\n </div>\n </div>\n}\n", styles: ["/* ==========================================================================\n Schedules Component\n ========================================================================== */\n\n:host {\n display: block;\n height: 100%;\n overflow-y: auto;\n background: var(--mj-bg-page);\n}\n\n.schedules-root {\n padding: 24px;\n max-width: 1400px;\n margin: 0 auto;\n}\n\n.loading-container {\n display: flex;\n align-items: center;\n justify-content: center;\n height: 300px;\n}\n\n/* --------------------------------------------------------------------------\n Header\n -------------------------------------------------------------------------- */\n\n.section-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n margin-bottom: 24px;\n}\n\n.header-left {\n display: flex;\n align-items: center;\n gap: 16px;\n flex-wrap: wrap;\n}\n\n.section-title {\n font-size: 20px;\n font-weight: 600;\n color: var(--mj-text-primary);\n margin: 0;\n display: flex;\n align-items: center;\n gap: 8px;\n}\n\n.section-title i {\n color: var(--mj-brand-primary);\n}\n\n.header-meta {\n font-size: 13px;\n color: var(--mj-text-muted);\n padding: 2px 10px;\n background: var(--mj-bg-surface-active);\n border-radius: 12px;\n}\n\n.running-badge {\n background: var(--mj-color-warning-100);\n color: var(--mj-color-warning-600);\n}\n\n.running-badge i {\n margin-right: 4px;\n}\n\n/* --------------------------------------------------------------------------\n Buttons (shared)\n -------------------------------------------------------------------------- */\n\n.btn {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n padding: 8px 16px;\n border-radius: 8px;\n font-size: 13px;\n font-weight: 500;\n cursor: pointer;\n border: none;\n transition: all 0.15s ease;\n white-space: nowrap;\n}\n\n.btn:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n}\n\n.btn-primary {\n background: var(--mj-brand-primary);\n color: var(--mj-bg-surface);\n}\n\n.btn-primary:hover:not(:disabled) {\n background: var(--mj-color-info-700);\n}\n\n.btn-outline {\n background: var(--mj-bg-surface);\n color: var(--mj-text-secondary);\n border: 1px solid var(--mj-border-strong);\n}\n\n.btn-outline:hover:not(:disabled) {\n background: var(--mj-bg-surface-hover);\n border-color: var(--mj-text-disabled);\n}\n\n/* --------------------------------------------------------------------------\n Timeline Section\n -------------------------------------------------------------------------- */\n\n.timeline-section {\n background: var(--mj-bg-surface);\n border-radius: 12px;\n padding: 20px 24px;\n margin-bottom: 24px;\n box-shadow: 0 1px 3px rgba(0, 0, 0, 0.06);\n border: 1px solid var(--mj-border-default);\n}\n\n.timeline-title {\n font-size: 14px;\n font-weight: 600;\n color: var(--mj-text-secondary);\n margin-bottom: 16px;\n display: flex;\n align-items: center;\n gap: 8px;\n}\n\n.timeline-title i {\n color: var(--mj-text-muted);\n}\n\n.timeline-container {\n display: flex;\n flex-direction: column;\n gap: 8px;\n}\n\n/* Grid labels row */\n.timeline-grid-labels {\n display: flex;\n align-items: flex-end;\n height: 20px;\n margin-bottom: 4px;\n}\n\n.timeline-label-spacer {\n width: 200px;\n min-width: 200px;\n flex-shrink: 0;\n}\n\n.timeline-grid-track {\n flex: 1;\n position: relative;\n height: 20px;\n}\n\n.timeline-hour-label {\n position: absolute;\n font-size: 10px;\n color: var(--mj-text-disabled);\n transform: translateX(-50%);\n white-space: nowrap;\n}\n\n/* Individual timeline rows */\n.timeline-row {\n display: flex;\n align-items: center;\n height: 28px;\n}\n\n.timeline-row-label {\n width: 200px;\n min-width: 200px;\n flex-shrink: 0;\n font-size: 12px;\n font-weight: 500;\n color: var(--mj-text-secondary);\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n padding-right: 16px;\n}\n\n.timeline-track-container {\n flex: 1;\n position: relative;\n height: 28px;\n display: flex;\n align-items: center;\n}\n\n.timeline-track {\n position: absolute;\n left: 0;\n right: 0;\n height: 4px;\n background: var(--mj-border-default);\n border-radius: 2px;\n}\n\n.timeline-grid-line {\n position: absolute;\n top: 0;\n bottom: 0;\n width: 1px;\n background: var(--mj-border-subtle);\n z-index: 0;\n}\n\n/* Markers */\n.timeline-marker {\n position: absolute;\n width: 8px;\n height: 8px;\n border-radius: 50%;\n background: var(--mj-text-disabled);\n transform: translate(-50%, 0);\n z-index: 2;\n transition: all 0.2s ease;\n}\n\n.timeline-marker:hover {\n transform: translate(-50%, 0) scale(1.4);\n}\n\n.timeline-marker-next {\n width: 12px;\n height: 12px;\n background: var(--mj-brand-primary);\n box-shadow: 0 0 0 3px rgba(37, 99, 235, 0.2);\n}\n\n/* Now line */\n.timeline-now-line {\n position: absolute;\n left: 0;\n top: -2px;\n bottom: -2px;\n width: 2px;\n background: var(--mj-status-error);\n z-index: 3;\n border-radius: 1px;\n}\n\n/* Locked indicator */\n.timeline-locked-indicator {\n position: absolute;\n left: -4px;\n font-size: 12px;\n color: var(--mj-color-warning-600);\n z-index: 4;\n animation: pulse-opacity 1.5s ease-in-out infinite;\n}\n\n@keyframes pulse-opacity {\n 0%, 100% { opacity: 1; }\n 50% { opacity: 0.4; }\n}\n\n/* --------------------------------------------------------------------------\n Schedule Cards\n -------------------------------------------------------------------------- */\n\n.cards-section {\n display: flex;\n flex-direction: column;\n gap: 16px;\n}\n\n.schedule-card {\n display: flex;\n align-items: flex-start;\n background: var(--mj-bg-surface);\n border-radius: 12px;\n padding: 24px;\n box-shadow: 0 1px 3px rgba(0, 0, 0, 0.06);\n border: 1px solid var(--mj-border-default);\n transition: box-shadow 0.2s ease;\n gap: 32px;\n}\n\n.schedule-card:hover {\n box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08);\n}\n\n.schedule-card-disabled {\n opacity: 0.6;\n}\n\n/* Card: Left (info) */\n.card-info {\n display: flex;\n align-items: flex-start;\n gap: 16px;\n min-width: 220px;\n flex-shrink: 0;\n}\n\n.card-icon {\n width: 48px;\n height: 48px;\n border-radius: 12px;\n background: var(--mj-status-info-bg);\n display: flex;\n align-items: center;\n justify-content: center;\n flex-shrink: 0;\n}\n\n.card-icon i {\n font-size: 20px;\n color: var(--mj-brand-primary);\n}\n\n.card-details {\n display: flex;\n flex-direction: column;\n gap: 4px;\n}\n\n.card-name {\n font-size: 15px;\n font-weight: 600;\n color: var(--mj-text-primary);\n}\n\n.card-company {\n font-size: 12px;\n color: var(--mj-text-disabled);\n}\n\n.card-badges {\n display: flex;\n gap: 6px;\n margin-top: 4px;\n flex-wrap: wrap;\n}\n\n.badge {\n font-size: 11px;\n font-weight: 500;\n padding: 2px 8px;\n border-radius: 10px;\n display: inline-flex;\n align-items: center;\n gap: 4px;\n}\n\n.badge-success {\n background: var(--mj-status-success-bg);\n color: var(--mj-color-success-600);\n}\n\n.badge-gray {\n background: var(--mj-bg-surface-hover);\n color: var(--mj-text-muted);\n}\n\n.badge-running {\n background: var(--mj-color-warning-100);\n color: var(--mj-color-warning-600);\n}\n\n/* Card: Center (config) */\n.card-config {\n flex: 1;\n min-width: 0;\n display: flex;\n flex-direction: column;\n gap: 12px;\n}\n\n.config-row {\n display: flex;\n flex-direction: column;\n gap: 8px;\n}\n\n/* Toggle switch */\n.toggle-label {\n display: flex;\n align-items: center;\n gap: 10px;\n font-size: 13px;\n font-weight: 500;\n color: var(--mj-text-secondary);\n cursor: pointer;\n}\n\n.toggle-switch {\n position: relative;\n width: 40px;\n height: 22px;\n border-radius: 11px;\n background: var(--mj-color-neutral-300);\n border: none;\n cursor: pointer;\n transition: background 0.2s ease;\n padding: 0;\n flex-shrink: 0;\n}\n\n.toggle-switch.toggle-on {\n background: var(--mj-brand-primary);\n}\n\n.toggle-knob {\n position: absolute;\n top: 2px;\n left: 2px;\n width: 18px;\n height: 18px;\n border-radius: 50%;\n background: var(--mj-bg-surface);\n box-shadow: 0 1px 3px rgba(0, 0, 0, 0.15);\n transition: transform 0.2s ease;\n}\n\n.toggle-on .toggle-knob {\n transform: translateX(18px);\n}\n\n/* Type pills */\n.type-pills {\n display: flex;\n gap: 4px;\n background: var(--mj-bg-surface-hover);\n border-radius: 8px;\n padding: 3px;\n width: fit-content;\n}\n\n.type-pill {\n padding: 5px 14px;\n border-radius: 6px;\n font-size: 12px;\n font-weight: 500;\n color: var(--mj-text-muted);\n background: transparent;\n border: none;\n cursor: pointer;\n transition: all 0.15s ease;\n}\n\n.type-pill:hover {\n color: var(--mj-text-secondary);\n}\n\n.type-pill-active {\n background: var(--mj-bg-surface);\n color: var(--mj-brand-primary);\n box-shadow: 0 1px 3px rgba(0, 0, 0, 0.08);\n}\n\n/* Interval presets */\n.interval-presets {\n display: flex;\n flex-wrap: wrap;\n gap: 6px;\n}\n\n.preset-btn {\n padding: 4px 12px;\n border-radius: 6px;\n font-size: 12px;\n font-weight: 500;\n color: var(--mj-text-muted);\n background: var(--mj-bg-surface-hover);\n border: 1px solid var(--mj-border-default);\n cursor: pointer;\n transition: all 0.15s ease;\n}\n\n.preset-btn:hover {\n border-color: var(--mj-text-disabled);\n color: var(--mj-text-secondary);\n}\n\n.preset-btn-active {\n background: var(--mj-status-info-bg);\n border-color: var(--mj-brand-primary);\n color: var(--mj-brand-primary);\n}\n\n/* Custom interval input */\n.custom-interval {\n display: flex;\n align-items: center;\n gap: 6px;\n margin-top: 4px;\n}\n\n.interval-input {\n width: 100px;\n padding: 5px 10px;\n border: 1px solid var(--mj-border-default);\n border-radius: 6px;\n font-size: 12px;\n color: var(--mj-text-secondary);\n outline: none;\n transition: border-color 0.15s ease;\n}\n\n.interval-input:focus {\n border-color: var(--mj-brand-primary);\n box-shadow: 0 0 0 2px rgba(37, 99, 235, 0.1);\n}\n\n.interval-unit {\n font-size: 12px;\n color: var(--mj-text-disabled);\n}\n\n/* Cron config */\n.cron-presets {\n display: flex;\n flex-wrap: wrap;\n gap: 6px;\n}\n\n.cron-input-row {\n display: flex;\n align-items: center;\n gap: 12px;\n margin-top: 4px;\n flex-wrap: wrap;\n}\n\n.cron-input {\n width: 180px;\n padding: 5px 10px;\n border: 1px solid var(--mj-border-default);\n border-radius: 6px;\n font-size: 12px;\n font-family: 'SF Mono', 'Fira Code', 'Cascadia Code', monospace;\n color: var(--mj-text-secondary);\n outline: none;\n transition: border-color 0.15s ease;\n}\n\n.cron-input:focus {\n border-color: var(--mj-brand-primary);\n box-shadow: 0 0 0 2px rgba(37, 99, 235, 0.1);\n}\n\n.cron-description {\n font-size: 12px;\n color: var(--mj-text-muted);\n font-style: italic;\n}\n\n/* Card: Right (status) */\n.card-status {\n display: flex;\n flex-direction: column;\n align-items: flex-end;\n gap: 16px;\n min-width: 180px;\n flex-shrink: 0;\n}\n\n.status-items {\n display: flex;\n flex-direction: column;\n gap: 8px;\n text-align: right;\n}\n\n.status-item {\n display: flex;\n flex-direction: column;\n gap: 2px;\n}\n\n.status-label {\n font-size: 11px;\n color: var(--mj-text-disabled);\n text-transform: uppercase;\n letter-spacing: 0.5px;\n}\n\n.status-value {\n font-size: 13px;\n font-weight: 500;\n color: var(--mj-text-secondary);\n}\n\n.status-running {\n color: var(--mj-color-warning-600);\n display: flex;\n align-items: center;\n justify-content: flex-end;\n gap: 6px;\n}\n\n.status-idle {\n color: var(--mj-color-success-600);\n}\n\n/* Card actions */\n.card-actions {\n display: flex;\n gap: 8px;\n}\n\n.btn-save {\n animation: slideIn 0.2s ease;\n}\n\n@keyframes slideIn {\n from {\n opacity: 0;\n transform: translateX(8px);\n }\n to {\n opacity: 1;\n transform: translateX(0);\n }\n}\n\n.btn-run {\n font-size: 12px;\n padding: 6px 12px;\n}\n\n/* --------------------------------------------------------------------------\n Empty State\n -------------------------------------------------------------------------- */\n\n.empty-state {\n text-align: center;\n padding: 60px 24px;\n color: var(--mj-text-disabled);\n}\n\n.empty-state i {\n font-size: 48px;\n margin-bottom: 16px;\n display: block;\n}\n\n.empty-state p {\n font-size: 15px;\n margin: 0;\n}\n\n/* --------------------------------------------------------------------------\n Responsive\n -------------------------------------------------------------------------- */\n\n@media (max-width: 900px) {\n .schedule-card {\n flex-direction: column;\n gap: 20px;\n }\n\n .card-info {\n min-width: 0;\n width: 100%;\n }\n\n .card-config {\n width: 100%;\n }\n\n .card-status {\n align-items: flex-start;\n width: 100%;\n min-width: 0;\n }\n\n .status-items {\n text-align: left;\n flex-direction: row;\n gap: 20px;\n flex-wrap: wrap;\n }\n\n .status-running {\n justify-content: flex-start;\n }\n\n .card-actions {\n width: 100%;\n }\n\n .card-actions .btn {\n flex: 1;\n justify-content: center;\n }\n}\n\n@media (max-width: 768px) {\n .timeline-section {\n display: none;\n }\n\n .schedules-root {\n padding: 16px;\n }\n\n .section-header {\n flex-direction: column;\n align-items: flex-start;\n gap: 12px;\n }\n\n .header-left {\n gap: 8px;\n }\n}\n"] }]
836
928
  }], null, null); })();
837
- (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(SchedulesComponent, { className: "SchedulesComponent", filePath: "src/Integration/components/schedules/schedules.component.ts", lineNumber: 62 }); })();
929
+ (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(SchedulesComponent, { className: "SchedulesComponent", filePath: "src/Integration/components/schedules/schedules.component.ts", lineNumber: 63 }); })();
838
930
  export function LoadSchedulesComponent() {
839
931
  // Tree-shaking prevention: importing this module causes
840
932
  // @RegisterClass decorators to fire, registering components.