@acorex-platform/extension-project-management-jira-client 2.0.0-next.2 → 2.0.0-next.21

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,257 +1,13 @@
1
- import { AXMIntegrationManagementModule } from '@acorex-platform/module-integration-management-client';
2
- import { AXPIntegrationRuntimeService, AXP_INTEGRATION_SECRET_STORE, AXP_INTEGRATION_CONNECTION_ESTABLISHED, AXP_MENU_PROVIDER, AXP_STATUS_PROVIDERS, AXP_INTEGRATION_PROVIDER, AXP_INTEGRATION_RUNTIME_PROVIDER } from '@acorex-platform/framework-client/common';
3
- import { AXP_LOGO_PROVIDER, AXP_EXPRESSION_EVALUATOR_SCOPE_PROVIDER } from '@acorex-platform/framework-client/core';
4
- import { AXPEntityService, ensureListActions, actionExists, AXP_ENTITY_DEFINITION_LOADER, AXP_ENTITY_MODIFIER } from '@acorex-platform/framework-client/layout/entity';
5
- import { provideCommandSetups } from '@acorex-platform/framework-client/runtime';
1
+ import { AXMIntegrationConnectionManager, ensureUserIntegrationConnectionsSection, AXMIntegrationManagementModule } from '@acorex-platform/module-integration-management-client';
2
+ import { AXP_SETTING_DEFINITION_PROVIDER, AXP_INTEGRATION_PROVIDER, AXP_INTEGRATION_RUNTIME_PROVIDER } from '@acorex-platform/framework-client/common';
3
+ import { AXPCommandService, provideCommandSetups } from '@acorex-platform/framework-client/runtime';
6
4
  import * as i0 from '@angular/core';
7
- import { Injectable, inject, NgModule, input, computed, ChangeDetectionStrategy, Component, signal } from '@angular/core';
8
- import { RootConfig, AXEJiraCommandKeys, AXPAdministrationPermissionKeys, AXEJiraMenuKeys, AXE_JIRA_PROJECT_SYNC_STRATEGIES } from '@acorex-platform/extension-project-management-jira-client/contracts';
9
- import { AXPStatusProvider, AXPSystemActionType, AXPWidgetsList, AXP_INTEGRATION_AUTH_METHOD_TYPE, AXP_INTEGRATION_CONNECTION_SCOPE, AXP_INTEGRATION_CAPABILITY, AXP_INTEGRATION_CONNECTION_STATUS } from '@acorex-platform/framework-shared/core';
10
- import { AXPEntityCommandScope } from '@acorex-platform/framework-shared/entity';
11
- import { RootConfig as RootConfig$1 } from '@acorex-platform/module-project-management-client/contracts';
5
+ import { Injectable, inject, NgModule, input, computed, ChangeDetectionStrategy, Component } from '@angular/core';
6
+ import { RootConfig, AXEJiraCommandKeys } from '@acorex-platform/extension-project-management-jira-client/contracts';
7
+ import { AXPWidgetsList, AXP_INTEGRATION_AUTH_METHOD_TYPE, AXP_INTEGRATION_CONNECTION_SCOPE, AXP_INTEGRATION_CAPABILITY, AXP_INTEGRATION_CONNECTION_STATUS, runtimeCommandPayloadData } from '@acorex-platform/framework-shared/core';
12
8
  import { trim, defaultTo } from 'lodash-es';
13
- import { AXPCommonMenuKeys } from '@acorex-platform/module-common-client/contracts';
14
- import * as i2 from '@acorex/components/button';
15
- import { AXButtonModule } from '@acorex/components/button';
16
- import * as i3 from '@acorex/components/check-box';
17
- import { AXCheckBoxModule } from '@acorex/components/check-box';
18
- import * as i4 from '@acorex/components/decorators';
19
- import { AXDecoratorModule } from '@acorex/components/decorators';
20
- import * as i5 from '@acorex/components/loading';
21
- import { AXLoadingModule } from '@acorex/components/loading';
22
- import { AXBasePageComponent } from '@acorex/components/page';
23
- import { AXComboBoxComponent } from '@acorex/components/combo-box';
24
- import * as i6 from '@acorex/core/translation';
25
- import { AXTranslationModule } from '@acorex/core/translation';
26
- import { AsyncPipe } from '@angular/common';
27
- import * as i1 from '@angular/forms';
28
- import { FormsModule } from '@angular/forms';
29
-
30
- //#endregion
31
- //#region ---- Entity Provider ----
32
- class AXEJiraEntityProvider {
33
- preload() {
34
- const module = RootConfig.module.name;
35
- return Object.values(RootConfig.entities).map((entity) => ({
36
- module,
37
- entity: entity.name,
38
- }));
39
- }
40
- async get(moduleName, entityName) {
41
- if (moduleName !== RootConfig.module.name) {
42
- return null;
43
- }
44
- switch (entityName) {
45
- case RootConfig.entities.jiraInstance.name:
46
- return (await import('@acorex-platform/extension-project-management-jira-domain')).jiraInstanceFactory();
47
- case RootConfig.entities.jiraProject.name:
48
- return (await import('@acorex-platform/extension-project-management-jira-domain')).jiraProjectFactory();
49
- case RootConfig.entities.jiraIssueType.name:
50
- return (await import('@acorex-platform/extension-project-management-jira-domain')).jiraIssueTypeFactory();
51
- case RootConfig.entities.jiraField.name:
52
- return (await import('@acorex-platform/extension-project-management-jira-domain')).jiraFieldFactory();
53
- case RootConfig.entities.jiraProjectIssueType.name:
54
- return (await import('@acorex-platform/extension-project-management-jira-domain')).jiraProjectIssueTypeFactory();
55
- case RootConfig.entities.jiraProjectIssueTypeField.name:
56
- return (await import('@acorex-platform/extension-project-management-jira-domain')).jiraProjectIssueTypeFieldFactory();
57
- case RootConfig.entities.jiraWorkflowStatus.name:
58
- return (await import('@acorex-platform/extension-project-management-jira-domain')).jiraWorkflowStatusFactory();
59
- case RootConfig.entities.jiraIssue.name:
60
- return (await import('@acorex-platform/extension-project-management-jira-domain')).jiraIssueFactory();
61
- case RootConfig.entities.jiraWorklog.name:
62
- return (await import('@acorex-platform/extension-project-management-jira-domain')).jiraWorklogFactory();
63
- default:
64
- return null;
65
- }
66
- }
67
- async list() {
68
- const module = RootConfig.module.name;
69
- return Object.values(RootConfig.entities).map((entity) => ({
70
- name: entity.name,
71
- module,
72
- }));
73
- }
74
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXEJiraEntityProvider, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
75
- static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXEJiraEntityProvider }); }
76
- }
77
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXEJiraEntityProvider, decorators: [{
78
- type: Injectable
79
- }] });
80
-
81
- //#region ---- Imports ----
82
- //#endregion
83
- //#region ---- Status Helper ----
84
- const status = (name, title, color, options = {}) => ({
85
- name,
86
- title,
87
- color,
88
- ...options,
89
- });
90
- //#endregion
91
- //#region ---- Jira Project Status Provider ----
92
- /**
93
- * Operational sync status for mirrored Jira projects (not Jira workflow statuses).
94
- */
95
- class AXEJiraProjectStatusProvider extends AXPStatusProvider {
96
- constructor() {
97
- super(...arguments);
98
- this.key = 'extensions-jira.status.jira-project';
99
- this.statuses = [
100
- status('Active', '@extensions-jira:projects.statuses.active', 'success', {
101
- order: 1,
102
- generateView: true,
103
- isInitial: true,
104
- icon: 'fa-light fa-circle-check',
105
- }),
106
- status('Archived', '@extensions-jira:projects.statuses.archived', 'neutral', {
107
- order: 2,
108
- generateView: true,
109
- isFinal: true,
110
- icon: 'fa-light fa-box-archive',
111
- }),
112
- status('SyncError', '@extensions-jira:projects.statuses.sync-error', 'danger', {
113
- order: 3,
114
- generateView: true,
115
- icon: 'fa-light fa-triangle-exclamation',
116
- }),
117
- ];
118
- this.transitions = [
119
- {
120
- from: 'Active',
121
- to: 'Archived',
122
- title: '@extensions-jira:projects.transitions.archive',
123
- },
124
- {
125
- from: 'Archived',
126
- to: 'Active',
127
- title: '@extensions-jira:projects.transitions.activate',
128
- },
129
- {
130
- from: 'Active',
131
- to: 'SyncError',
132
- title: '@extensions-jira:projects.transitions.mark-sync-error',
133
- },
134
- {
135
- from: 'SyncError',
136
- to: 'Active',
137
- title: '@extensions-jira:projects.transitions.resolve-sync-error',
138
- },
139
- ];
140
- this.defaultStatus = 'Active';
141
- }
142
- }
143
- //#endregion
144
-
145
- //#region ---- Imports ----
146
- //#endregion
147
- /** Expression scope namespace (no hyphens — safe for `with (scope)` eval). */
148
- const AXE_JIRA_EXPRESSION_SCOPE = 'JiraExtension';
149
- //#region ---- Provider ----
150
- class AXEJiraProjectLinkExpressionEvaluatorScopeProvider {
151
- constructor() {
152
- this.entityService = inject(AXPEntityService);
153
- this.integrationRuntime = inject(AXPIntegrationRuntimeService);
154
- }
155
- async provide(context) {
156
- context.addScope(AXE_JIRA_EXPRESSION_SCOPE, {
157
- hasProjectLink: async (platformProjectIdRaw) => {
158
- const platformProjectId = typeof platformProjectIdRaw === 'string' ? platformProjectIdRaw.trim() : '';
159
- if (!platformProjectId) {
160
- return false;
161
- }
162
- const result = await this.entityService
163
- .withEntity(RootConfig.module.name, RootConfig.entities.jiraProject.name)
164
- .data()
165
- .query({
166
- skip: 0,
167
- take: 1,
168
- filter: {
169
- field: 'platformProjectId',
170
- operator: { type: 'equal' },
171
- value: platformProjectId,
172
- },
173
- });
174
- return (result.items?.length ?? 0) > 0;
175
- },
176
- hasJiraConnection: async () => {
177
- try {
178
- await this.integrationRuntime.getConnection({ providerId: 'jira', connectionScope: 'tenant' });
179
- return true;
180
- }
181
- catch {
182
- return false;
183
- }
184
- },
185
- });
186
- }
187
- }
188
- //#endregion
189
-
190
- //#region ---- Imports ----
191
- //#endregion
192
- const IMPORT_ORDER = 35;
193
- const SYNC_ORDER = 36;
194
- function buildImportAction() {
195
- return {
196
- name: AXEJiraCommandKeys.ProjectImportFromJira,
197
- title: '@extensions-jira:actions.import-from-jira.title',
198
- command: { name: AXEJiraCommandKeys.ProjectImportFromJira },
199
- priority: 'secondary',
200
- type: AXPSystemActionType.Import,
201
- icon: 'fa-brands fa-jira',
202
- scope: AXPEntityCommandScope.TypeLevel,
203
- order: IMPORT_ORDER,
204
- };
205
- }
206
- function buildSyncAction() {
207
- return {
208
- name: AXEJiraCommandKeys.ProjectSync,
209
- title: '@extensions-jira:actions.sync.title',
210
- command: {
211
- name: AXEJiraCommandKeys.ProjectSync,
212
- options: { platformProjectId: '{{ context.eval("id") }}' },
213
- },
214
- priority: 'secondary',
215
- type: AXPSystemActionType.Sync,
216
- icon: 'fa-light fa-arrows-rotate',
217
- scope: AXPEntityCommandScope.Individual,
218
- order: SYNC_ORDER,
219
- hidden: `{{ !(await ${AXE_JIRA_EXPRESSION_SCOPE}.hasProjectLink(context.eval("id"))) }}`,
220
- };
221
- }
222
- /** Adds Jira import/sync actions to ProjectManagement.Project views. */
223
- const AXEJiraProjectEntityModifier = {
224
- entityName: RootConfig$1.entities.project.name,
225
- order: 50,
226
- modifier: (ctx) => {
227
- if (ctx.module.get() !== RootConfig$1.module.name) {
228
- return;
229
- }
230
- ensureListActions(ctx);
231
- ctx.interfaces.update((interfaces) => {
232
- const next = (interfaces ?? {});
233
- const master = (next['master'] ?? {});
234
- const list = (master['list'] ?? { actions: [], views: [] });
235
- const listActions = (list['actions'] ?? []);
236
- if (!actionExists(listActions, AXEJiraCommandKeys.ProjectImportFromJira)) {
237
- listActions.push(buildImportAction());
238
- }
239
- list['actions'] = listActions;
240
- master['list'] = list;
241
- const single = master['single'];
242
- if (single) {
243
- const singleActions = (single['actions'] ?? []);
244
- if (!actionExists(singleActions, AXEJiraCommandKeys.ProjectSync)) {
245
- singleActions.push(buildSyncAction());
246
- }
247
- single['actions'] = singleActions;
248
- }
249
- next['master'] = master;
250
- return next;
251
- });
252
- },
253
- };
254
- //#endregion
9
+ import { AXP_LOGO_PROVIDER } from '@acorex-platform/framework-client/core';
10
+ import { AXPSessionService } from '@acorex-platform/framework-client/auth';
255
11
 
256
12
  //#region ---- Jira logo registry key ----
257
13
  const axpJiraLogoKey = 'jira';
@@ -273,8 +29,8 @@ class AXEJiraIntegrationProvider {
273
29
  AXP_INTEGRATION_CAPABILITY.Sync,
274
30
  AXP_INTEGRATION_CAPABILITY.TimeLogging,
275
31
  ],
276
- connectionScopes: [AXP_INTEGRATION_CONNECTION_SCOPE.Tenant, AXP_INTEGRATION_CONNECTION_SCOPE.User],
277
- allowMultipleConnections: true,
32
+ connectionScopes: [AXP_INTEGRATION_CONNECTION_SCOPE.User],
33
+ allowMultipleConnections: false,
278
34
  defaultConnectionKey: 'default',
279
35
  connectCommandKey: AXEJiraCommandKeys.ConnectionConnect,
280
36
  authMethods: [
@@ -363,63 +119,76 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
363
119
  type: Injectable
364
120
  }] });
365
121
 
122
+ //#region ---- Imports ----
123
+ //#endregion
124
+ //#region ---- Runtime Provider ----
366
125
  class AXEJiraIntegrationRuntimeProvider {
367
126
  constructor() {
368
127
  this.providerId = 'jira';
369
- this.secretStore = inject(AXP_INTEGRATION_SECRET_STORE, { optional: true });
128
+ this.commandService = inject(AXPCommandService);
129
+ this.connectionManager = inject(AXMIntegrationConnectionManager);
370
130
  }
371
131
  async getHooks() {
372
132
  return {
373
133
  validateConnection: (ctx) => this.validateConnection(ctx),
374
134
  createClient: (ctx) => this.createClient(ctx),
135
+ getConnectionSettingsBadges: (ctx) => this.recheckStoredConnection(ctx),
375
136
  };
376
137
  }
377
- async validateConnection(ctx) {
378
- if (!this.secretStore) {
379
- return { valid: false, status: AXP_INTEGRATION_CONNECTION_STATUS.Invalid, errorMessage: 'Secret store unavailable.' };
380
- }
138
+ async recheckStoredConnection(ctx) {
139
+ const config = (ctx.connection.config ?? {});
140
+ const token = trim(defaultTo(config.token, ''));
141
+ const instanceUrl = trim(defaultTo(ctx.connection.instanceUrl, ''));
381
142
  try {
382
- const credentials = await this.secretStore.resolve(ctx.secretRef, ctx);
383
- const token = credentials.token ??
384
- credentials.apiToken ??
385
- credentials.accessToken;
386
- if (!trim(defaultTo(token, ''))) {
387
- return {
388
- valid: false,
389
- status: AXP_INTEGRATION_CONNECTION_STATUS.Invalid,
390
- errorMessage: 'No token available for Jira connection.',
391
- };
143
+ const reachable = token && instanceUrl
144
+ ? await this.probeJira(instanceUrl, token)
145
+ : false;
146
+ if (!reachable && ctx.connection.id) {
147
+ await this.connectionManager.updateConnection(ctx.connection.id, {
148
+ status: AXP_INTEGRATION_CONNECTION_STATUS.NotConnected,
149
+ isRevoked: false,
150
+ });
392
151
  }
393
- return {
394
- valid: true,
395
- status: AXP_INTEGRATION_CONNECTION_STATUS.Connected,
396
- externalAccountName: defaultTo(ctx.instanceUrl, 'Jira'),
397
- };
398
152
  }
399
153
  catch {
154
+ return [];
155
+ }
156
+ return [];
157
+ }
158
+ async probeJira(instanceUrl, token) {
159
+ const probe = await this.commandService.executeRemote(AXEJiraCommandKeys.ConnectionValidate, { instanceUrl, token });
160
+ return probe?.success === true && !!trim(defaultTo(runtimeCommandPayloadData(probe)?.displayName, ''));
161
+ }
162
+ async validateConnection(ctx) {
163
+ const token = this.readToken(ctx);
164
+ const instanceUrl = trim(defaultTo(ctx.instanceUrl, ''));
165
+ if (!token || !instanceUrl || !(await this.probeJira(instanceUrl, token))) {
400
166
  return {
401
167
  valid: false,
402
168
  status: AXP_INTEGRATION_CONNECTION_STATUS.Invalid,
403
- errorMessage: 'Failed to validate Jira credentials.',
169
+ errorMessage: 'Jira rejected the saved address or personal access token.',
404
170
  };
405
171
  }
172
+ return {
173
+ valid: true,
174
+ status: AXP_INTEGRATION_CONNECTION_STATUS.Connected,
175
+ externalAccountName: instanceUrl,
176
+ };
406
177
  }
407
178
  async createClient(ctx) {
408
- if (!this.secretStore) {
409
- throw new Error('Secret store unavailable.');
410
- }
411
- const credentials = await this.secretStore.resolve(ctx.secretRef, ctx);
412
- const token = credentials.token ??
413
- credentials.apiToken ??
414
- credentials.accessToken;
415
- if (!trim(defaultTo(token, ''))) {
179
+ const token = this.readToken(ctx);
180
+ if (!token) {
416
181
  throw new Error('Jira connection has no usable token.');
417
182
  }
418
183
  return {
419
- token: token,
184
+ token,
420
185
  baseUrl: defaultTo(ctx.instanceUrl, undefined),
421
186
  };
422
187
  }
188
+ readToken(ctx) {
189
+ const config = (ctx.config ?? {});
190
+ return trim(defaultTo(config.token, ''));
191
+ }
423
192
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXEJiraIntegrationRuntimeProvider, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
424
193
  static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXEJiraIntegrationRuntimeProvider }); }
425
194
  }
@@ -427,51 +196,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
427
196
  type: Injectable
428
197
  }] });
429
198
 
430
- //#region ---- Imports ----
431
- //#endregion
432
- //#region ---- Handler ----
433
- class AXEJiraConnectionEstablishedHandler {
434
- constructor() {
435
- this.providerId = 'jira';
436
- this.entityService = inject(AXPEntityService);
437
- }
438
- async onEstablished(connection) {
439
- if (connection.connectionScope !== AXP_INTEGRATION_CONNECTION_SCOPE.Tenant) {
440
- return;
441
- }
442
- const connectionKey = trim(connection.connectionKey);
443
- if (!connectionKey) {
444
- return;
445
- }
446
- const accessor = this.entityService
447
- .withEntity(RootConfig.module.name, RootConfig.entities.jiraInstance.name)
448
- .data();
449
- const result = await accessor.query({
450
- skip: 0,
451
- take: 1,
452
- filter: {
453
- logic: 'and',
454
- filters: [{ field: 'name', operator: { type: 'equal' }, value: connectionKey }],
455
- },
456
- });
457
- const instance = result.items?.[0];
458
- if (!instance || instance.integrationConnectionId === connection.id) {
459
- return;
460
- }
461
- await accessor.update(instance.id, { integrationConnectionId: connection.id });
462
- }
463
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXEJiraConnectionEstablishedHandler, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
464
- static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXEJiraConnectionEstablishedHandler }); }
465
- }
466
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXEJiraConnectionEstablishedHandler, decorators: [{
467
- type: Injectable
468
- }] });
469
- const provideJiraConnectionEstablishedHandler = {
470
- provide: AXP_INTEGRATION_CONNECTION_ESTABLISHED,
471
- useClass: AXEJiraConnectionEstablishedHandler,
472
- multi: true,
473
- };
474
-
475
199
  const jiraLogoProvider = {
476
200
  key: axpJiraLogoKey,
477
201
  resolve: async (request) => ({
@@ -489,91 +213,45 @@ const AXE_JIRA_LOGO_PROVIDER = {
489
213
  multi: true,
490
214
  };
491
215
 
492
- //#region ---- Imports ----
216
+ //#region ----- Imports -----
217
+ //#endregion
218
+ //#region ----- Constants -----
219
+ const JIRA_I18N = `@${RootConfig.config.i18n}:settings.jira`;
220
+ const JIRA_PROVIDER_ID = 'jira';
493
221
  //#endregion
494
- //#region ---- Menu Provider ----
495
- class AXEJiraMenuProvider {
222
+ //#region ----- Settings Provider -----
223
+ class AXEJiraSettingsProvider {
496
224
  constructor() {
497
- this.entityService = inject(AXPEntityService);
225
+ this.sessionService = inject(AXPSessionService);
498
226
  }
499
227
  async provide(context) {
500
- const module = RootConfig.module;
501
- const policy = {
502
- features: [module.name],
503
- permissions: [AXPAdministrationPermissionKeys.MaintainConnections],
504
- };
505
- const configMenu = context.find(AXPCommonMenuKeys.Configuration);
506
- if (!context.find(AXEJiraMenuKeys.JiraExtensionConfig).exists) {
507
- configMenu.addItems([
508
- {
509
- name: AXEJiraMenuKeys.JiraExtensionConfig,
510
- text: '@extensions-jira:module.menus.configuration.title',
511
- icon: module.icon,
512
- type: 'menu',
513
- priority: 8935,
514
- policy,
515
- children: [],
516
- },
517
- ]);
228
+ if (!this.sessionService.isModuleEnabled(RootConfig.module.name)) {
229
+ return;
518
230
  }
519
- const jiraConfig = context.find(AXEJiraMenuKeys.JiraExtensionConfig);
520
- const catalogPolicy = {
521
- features: [module.name],
522
- permissions: [AXPAdministrationPermissionKeys.ViewIntegrations],
523
- };
524
- jiraConfig.addItems([
525
- {
526
- name: AXEJiraMenuKeys.Instances,
527
- text: RootConfig.entities.jiraInstance.titlePlural,
528
- path: this.entityService.createPath(module.name, RootConfig.entities.jiraInstance.name),
529
- icon: RootConfig.entities.jiraInstance.icon,
530
- priority: 1,
531
- policy,
532
- },
533
- {
534
- name: AXEJiraMenuKeys.IssueTypes,
535
- text: RootConfig.entities.jiraIssueType.titlePlural,
536
- path: this.entityService.createPath(module.name, RootConfig.entities.jiraIssueType.name),
537
- icon: RootConfig.entities.jiraIssueType.icon,
538
- priority: 3,
539
- policy: catalogPolicy,
540
- },
541
- {
542
- name: AXEJiraMenuKeys.Fields,
543
- text: RootConfig.entities.jiraField.titlePlural,
544
- path: this.entityService.createPath(module.name, RootConfig.entities.jiraField.name),
545
- icon: RootConfig.entities.jiraField.icon,
546
- priority: 4,
547
- policy: catalogPolicy,
548
- },
549
- {
550
- name: AXEJiraMenuKeys.Issues,
551
- text: RootConfig.entities.jiraIssue.titlePlural,
552
- path: this.entityService.createPath(module.name, RootConfig.entities.jiraIssue.name),
553
- icon: RootConfig.entities.jiraIssue.icon,
554
- priority: 5,
555
- policy: {
556
- features: [module.name],
557
- permissions: [AXPAdministrationPermissionKeys.ViewIntegrations],
558
- },
559
- },
560
- {
561
- name: AXEJiraMenuKeys.Worklogs,
562
- text: RootConfig.entities.jiraWorklog.titlePlural,
563
- path: this.entityService.createPath(module.name, RootConfig.entities.jiraWorklog.name),
564
- icon: RootConfig.entities.jiraWorklog.icon,
565
- priority: 6,
566
- policy: {
567
- features: [module.name],
568
- permissions: [AXPAdministrationPermissionKeys.ViewIntegrations],
231
+ const connections = ensureUserIntegrationConnectionsSection(context);
232
+ if (!connections) {
233
+ return;
234
+ }
235
+ connections.addSetting({
236
+ key: 'Jira:Setting:ConnectionPanel',
237
+ title: `${JIRA_I18N}.section-title`,
238
+ scope: 'U',
239
+ overridableAt: [],
240
+ isInherited: false,
241
+ widget: {
242
+ type: AXPWidgetsList.Advanced.IntegrationConnectionsPanel,
243
+ layout: { stacked: true },
244
+ options: {
245
+ providerId: JIRA_PROVIDER_ID,
246
+ connectionScope: 'user',
569
247
  },
570
248
  },
571
- ]);
249
+ });
572
250
  }
573
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXEJiraMenuProvider, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
574
- static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXEJiraMenuProvider }); }
251
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXEJiraSettingsProvider, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
252
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXEJiraSettingsProvider }); }
575
253
  }
576
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXEJiraMenuProvider, decorators: [{
254
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXEJiraSettingsProvider, decorators: [{
577
255
  type: Injectable
578
256
  }] });
579
257
 
@@ -586,28 +264,8 @@ class AXEJiraExtensionModule {
586
264
  static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXEJiraExtensionModule, providers: [
587
265
  AXE_JIRA_LOGO_PROVIDER,
588
266
  {
589
- provide: AXP_MENU_PROVIDER,
590
- useClass: AXEJiraMenuProvider,
591
- multi: true,
592
- },
593
- {
594
- provide: AXP_ENTITY_DEFINITION_LOADER,
595
- useClass: AXEJiraEntityProvider,
596
- multi: true,
597
- },
598
- {
599
- provide: AXP_ENTITY_MODIFIER,
600
- useValue: AXEJiraProjectEntityModifier,
601
- multi: true,
602
- },
603
- {
604
- provide: AXP_EXPRESSION_EVALUATOR_SCOPE_PROVIDER,
605
- useClass: AXEJiraProjectLinkExpressionEvaluatorScopeProvider,
606
- multi: true,
607
- },
608
- {
609
- provide: AXP_STATUS_PROVIDERS,
610
- useClass: AXEJiraProjectStatusProvider,
267
+ provide: AXP_SETTING_DEFINITION_PROVIDER,
268
+ useClass: AXEJiraSettingsProvider,
611
269
  multi: true,
612
270
  },
613
271
  {
@@ -620,11 +278,10 @@ class AXEJiraExtensionModule {
620
278
  useClass: AXEJiraIntegrationRuntimeProvider,
621
279
  multi: true,
622
280
  },
623
- provideJiraConnectionEstablishedHandler,
624
281
  provideCommandSetups([
625
282
  {
626
283
  key: AXEJiraCommandKeys.ConnectionConnect,
627
- command: () => import('./acorex-platform-extension-project-management-jira-client-connect-jira.command-BhnLiDk0.mjs').then((c) => c.ConnectJiraCommand),
284
+ command: () => import('./acorex-platform-extension-project-management-jira-client-connect-jira.command-w724jEDQ.mjs').then((c) => c.ConnectJiraCommand),
628
285
  },
629
286
  ]),
630
287
  ], imports: [AXMIntegrationManagementModule] }); }
@@ -636,28 +293,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
636
293
  providers: [
637
294
  AXE_JIRA_LOGO_PROVIDER,
638
295
  {
639
- provide: AXP_MENU_PROVIDER,
640
- useClass: AXEJiraMenuProvider,
641
- multi: true,
642
- },
643
- {
644
- provide: AXP_ENTITY_DEFINITION_LOADER,
645
- useClass: AXEJiraEntityProvider,
646
- multi: true,
647
- },
648
- {
649
- provide: AXP_ENTITY_MODIFIER,
650
- useValue: AXEJiraProjectEntityModifier,
651
- multi: true,
652
- },
653
- {
654
- provide: AXP_EXPRESSION_EVALUATOR_SCOPE_PROVIDER,
655
- useClass: AXEJiraProjectLinkExpressionEvaluatorScopeProvider,
656
- multi: true,
657
- },
658
- {
659
- provide: AXP_STATUS_PROVIDERS,
660
- useClass: AXEJiraProjectStatusProvider,
296
+ provide: AXP_SETTING_DEFINITION_PROVIDER,
297
+ useClass: AXEJiraSettingsProvider,
661
298
  multi: true,
662
299
  },
663
300
  {
@@ -670,11 +307,10 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
670
307
  useClass: AXEJiraIntegrationRuntimeProvider,
671
308
  multi: true,
672
309
  },
673
- provideJiraConnectionEstablishedHandler,
674
310
  provideCommandSetups([
675
311
  {
676
312
  key: AXEJiraCommandKeys.ConnectionConnect,
677
- command: () => import('./acorex-platform-extension-project-management-jira-client-connect-jira.command-BhnLiDk0.mjs').then((c) => c.ConnectJiraCommand),
313
+ command: () => import('./acorex-platform-extension-project-management-jira-client-connect-jira.command-w724jEDQ.mjs').then((c) => c.ConnectJiraCommand),
678
314
  },
679
315
  ]),
680
316
  ],
@@ -753,247 +389,9 @@ var jiraLogo_component = /*#__PURE__*/Object.freeze({
753
389
  AXEJiraLogoComponent: AXEJiraLogoComponent
754
390
  });
755
391
 
756
- //#endregion
757
- //#region ---- Component ----
758
- class AXEJiraProjectImportPopupComponent extends AXBasePageComponent {
759
- constructor() {
760
- super(...arguments);
761
- this.loading = signal(false, /* @ts-ignore */
762
- ...(ngDevMode ? [{ debugName: "loading" }] : /* istanbul ignore next */ []));
763
- this.defaultProjectTypeId = signal('', /* @ts-ignore */
764
- ...(ngDevMode ? [{ debugName: "defaultProjectTypeId" }] : /* istanbul ignore next */ []));
765
- this.rows = signal([], /* @ts-ignore */
766
- ...(ngDevMode ? [{ debugName: "rows" }] : /* istanbul ignore next */ []));
767
- this.projectTypes = [];
768
- this.remoteProjects = [];
769
- this.alreadyLinkedJiraIds = new Set();
770
- this.strategyOptions = AXE_JIRA_PROJECT_SYNC_STRATEGIES.map((id) => ({
771
- id,
772
- text: `@extensions-jira:import.sync-strategy.${id}`,
773
- value: id,
774
- }));
775
- }
776
- get projectTypeComboItems() {
777
- return this.projectTypes.map((type) => ({
778
- id: type.id,
779
- text: type.title,
780
- value: type.id,
781
- }));
782
- }
783
- ngOnInit() {
784
- super.ngOnInit();
785
- this.defaultProjectTypeId.set(this.projectTypes[0]?.id ?? '');
786
- this.rows.set(this.remoteProjects.map((remote) => ({
787
- remote,
788
- selected: !this.alreadyLinkedJiraIds.has(remote.id),
789
- syncStrategy: 'merge',
790
- alreadyLinked: this.alreadyLinkedJiraIds.has(remote.id),
791
- })));
792
- }
793
- handleProjectTypeChange(event) {
794
- const value = event.value;
795
- this.defaultProjectTypeId.set(typeof value === 'string' ? value : '');
796
- }
797
- toggleRow(remoteId, event) {
798
- const selected = !!event.value;
799
- this.rows.update((items) => items.map((row) => (row.remote.id === remoteId ? { ...row, selected } : row)));
800
- }
801
- changeStrategy(remoteId, event) {
802
- const raw = event.value;
803
- const syncStrategy = typeof raw === 'string' && AXE_JIRA_PROJECT_SYNC_STRATEGIES.includes(raw)
804
- ? raw
805
- : 'merge';
806
- this.rows.update((items) => items.map((row) => (row.remote.id === remoteId ? { ...row, syncStrategy } : row)));
807
- }
808
- hasSelection() {
809
- return this.rows().some((row) => row.selected && !row.alreadyLinked);
810
- }
811
- confirm() {
812
- const result = {
813
- defaultProjectTypeId: this.defaultProjectTypeId(),
814
- rows: this.rows()
815
- .filter((row) => row.selected && !row.alreadyLinked)
816
- .map((row) => ({
817
- remote: row.remote,
818
- selected: true,
819
- syncStrategy: row.syncStrategy,
820
- })),
821
- };
822
- this.close(result);
823
- }
824
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXEJiraProjectImportPopupComponent, deps: null, target: i0.ɵɵFactoryTarget.Component }); }
825
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.8", type: AXEJiraProjectImportPopupComponent, isStandalone: true, selector: "axe-jira-project-import-popup", usesInheritance: true, ngImport: i0, template: `
826
- <div class="flex flex-col gap-4 p-4 max-h-[70vh] overflow-auto">
827
- <div class="flex flex-col gap-1">
828
- <span class="text-sm font-medium">
829
- {{ '@extensions-jira:import.popup.default-project-type' | translate | async }}
830
- </span>
831
- <ax-combo-box
832
- [items]="projectTypeComboItems"
833
- [searchable]="false"
834
- [value]="defaultProjectTypeId()"
835
- (onValueChanged)="handleProjectTypeChange($event)"
836
- ></ax-combo-box>
837
- </div>
838
-
839
- @if (loading()) {
840
- <ax-loading></ax-loading>
841
- } @else if (rows().length === 0) {
842
- <p class="text-sm opacity-70">
843
- {{ '@extensions-jira:import.popup.empty' | translate | async }}
844
- </p>
845
- } @else {
846
- <table class="w-full text-sm">
847
- <thead>
848
- <tr class="border-b">
849
- <th class="p-2 text-start"></th>
850
- <th class="p-2 text-start">{{ '@extensions-jira:import.popup.columns.key' | translate | async }}</th>
851
- <th class="p-2 text-start">{{ '@extensions-jira:import.popup.columns.name' | translate | async }}</th>
852
- <th class="p-2 text-start">{{ '@extensions-jira:import.popup.columns.strategy' | translate | async }}</th>
853
- </tr>
854
- </thead>
855
- <tbody>
856
- @for (row of rows(); track row.remote.id) {
857
- <tr class="border-b border-opacity-20">
858
- <td class="p-2">
859
- <ax-check-box
860
- [ngModel]="row.selected"
861
- [disabled]="row.alreadyLinked"
862
- (onValueChanged)="toggleRow(row.remote.id, $event)"
863
- ></ax-check-box>
864
- </td>
865
- <td class="p-2">{{ row.remote.key }}</td>
866
- <td class="p-2">{{ row.remote.name }}</td>
867
- <td class="p-2">
868
- <ax-combo-box
869
- class="min-w-40"
870
- [items]="strategyOptions"
871
- [searchable]="false"
872
- [value]="row.syncStrategy"
873
- [disabled]="!row.selected"
874
- (onValueChanged)="changeStrategy(row.remote.id, $event)"
875
- ></ax-combo-box>
876
- </td>
877
- </tr>
878
- }
879
- </tbody>
880
- </table>
881
- }
882
- </div>
883
- <ax-footer>
884
- <ax-suffix>
885
- <ax-button
886
- look="outline"
887
- [text]="'@general:actions.cancel.title' | translate | async"
888
- (onClick)="close()"
889
- ></ax-button>
890
- <ax-button
891
- look="solid"
892
- color="primary"
893
- [disabled]="!hasSelection()"
894
- [text]="'@extensions-jira:import.popup.confirm' | translate | async"
895
- (onClick)="confirm()"
896
- ></ax-button>
897
- </ax-suffix>
898
- </ax-footer>
899
- `, isInline: true, dependencies: [{ kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: AXButtonModule }, { kind: "component", type: i2.AXButtonComponent, selector: "ax-button", inputs: ["disabled", "tabIndex", "color", "look", "text", "toggleable", "selected", "iconOnly", "type", "loadingText"], outputs: ["onBlur", "onFocus", "onClick", "selectedChange", "toggleableChange", "lookChange", "colorChange", "disabledChange", "loadingTextChange"] }, { kind: "ngmodule", type: AXCheckBoxModule }, { kind: "component", type: i3.AXCheckBoxComponent, selector: "ax-check-box", inputs: ["disabled", "tabIndex", "readonly", "color", "value", "name", "id", "isLoading", "indeterminate"], outputs: ["onBlur", "onFocus", "valueChange", "onValueChanged"] }, { kind: "ngmodule", type: AXDecoratorModule }, { kind: "component", type: i4.AXDecoratorGenericComponent, selector: "ax-footer, ax-header, ax-content, ax-divider, ax-form-hint, ax-prefix, ax-suffix, ax-text, ax-title, ax-subtitle, ax-placeholder, ax-overlay" }, { kind: "ngmodule", type: AXLoadingModule }, { kind: "component", type: i5.AXLoadingComponent, selector: "ax-loading", inputs: ["visible", "type", "context"], outputs: ["visibleChange"] }, { kind: "component", type: AXComboBoxComponent, selector: "ax-combo-box", inputs: ["name", "items", "searchable", "multiple", "adaptivityEnabled", "caption", "alternate", "placeholder", "searchPlaceholder", "highlightSearch", "emptyTemplate", "itemTemplate", "selectedTemplate", "disabled", "readonly", "look", "value"], outputs: ["lookChange", "valueChange", "onValueChanged", "onFocus", "onBlur", "onItemClick", "onItemSelected", "onOpened", "onClosed"] }, { kind: "ngmodule", type: AXTranslationModule }, { kind: "pipe", type: AsyncPipe, name: "async" }, { kind: "pipe", type: i6.AXTranslatorPipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
900
- }
901
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXEJiraProjectImportPopupComponent, decorators: [{
902
- type: Component,
903
- args: [{
904
- selector: 'axe-jira-project-import-popup',
905
- changeDetection: ChangeDetectionStrategy.OnPush,
906
- imports: [
907
- AsyncPipe,
908
- FormsModule,
909
- AXButtonModule,
910
- AXCheckBoxModule,
911
- AXDecoratorModule,
912
- AXLoadingModule,
913
- AXComboBoxComponent,
914
- AXTranslationModule,
915
- ],
916
- template: `
917
- <div class="flex flex-col gap-4 p-4 max-h-[70vh] overflow-auto">
918
- <div class="flex flex-col gap-1">
919
- <span class="text-sm font-medium">
920
- {{ '@extensions-jira:import.popup.default-project-type' | translate | async }}
921
- </span>
922
- <ax-combo-box
923
- [items]="projectTypeComboItems"
924
- [searchable]="false"
925
- [value]="defaultProjectTypeId()"
926
- (onValueChanged)="handleProjectTypeChange($event)"
927
- ></ax-combo-box>
928
- </div>
929
-
930
- @if (loading()) {
931
- <ax-loading></ax-loading>
932
- } @else if (rows().length === 0) {
933
- <p class="text-sm opacity-70">
934
- {{ '@extensions-jira:import.popup.empty' | translate | async }}
935
- </p>
936
- } @else {
937
- <table class="w-full text-sm">
938
- <thead>
939
- <tr class="border-b">
940
- <th class="p-2 text-start"></th>
941
- <th class="p-2 text-start">{{ '@extensions-jira:import.popup.columns.key' | translate | async }}</th>
942
- <th class="p-2 text-start">{{ '@extensions-jira:import.popup.columns.name' | translate | async }}</th>
943
- <th class="p-2 text-start">{{ '@extensions-jira:import.popup.columns.strategy' | translate | async }}</th>
944
- </tr>
945
- </thead>
946
- <tbody>
947
- @for (row of rows(); track row.remote.id) {
948
- <tr class="border-b border-opacity-20">
949
- <td class="p-2">
950
- <ax-check-box
951
- [ngModel]="row.selected"
952
- [disabled]="row.alreadyLinked"
953
- (onValueChanged)="toggleRow(row.remote.id, $event)"
954
- ></ax-check-box>
955
- </td>
956
- <td class="p-2">{{ row.remote.key }}</td>
957
- <td class="p-2">{{ row.remote.name }}</td>
958
- <td class="p-2">
959
- <ax-combo-box
960
- class="min-w-40"
961
- [items]="strategyOptions"
962
- [searchable]="false"
963
- [value]="row.syncStrategy"
964
- [disabled]="!row.selected"
965
- (onValueChanged)="changeStrategy(row.remote.id, $event)"
966
- ></ax-combo-box>
967
- </td>
968
- </tr>
969
- }
970
- </tbody>
971
- </table>
972
- }
973
- </div>
974
- <ax-footer>
975
- <ax-suffix>
976
- <ax-button
977
- look="outline"
978
- [text]="'@general:actions.cancel.title' | translate | async"
979
- (onClick)="close()"
980
- ></ax-button>
981
- <ax-button
982
- look="solid"
983
- color="primary"
984
- [disabled]="!hasSelection()"
985
- [text]="'@extensions-jira:import.popup.confirm' | translate | async"
986
- (onClick)="confirm()"
987
- ></ax-button>
988
- </ax-suffix>
989
- </ax-footer>
990
- `,
991
- }]
992
- }] });
993
-
994
392
  /**
995
393
  * Generated bundle index. Do not edit.
996
394
  */
997
395
 
998
- export { AXEJiraExtensionModule, AXEJiraLogoComponent, AXEJiraProjectImportPopupComponent, AXE_JIRA_LOGO_PROVIDER, AXE_JIRA_MARK_BLUE, AXE_JIRA_MARK_LEFT_PATH, AXE_JIRA_MARK_RIGHT_PATH, axpJiraLogoKey };
396
+ export { AXEJiraExtensionModule, AXEJiraLogoComponent, AXE_JIRA_LOGO_PROVIDER, AXE_JIRA_MARK_BLUE, AXE_JIRA_MARK_LEFT_PATH, AXE_JIRA_MARK_RIGHT_PATH, axpJiraLogoKey };
999
397
  //# sourceMappingURL=acorex-platform-extension-project-management-jira-client.mjs.map