From 4cfdf4dde62d4b262dc2ca23e66dc22268a6a3d5 Mon Sep 17 00:00:00 2001 From: RAHUL RATHORE <41804588+rahul-rocket@users.noreply.github.com> Date: Sat, 4 Nov 2023 18:19:26 +0530 Subject: [PATCH 01/15] fix: #7000 rename Github to GitHub --- apps/gauzy/src/assets/i18n/en.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/apps/gauzy/src/assets/i18n/en.json b/apps/gauzy/src/assets/i18n/en.json index 5d9665768e4..3549871a640 100644 --- a/apps/gauzy/src/assets/i18n/en.json +++ b/apps/gauzy/src/assets/i18n/en.json @@ -304,7 +304,7 @@ "SCREEN_CAPTURE": "Screen Capture", "NUMBER": "Number", "PROVIDER": "Provider", - "GITHUB_REPOSITORY": "Github Repository", + "GITHUB_REPOSITORY": "GitHub Repository", "ISSUES_SYNC": "Issues in Sync", "ISSUES_SYNC_COUNT": "{{ count }} Issues Auto Sync", "ENABLED_DISABLED_SYNC": "Enabled / Disabled Sync", @@ -427,7 +427,7 @@ "FACEBOOK": "Facebook", "INSTAGRAM": "Instagram", "TWITTER": "Twitter", - "GITHUB": "Github", + "GITHUB": "GitHub", "GITLAB": "Gitlab", "UPWORK": "Upwork", "STACK_OVERFLOW": "Stackoverflow", @@ -475,7 +475,7 @@ "AUTO_SYNC_TASKS": "Auto-sync tasks", "AUTO_SYNC_TASKS_BASED_ON_LABEL": "Is tasks Auto-sync based on Label?", "AUTO_SYNC_TAG": "Label", - "GITHUB_REPOSITORY": "Github Repository", + "GITHUB_REPOSITORY": "GitHub Repository", "PROJECT": "Project" }, "PLACEHOLDERS": { @@ -581,7 +581,7 @@ "FACEBOOK": "Facebook", "INSTAGRAM": "Instagram", "TWITTER": "Twitter", - "GITHUB": "Github", + "GITHUB": "GitHub", "GITLAB": "Gitlab", "UPWORK": "Upwork", "STACK_OVERFLOW": "Stackoverflow", From 4cd40523d8c97d3558a8dd1f1601284122b166f0 Mon Sep 17 00:00:00 2001 From: RAHUL RATHORE <41804588+rahul-rocket@users.noreply.github.com> Date: Sat, 4 Nov 2023 18:37:21 +0530 Subject: [PATCH 02/15] fix: #7000 github auto sync tab table label text --- .../github/components/view/view.component.html | 17 ++++++++++------- .../github/components/view/view.component.ts | 3 +++ apps/gauzy/src/assets/i18n/en.json | 1 + 3 files changed, 14 insertions(+), 7 deletions(-) diff --git a/apps/gauzy/src/app/pages/integrations/github/components/view/view.component.html b/apps/gauzy/src/app/pages/integrations/github/components/view/view.component.html index a68776c4b4f..62a698271c7 100644 --- a/apps/gauzy/src/app/pages/integrations/github/components/view/view.component.html +++ b/apps/gauzy/src/app/pages/integrations/github/components/view/view.component.html @@ -95,14 +95,17 @@
-
-
- +
+
+
+ {{'INTEGRATIONS.GITHUB_PAGE.AUTO_SYNC_TABLE_LABEL' | translate}} +
+
diff --git a/apps/gauzy/src/app/pages/integrations/github/components/view/view.component.ts b/apps/gauzy/src/app/pages/integrations/github/components/view/view.component.ts index 9af066fbf01..4920ec85d90 100644 --- a/apps/gauzy/src/app/pages/integrations/github/components/view/view.component.ts +++ b/apps/gauzy/src/app/pages/integrations/github/components/view/view.component.ts @@ -345,7 +345,10 @@ export class GithubViewComponent extends PaginationFilterBaseComponent implement // Define settings for the Smart Table this.settingsSmartTableProjects = { selectedRowIndex: -1, // Initialize the selected row index + hideSubHeader: true, actions: false, + mode: 'external', + editable: true, pager: { display: false, perPage: pagination ? pagination.itemsPerPage : 10 diff --git a/apps/gauzy/src/assets/i18n/en.json b/apps/gauzy/src/assets/i18n/en.json index 3549871a640..b6f8dc10a89 100644 --- a/apps/gauzy/src/assets/i18n/en.json +++ b/apps/gauzy/src/assets/i18n/en.json @@ -1245,6 +1245,7 @@ }, "GITHUB_PAGE": { "TITLE": "GitHub Integration", + "AUTO_SYNC_TABLE_LABEL": "Syncing GitHub Repositories and Projects", "SELECT_REPOSITORY": "Select repository", "SEARCH_REPOSITORY": "Type to search repository", "SYNCED_ISSUES": "'{{repository}}' issues & labels synced successfully", From 5c4baaf7f79ecaed891d1c0a7574a40814c66840 Mon Sep 17 00:00:00 2001 From: RAHUL RATHORE <41804588+rahul-rocket@users.noreply.github.com> Date: Sun, 5 Nov 2023 13:50:57 +0530 Subject: [PATCH 03/15] fix: #7000 organization projects & repository synced query --- .../organization-project-setting.update.handler.ts | 14 ++++++++++---- .../organization-project.controller.ts | 2 +- .../organization-project.service.ts | 6 ------ 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/packages/core/src/organization-project/commands/handlers/organization-project-setting.update.handler.ts b/packages/core/src/organization-project/commands/handlers/organization-project-setting.update.handler.ts index aa709e67a43..64f9ec220d3 100644 --- a/packages/core/src/organization-project/commands/handlers/organization-project-setting.update.handler.ts +++ b/packages/core/src/organization-project/commands/handlers/organization-project-setting.update.handler.ts @@ -1,6 +1,5 @@ import { HttpException, HttpStatus, Logger } from '@nestjs/common'; import { CommandHandler, ICommandHandler } from '@nestjs/cqrs'; -import { UpdateResult } from 'typeorm'; import { IOrganizationProjectSetting } from '@gauzy/contracts'; import { OrganizationProjectService } from '../../organization-project.service'; import { OrganizationProjectSettingUpdateCommand } from '../organization-project-setting.update.command'; @@ -17,18 +16,25 @@ export class OrganizationProjectSettingUpdateHandler implements ICommandHandler< * Execute an organization project setting update command. * * @param command - An `OrganizationProjectSettingUpdateCommand` object containing the update details. - * @returns A promise that resolves to an `UpdateResult` object representing the result of the update operation. + * @returns A promise that resolves to an `IOrganizationProjectSetting` or an `UpdateResult` object representing the result of the update operation. */ public async execute( command: OrganizationProjectSettingUpdateCommand - ): Promise { + ): Promise { try { + // Extract the 'id' and 'input' properties from the command object. const { id, input } = command; - return await this._organizationProjectService.update(id, input); + + // Update the organization project setting using the provided 'id' and 'input'. + await this._organizationProjectService.update(id, input); + + // Retrieve and return the updated organization project setting. + return await this._organizationProjectService.findOneByIdString(id); } catch (error) { // Handle errors and return an appropriate error response this.logger.error('Failed to update project integration settings', error.message); throw new HttpException(`Failed to update project integration settings: ${error.message}`, HttpStatus.BAD_REQUEST); } } + } diff --git a/packages/core/src/organization-project/organization-project.controller.ts b/packages/core/src/organization-project/organization-project.controller.ts index 996d866399f..26d067a55ff 100644 --- a/packages/core/src/organization-project/organization-project.controller.ts +++ b/packages/core/src/organization-project/organization-project.controller.ts @@ -183,7 +183,7 @@ export class OrganizationProjectController extends CrudController diff --git a/packages/core/src/organization-project/organization-project.service.ts b/packages/core/src/organization-project/organization-project.service.ts index 3ac146b9b97..8bd65570454 100644 --- a/packages/core/src/organization-project/organization-project.service.ts +++ b/packages/core/src/organization-project/organization-project.service.ts @@ -182,9 +182,6 @@ export class OrganizationProjectService extends TenantAwareCrudService Date: Sun, 5 Nov 2023 15:23:51 +0530 Subject: [PATCH 04/15] fix: #7000 repository resync issues button component --- .../resync-button.component.html | 14 +++ .../resync-button/resync-button.component.ts | 91 +++++++++++++++++++ .../src/app/@shared/table-components/index.ts | 1 + .../table-components.module.ts | 11 ++- 4 files changed, 114 insertions(+), 3 deletions(-) create mode 100644 apps/gauzy/src/app/@shared/table-components/github/resync-button/resync-button.component.html create mode 100644 apps/gauzy/src/app/@shared/table-components/github/resync-button/resync-button.component.ts diff --git a/apps/gauzy/src/app/@shared/table-components/github/resync-button/resync-button.component.html b/apps/gauzy/src/app/@shared/table-components/github/resync-button/resync-button.component.html new file mode 100644 index 00000000000..93174669655 --- /dev/null +++ b/apps/gauzy/src/app/@shared/table-components/github/resync-button/resync-button.component.html @@ -0,0 +1,14 @@ + diff --git a/apps/gauzy/src/app/@shared/table-components/github/resync-button/resync-button.component.ts b/apps/gauzy/src/app/@shared/table-components/github/resync-button/resync-button.component.ts new file mode 100644 index 00000000000..fc0e459efd3 --- /dev/null +++ b/apps/gauzy/src/app/@shared/table-components/github/resync-button/resync-button.component.ts @@ -0,0 +1,91 @@ +import { Component, EventEmitter, Input, Output } from '@angular/core'; +import { ViewCell } from 'ng2-smart-table'; + +@Component({ + selector: 'ngx-resync-button', + templateUrl: './resync-button.component.html', + styleUrls: [] +}) +export class ResyncButtonComponent implements ViewCell { + + /** + * Getter and Setter for managing a dynamic value. + */ + _value: any; + /** + * Getter for retrieving the current value. + * + * @returns The current value of the dynamic element. + */ + get value(): any { + return this._value; + } + /** + * Setter for updating the dynamic value. + * This setter is decorated with @Input to allow external components to bind and update the value. + * + * @param value - The new value to set for the dynamic element. + */ + @Input() set value(value: any) { + // Stores the value in the local variable for future reference. + this._value = value; + } + + /** + * An @Input property used to pass data from a parent component to this component. + * + */ + @Input() rowData: any; + + /** + * Associated getter/setter for managing a boolean value indicating synchronization status. + */ + private _syncing: boolean = false; + + /** + * Getter for retrieving the current synchronization status. + * + * @returns The current synchronization status (a boolean value). + */ + get syncing(): boolean { + return this._syncing; + } + + /** + * Setter for updating the synchronization status. + * This setter is decorated with @Input, allowing external components to set and update the synchronization status. + * + * @param value - The new synchronization status (a boolean value). + */ + @Input() set syncing(value: boolean) { + // Update the synchronization status with the provided value. + this._syncing = value; + } + + /** + * An output property for emitting click events. + * + * This output property emits events of type Event when a click event occurs. + */ + @Output() clicked: EventEmitter = new EventEmitter(); + + constructor() { } + + /** + * Handle a click event, conditionally emitting it for further processing. + * + * @param event - The click event to be handled. + */ + onClicked(event: Event) { + // Access the repository data from the component's rowData. + const repository = this.rowData.repository; + + // Check if the repository data exists and has synchronization enabled. + if (!repository || !repository.hasSyncEnabled) { + return; // If repository is missing or synchronization is not enabled, exit the function. + } + + // Emit the event using an EventEmitter, possibly to notify other parts of the application. + this.clicked.emit(event); + } +} diff --git a/apps/gauzy/src/app/@shared/table-components/index.ts b/apps/gauzy/src/app/@shared/table-components/index.ts index ce96fe6ac52..0a2343b1333 100644 --- a/apps/gauzy/src/app/@shared/table-components/index.ts +++ b/apps/gauzy/src/app/@shared/table-components/index.ts @@ -34,3 +34,4 @@ export * from './trust-html/trust-html.component'; export * from './toggle-switch/toggle-switch.component'; export * from './github/repository/repository.component'; export * from './github/issue-title-description/issue-title-description.component'; +export * from './github/resync-button/resync-button.component'; diff --git a/apps/gauzy/src/app/@shared/table-components/table-components.module.ts b/apps/gauzy/src/app/@shared/table-components/table-components.module.ts index c2bb021dedd..151abbc4c0f 100644 --- a/apps/gauzy/src/app/@shared/table-components/table-components.module.ts +++ b/apps/gauzy/src/app/@shared/table-components/table-components.module.ts @@ -5,7 +5,8 @@ import { NbIconModule, NbTooltipModule, NbBadgeModule, - NbToggleModule + NbToggleModule, + NbButtonModule } from '@nebular/theme'; import { DateViewComponent } from './date-view/date-view.component'; import { IncomeExpenseAmountComponent } from './income-amount/income-amount.component'; @@ -47,6 +48,7 @@ import { TrustHtmlLinkComponent } from './trust-html/trust-html.component'; import { ToggleSwitchComponent } from './toggle-switch/toggle-switch.component'; import { GithubRepositoryComponent } from './github/repository/repository.component'; import { GithubIssueTitleDescriptionComponent } from './github/issue-title-description/issue-title-description.component'; +import { ResyncButtonComponent } from './github/resync-button/resync-button.component'; import { StatusBadgeModule } from '../status-badge'; @NgModule({ @@ -54,6 +56,7 @@ import { StatusBadgeModule } from '../status-badge'; CommonModule, FormsModule, NbBadgeModule, + NbButtonModule, NbIconModule, NbToggleModule, NbTooltipModule, @@ -100,7 +103,8 @@ import { StatusBadgeModule } from '../status-badge'; TrustHtmlLinkComponent, ToggleSwitchComponent, GithubRepositoryComponent, - GithubIssueTitleDescriptionComponent + GithubIssueTitleDescriptionComponent, + ResyncButtonComponent ], exports: [ NotesWithTagsComponent, @@ -125,7 +129,8 @@ import { StatusBadgeModule } from '../status-badge'; TrustHtmlLinkComponent, GithubRepositoryComponent, ToggleSwitchComponent, - GithubIssueTitleDescriptionComponent + GithubIssueTitleDescriptionComponent, + ResyncButtonComponent ], providers: [] }) From 0b213ff36b081f0f098746f95864ebac4442a862 Mon Sep 17 00:00:00 2001 From: RAHUL RATHORE <41804588+rahul-rocket@users.noreply.github.com> Date: Sun, 5 Nov 2023 15:24:56 +0530 Subject: [PATCH 05/15] fix: #7000 sync auto/manual issues using organization repository --- .../@core/services/github/github.service.ts | 8 +- .../components/view/view.component.html | 270 +++++++++--------- .../github/components/view/view.component.ts | 81 ++++-- apps/gauzy/src/assets/i18n/en.json | 4 +- .../common-angular/src/utils/shared-utils.ts | 4 +- packages/common/src/utils/shared-utils.ts | 8 + packages/contracts/src/github.model.ts | 11 +- .../dto/process-github-issue-sync.dto.ts | 4 +- .../github/github-sync.controller.ts | 12 +- .../integration/github/github-sync.service.ts | 65 ++--- ...on-sync-github-repository-issue.command.ts | 4 +- .../organization-project.service.ts | 3 +- 12 files changed, 264 insertions(+), 210 deletions(-) diff --git a/apps/gauzy/src/app/@core/services/github/github.service.ts b/apps/gauzy/src/app/@core/services/github/github.service.ts index a5d1ad52d1d..ebc1b072e35 100644 --- a/apps/gauzy/src/app/@core/services/github/github.service.ts +++ b/apps/gauzy/src/app/@core/services/github/github.service.ts @@ -110,7 +110,7 @@ export class GithubService { */ public autoSyncIssues( integrationId: IIntegrationTenant['id'], - repository: IGithubRepository, + repository: IOrganizationGithubRepository, options: { organizationId: IOrganization['id']; tenantId: IOrganization['tenantId']; @@ -119,7 +119,7 @@ export class GithubService { ): Observable { return this._http.post(`${API_PREFIX}/integration/github/${integrationId}/auto-sync/issues`, { integrationId, - repository: this._mapRepositoryPayload(repository), + repository, projectId: options.projectId, organizationId: options.organizationId, tenantId: options.tenantId @@ -135,7 +135,7 @@ export class GithubService { */ public manualSyncIssues( integrationId: IIntegrationTenant['id'], - repository: IGithubRepository, + repository: IOrganizationGithubRepository, options: { organizationId: IOrganization['id']; tenantId: IOrganization['tenantId']; @@ -145,7 +145,7 @@ export class GithubService { ): Observable { return this._http.post(`${API_PREFIX}/integration/github/${integrationId}/manual-sync/issues`, { integrationId, - repository: this._mapRepositoryPayload(repository), + repository, issues: this._mapIssuePayload(options.issues), projectId: options.projectId, organizationId: options.organizationId, diff --git a/apps/gauzy/src/app/pages/integrations/github/components/view/view.component.html b/apps/gauzy/src/app/pages/integrations/github/components/view/view.component.html index 62a698271c7..fae94e211b0 100644 --- a/apps/gauzy/src/app/pages/integrations/github/components/view/view.component.html +++ b/apps/gauzy/src/app/pages/integrations/github/components/view/view.component.html @@ -49,140 +49,152 @@
- - - - - -
-
- -
-
- -
-
- -
-
- + + + + + + +
+
+ +
+
+ +
+
+ +
+
+ +
-
- - -
-
-
- {{'INTEGRATIONS.GITHUB_PAGE.AUTO_SYNC_TABLE_LABEL' | translate}} -
+ + +
+
+
+ {{'INTEGRATIONS.GITHUB_PAGE.AUTO_SYNC_TABLE_LABEL' | translate}} +
+
+
- -
- - - + + + + - - - - -
-
- -
-
- -
-
- -
-
- + + + + + +
+
+ +
+
+ +
+
+ +
+
+ +
-
- - -
- -
- -
- - - + + +
+
- -
- - + +
+ + + +
+
+ + + + diff --git a/apps/gauzy/src/app/pages/integrations/github/components/view/view.component.ts b/apps/gauzy/src/app/pages/integrations/github/components/view/view.component.ts index 4920ec85d90..235718682f4 100644 --- a/apps/gauzy/src/app/pages/integrations/github/components/view/view.component.ts +++ b/apps/gauzy/src/app/pages/integrations/github/components/view/view.component.ts @@ -3,9 +3,9 @@ import { TitleCasePipe } from '@angular/common'; import { ActivatedRoute, Data, Router } from '@angular/router'; import { BehaviorSubject, EMPTY, Subject, debounceTime, finalize, first, firstValueFrom, of } from 'rxjs'; import { Observable } from 'rxjs/internal/Observable'; -import { catchError, filter, map, switchMap, tap } from 'rxjs/operators'; +import { catchError, filter, map, mergeMap, switchMap, tap } from 'rxjs/operators'; import { TranslateService } from '@ngx-translate/core'; -import { NbDialogService } from '@nebular/theme'; +import { NbDialogService, NbTabComponent } from '@nebular/theme'; import { UntilDestroy, untilDestroyed } from '@ngneat/until-destroy'; import { Ng2SmartTableComponent } from 'ng2-smart-table'; import { @@ -42,10 +42,16 @@ import { ProjectComponent, GithubRepositoryComponent, GithubIssueTitleDescriptionComponent, - ToggleSwitchComponent + ToggleSwitchComponent, + ResyncButtonComponent } from './../../../../../@shared/table-components'; import { GithubSettingsDialogComponent } from '../settings-dialog/settings-dialog.component'; +export enum SyncTabsEnum { + AUTO_SYNC = 'AUTO_SYNC', + MANUAL_SYNC = 'MANUAL_SYNC', +} + @UntilDestroy({ checkProperties: true }) @Component({ styleUrls: ['./view.component.scss'], @@ -54,6 +60,8 @@ import { GithubSettingsDialogComponent } from '../settings-dialog/settings-dialo }) export class GithubViewComponent extends PaginationFilterBaseComponent implements AfterViewInit, OnInit { + public syncTabsEnum: typeof SyncTabsEnum = SyncTabsEnum; + public nbTab$: Subject = new BehaviorSubject(SyncTabsEnum.AUTO_SYNC); public page$: Observable; // Observable for the organization project public settingsSmartTableIssues: object; // Settings for the Smart Table used for issues public settingsSmartTableProjects: object; // Settings for the Smart Table used for projects @@ -382,8 +390,8 @@ export class GithubViewComponent extends PaginationFilterBaseComponent implement hasSyncEnabled: { title: this.getTranslation('SM_TABLE.ENABLED_DISABLED_SYNC'), type: 'custom', - filter: false, renderComponent: ToggleSwitchComponent, + filter: false, valuePrepareFunction: (i: any, row: IOrganizationProject) => { return row?.repository?.hasSyncEnabled || false; }, @@ -398,6 +406,23 @@ export class GithubViewComponent extends PaginationFilterBaseComponent implement }); } }, + resync: { + title: this.getTranslation('SM_TABLE.RESYNC_ISSUES'), + type: 'custom', + renderComponent: ResyncButtonComponent, + filter: false, + onComponentInitFunction: (instance: any) => { + instance.clicked.subscribe({ + next: (event: Event) => { + // this.reSyncIssues(instance.rowData).subscribe(); + }, + error: (error: any) => { + // Handle and log errors + this._errorHandlingService.handleError(error); + } + }); + } + }, status: { title: this.getTranslation('SM_TABLE.STATUS'), // Set column title based on translation type: 'custom', @@ -527,6 +552,13 @@ export class GithubViewComponent extends PaginationFilterBaseComponent implement this.selectedIssues = selected; } + /** + * + */ + onChangeTab(tab: NbTabComponent) { + this.nbTab$.next(tab.tabId); + } + /** * Check if there is a valid organization, repository, and project. * If valid, log the organization, repository, and project to the console. @@ -549,20 +581,22 @@ export class GithubViewComponent extends PaginationFilterBaseComponent implement const { id: organizationId, tenantId } = this.organization; const { id: integrationId } = this.integration; const { id: projectId } = this.project; - const repository = this.repository; // Create a request object for syncing the GitHub repository const repositorySyncRequest: IIntegrationMapSyncRepository = { organizationId, tenantId, integrationId, - repository + repository: this.repository }; + let repository: IOrganizationGithubRepository; + // Synchronize the GitHub repository and update project settings this._githubService.syncGithubRepository(repositorySyncRequest) .pipe( - switchMap(({ id: repositoryId }: IOrganizationGithubRepository) => + tap((item: IOrganizationGithubRepository) => repository = item), + mergeMap(({ id: repositoryId }: IOrganizationGithubRepository) => this._organizationProjectsService.updateProjectSetting(projectId, { organizationId, tenantId, @@ -575,13 +609,16 @@ export class GithubViewComponent extends PaginationFilterBaseComponent implement throw new Error(`${response['message']}`); } }), - tap(() => this.subject$.next(true)), - switchMap(() => - this._githubService.autoSyncIssues(integrationId, this.repository, { - projectId, - organizationId, - tenantId - }) + mergeMap(() => + this._githubService.autoSyncIssues( + integrationId, + repository, + { + projectId, + organizationId, + tenantId + } + ) ), tap((process: boolean) => { if (process) { @@ -636,20 +673,22 @@ export class GithubViewComponent extends PaginationFilterBaseComponent implement const { id: organizationId, tenantId } = this.organization; const { id: integrationId } = this.integration; const { id: projectId } = this.project; - const repository = this.repository; // Create a request object for syncing the GitHub repository const repositorySyncRequest: IIntegrationMapSyncRepository = { organizationId, tenantId, integrationId, - repository + repository: this.repository }; + let repository: IOrganizationGithubRepository; + // Synchronize the GitHub repository and update project settings this._githubService.syncGithubRepository(repositorySyncRequest) .pipe( - switchMap(({ id: repositoryId }: IOrganizationGithubRepository) => + tap((item: IOrganizationGithubRepository) => repository = item), + mergeMap(({ id: repositoryId }: IOrganizationGithubRepository) => this._organizationProjectsService.updateProjectSetting(projectId, { organizationId, tenantId, @@ -657,10 +696,10 @@ export class GithubViewComponent extends PaginationFilterBaseComponent implement syncTag: SYNC_TAG_GAUZY }) ), - switchMap(() => + mergeMap(() => this._githubService.manualSyncIssues( integrationId, - this.repository, + repository, { projectId, organizationId, @@ -683,8 +722,8 @@ export class GithubViewComponent extends PaginationFilterBaseComponent implement this.getTranslation('TOASTR.TITLE.SUCCESS') ); } - this.subject$.next(true); this.resetTableSelectedItems(); + this.getRepositoryIssue(); }), catchError((error) => { // Handle and log errors @@ -741,7 +780,7 @@ export class GithubViewComponent extends PaginationFilterBaseComponent implement switch (row.status) { case GithubRepositoryStatusEnum.SYNCING: - badgeClass = 'primary'; + badgeClass = 'info'; break; case GithubRepositoryStatusEnum.SUCCESSFULLY: badgeClass = 'success'; diff --git a/apps/gauzy/src/assets/i18n/en.json b/apps/gauzy/src/assets/i18n/en.json index b6f8dc10a89..0d8b43840ba 100644 --- a/apps/gauzy/src/assets/i18n/en.json +++ b/apps/gauzy/src/assets/i18n/en.json @@ -160,7 +160,8 @@ "REPORT": "Report", "IGNORE": "Ignore", "ENABLED": "Enabled", - "DISABLED": "Disabled" + "DISABLED": "Disabled", + "RESYNC": "Resync" }, "SM_TABLE": { "NO_DATA": { @@ -307,6 +308,7 @@ "GITHUB_REPOSITORY": "GitHub Repository", "ISSUES_SYNC": "Issues in Sync", "ISSUES_SYNC_COUNT": "{{ count }} Issues Auto Sync", + "RESYNC_ISSUES": "Resync Issues", "ENABLED_DISABLED_SYNC": "Enabled / Disabled Sync", "ENABLE_DISABLE_INTEGRATION": "Enable / Disable Integration", "ACTIONS": "Actions" diff --git a/packages/common-angular/src/utils/shared-utils.ts b/packages/common-angular/src/utils/shared-utils.ts index caff2f4711e..dbdfa69e3f5 100644 --- a/packages/common-angular/src/utils/shared-utils.ts +++ b/packages/common-angular/src/utils/shared-utils.ts @@ -363,6 +363,4 @@ export function sleep(ms: number) { * @param {string} value - The string to parse into an integer. * @returns {number} - The parsed integer value. */ -export function parsedInt(value: string) { - return parseInt(value, 10); // Using base 10 for decimal numbers, as a best practice. -} +export const parsedInt = (value: string) => parseInt(value, 10); diff --git a/packages/common/src/utils/shared-utils.ts b/packages/common/src/utils/shared-utils.ts index 9bf27494530..9fbaec1fa48 100644 --- a/packages/common/src/utils/shared-utils.ts +++ b/packages/common/src/utils/shared-utils.ts @@ -183,3 +183,11 @@ export function sluggable(string: string, replacement: any = '-'): string { trim: true // trim leading and trailing replacement chars, defaults to `true` }).replace(/[_]/g, replacement); } + +/** + * How To Make A Sleep Function In TypeScript? + * + * @param ms + * @returns + */ +export const sleep = (ms: number) => new Promise((r) => setTimeout(r, ms)); diff --git a/packages/contracts/src/github.model.ts b/packages/contracts/src/github.model.ts index 44dd391d8af..916f3f42629 100644 --- a/packages/contracts/src/github.model.ts +++ b/packages/contracts/src/github.model.ts @@ -102,6 +102,12 @@ export enum GithubPropertyMapEnum { TOKEN_TYPE = 'token_type', SYNC_TAG = 'sync_tag' } + +export interface IGithubSyncIssuePayload extends IBasePerTenantAndOrganizationEntityModel, IRelationalOrganizationProject { + issues: IGithubIssue | IGithubIssue[]; + repository: IOrganizationGithubRepository; +} + /** * Represents a payload for GitHub issues, including organization and tenant information. */ @@ -110,13 +116,8 @@ export interface IGithubRepositoryPayload { } /** */ -export interface IGithubSyncIssuePayload extends IGithubRepositoryPayload, IBasePerTenantAndOrganizationEntityModel, IRelationalOrganizationProject { - issues: IGithubIssue | IGithubIssue[]; -} - export interface IGithubAutomationBase extends IGithubRepositoryPayload { integration: IIntegrationTenant; - repository: IGithubRepository; } export interface IGithubAutomationIssuePayload extends IGithubAutomationBase { diff --git a/packages/core/src/integration/github/dto/process-github-issue-sync.dto.ts b/packages/core/src/integration/github/dto/process-github-issue-sync.dto.ts index d0a4cc0b6f6..f770d174a1c 100644 --- a/packages/core/src/integration/github/dto/process-github-issue-sync.dto.ts +++ b/packages/core/src/integration/github/dto/process-github-issue-sync.dto.ts @@ -1,4 +1,4 @@ -import { IGithubIssue, IGithubRepository, IGithubSyncIssuePayload, IOrganizationProject } from "@gauzy/contracts"; +import { IGithubIssue, IGithubSyncIssuePayload, IOrganizationGithubRepository, IOrganizationProject } from "@gauzy/contracts"; import { ApiPropertyOptional } from "@nestjs/swagger"; import { IsArray, IsObject, IsOptional, IsUUID } from "class-validator"; import { TenantOrganizationBaseDTO } from "core/dto"; @@ -19,7 +19,7 @@ export class ProcessGithubIssueSyncDTO extends TenantOrganizationBaseDTO impleme @ApiPropertyOptional({ type: () => String }) @IsOptional() @IsObject() - readonly repository: IGithubRepository; + readonly repository: IOrganizationGithubRepository; @ApiPropertyOptional({ type: () => String }) @IsOptional() diff --git a/packages/core/src/integration/github/github-sync.controller.ts b/packages/core/src/integration/github/github-sync.controller.ts index d4b48231bed..cc2bbb1ac74 100644 --- a/packages/core/src/integration/github/github-sync.controller.ts +++ b/packages/core/src/integration/github/github-sync.controller.ts @@ -30,9 +30,10 @@ export class GitHubSyncController { ) { } /** + * Handle an HTTP POST request to manually synchronize GitHub issues and labels. * - * @param body - * @returns + * @param body - The request body containing data for synchronization. + * @returns An HTTP response with the result of the synchronization. */ @Post('/manual-sync/issues') @HttpCode(HttpStatus.CREATED) @@ -47,6 +48,8 @@ export class GitHubSyncController { if (!input || !input.organizationId) { throw new HttpException('Invalid sync issues & labels request parameters', HttpStatus.BAD_REQUEST); } + + // Call a service method to perform manual synchronization of GitHub issues and labels return await this._githubSyncService.manualSyncGithubIssues(integrationId, input, request); } catch (error) { // Handle errors and return an appropriate error response @@ -56,9 +59,10 @@ export class GitHubSyncController { } /** + * Handle an HTTP POST request to automatically synchronize GitHub issues. * - * @param body - * @returns + * @param body - The request body containing data for synchronization. + * @returns An HTTP response with the result of the synchronization. */ @Post('/auto-sync/issues') @HttpCode(HttpStatus.CREATED) diff --git a/packages/core/src/integration/github/github-sync.service.ts b/packages/core/src/integration/github/github-sync.service.ts index cd3b5a13bd2..13c0dca2dd1 100644 --- a/packages/core/src/integration/github/github-sync.service.ts +++ b/packages/core/src/integration/github/github-sync.service.ts @@ -8,7 +8,6 @@ import { IGithubAutomationIssuePayload, IGithubIssue, IGithubIssueLabel, - IGithubRepository, IGithubSyncIssuePayload, IGithubInstallationDeletedPayload, IIntegrationEntitySetting, @@ -30,13 +29,11 @@ import { isNotEmpty } from '@gauzy/common'; import { RequestContext } from 'core/context'; import { arrayToObject } from 'core/utils'; import { IntegrationTenantService } from 'integration-tenant/integration-tenant.service'; -import { OrganizationProjectSettingUpdateCommand } from 'organization-project/commands'; import { OrganizationProjectService } from 'organization-project/organization-project.service'; import { IntegrationMapSyncIssueCommand, IntegrationMapSyncLabelCommand } from 'integration-map/commands'; import { AutomationTaskSyncCommand } from 'tasks/commands'; import { AutomationLabelSyncCommand } from 'tags/commands'; import { GithubRepositoryService } from './repository/github-repository.service'; -import { IntegrationSyncGithubRepositoryCommand } from './commands'; import { IntegrationSyncGithubRepositoryIssueCommand } from './repository/issue/commands'; @Injectable() @@ -68,29 +65,23 @@ export class GithubSyncService { } try { - // Synchronize a GitHub repository based on the input data and store the result in 'repository'. - const repository: IOrganizationGithubRepository = await this._commandBus.execute( - new IntegrationSyncGithubRepositoryCommand(input) - ); - - // Update the organization project setting with the synchronized repository's ID. - await this._commandBus.execute( - new OrganizationProjectSettingUpdateCommand(input.projectId, { - repositoryId: repository.id - }) - ); - /** */ - const installation_id = settings['installation_id']; - if (installation_id) { - /** */ + // Extract the 'repository' object from the input payload + const repository: IOrganizationGithubRepository = input.repository; + + try { + // Extract the 'installation_id' from the integration settings + const installation_id = settings['installation_id']; + + // Extract repository details const { name: repo, owner } = repository; - const issues = await this.getRepositoryAllIssues(settings.installation_id, owner, repo); + // Retrieve GitHub issues for the repository + const issues = await this.getRepositoryAllIssues(installation_id, owner, repo); console.log(`Automatically syncing ${issues.length} issues`); + // Map the issues to the desired format using '_mapIssuePayload' method input.issues = this._mapIssuePayload(Array.isArray(issues) ? issues : [issues]); - } - try { + // Attempt to synchronize GitHub issues using the syncGithubIssues method. await this.syncingGithubIssues(integrationId, input); @@ -138,17 +129,8 @@ export class GithubSyncService { throw new HttpException('Invalid request parameter: Missing or unauthorized integration', HttpStatus.UNAUTHORIZED); } - // Synchronize a GitHub repository based on the input data and store the result in 'repository'. - const repository: IOrganizationGithubRepository = await this._commandBus.execute( - new IntegrationSyncGithubRepositoryCommand(input) - ); - - // Update the organization project setting with the synchronized repository's ID. - await this._commandBus.execute( - new OrganizationProjectSettingUpdateCommand(input.projectId, { - repositoryId: repository.id - }) - ); + // Extract the 'repository' object from the input payload + const repository: IOrganizationGithubRepository = input.repository; try { // Attempt to synchronize GitHub issues using the syncGithubIssues method. @@ -192,6 +174,7 @@ export class GithubSyncService { ): Promise { try { const { organizationId, repository } = input; + const tenantId = RequestContext.currentTenantId() || input.tenantId; const issues: IGithubIssue[] = Array.isArray(input.issues) ? input.issues : [input.issues]; @@ -244,8 +227,8 @@ export class GithubSyncService { console.error('Failed to fetch GitHub labels for the repository issue:', error.message); } - // Step 7: Synchronized GitHub repository issue. - const repositoryId = repository.id; + // Step 7: Synchronized GitHub Repository Issue. + const { repositoryId } = repository; await this._commandBus.execute( new IntegrationSyncGithubRepositoryIssueCommand( { @@ -321,7 +304,7 @@ export class GithubSyncService { organizationId: IOrganization['id'], tenantId: IOrganization['tenantId'], integrationId: IIntegrationTenant['id'], - repository: IGithubRepository, + repository: IOrganizationGithubRepository, issue_number: IGithubIssue['number'] }): Promise { try { @@ -342,7 +325,7 @@ export class GithubSyncService { const installation_id = settings.installation_id; /** Get Github Labels */ const response = await this._octokitService.getLabelsByIssueNumber(installation_id, { - owner: owner.login, + owner, repo, issue_number }); @@ -359,7 +342,7 @@ export class GithubSyncService { if (isNotEmpty(labelsToCreate)) { try { const response = await this._octokitService.createLabelsForIssue(installation_id, { - owner: owner.login, + owner, repo, issue_number, labels: labelsToCreate @@ -665,7 +648,13 @@ export class GithubSyncService { // Use a while to simplify pagination while (hasMoreIssues) { try { - const response = await this._octokitService.getRepositoryIssues(installation_id, { owner, repo, page, per_page }); + // Fetch issues for the current page + const response = await this._octokitService.getRepositoryIssues(installation_id, { + owner, + repo, + page, + per_page + }); if (Array.isArray(response.data) && response.data.length > 0) { // Append the retrieved issues to the result array issues.push(...response.data); diff --git a/packages/core/src/integration/github/repository/issue/commands/integration-sync-github-repository-issue.command.ts b/packages/core/src/integration/github/repository/issue/commands/integration-sync-github-repository-issue.command.ts index c3231d63253..5a7abbd2183 100644 --- a/packages/core/src/integration/github/repository/issue/commands/integration-sync-github-repository-issue.command.ts +++ b/packages/core/src/integration/github/repository/issue/commands/integration-sync-github-repository-issue.command.ts @@ -1,12 +1,12 @@ import { ICommand } from '@nestjs/cqrs'; -import { IGithubIssue, IGithubRepository, IIntegrationMapSyncBase } from '@gauzy/contracts'; +import { IGithubIssue, IIntegrationMapSyncBase, IOrganizationGithubRepository } from '@gauzy/contracts'; export class IntegrationSyncGithubRepositoryIssueCommand implements ICommand { static readonly type = '[Integration] Sync Github Repository Issue'; constructor( public readonly input: IIntegrationMapSyncBase, - public readonly repositoryId: IGithubRepository['id'], + public readonly repositoryId: IOrganizationGithubRepository['repositoryId'], public readonly issue: IGithubIssue, ) { } } diff --git a/packages/core/src/organization-project/organization-project.service.ts b/packages/core/src/organization-project/organization-project.service.ts index 8bd65570454..c5343c2ebb4 100644 --- a/packages/core/src/organization-project/organization-project.service.ts +++ b/packages/core/src/organization-project/organization-project.service.ts @@ -137,7 +137,8 @@ export class OrganizationProjectService extends TenantAwareCrudService Date: Sun, 5 Nov 2023 15:43:24 +0530 Subject: [PATCH 06/15] fix: #7000 if repository has synced disabled not create in github --- .../handlers/task.update-or-create.handler.ts | 153 ++++++++++-------- 1 file changed, 83 insertions(+), 70 deletions(-) diff --git a/packages/core/src/integration/github/commands/handlers/task.update-or-create.handler.ts b/packages/core/src/integration/github/commands/handlers/task.update-or-create.handler.ts index 8844104571d..0ac7e82594a 100644 --- a/packages/core/src/integration/github/commands/handlers/task.update-or-create.handler.ts +++ b/packages/core/src/integration/github/commands/handlers/task.update-or-create.handler.ts @@ -75,90 +75,103 @@ export class GithubTaskUpdateOrCreateCommandHandler implements ICommandHandler Date: Sun, 5 Nov 2023 16:47:06 +0530 Subject: [PATCH 07/15] fix: #7000 resync repository & project issues --- .../resync-button.component.html | 3 +- .../resync-button/resync-button.component.ts | 29 +---------- .../github/components/view/view.component.ts | 51 +++++++++++++++++-- 3 files changed, 50 insertions(+), 33 deletions(-) diff --git a/apps/gauzy/src/app/@shared/table-components/github/resync-button/resync-button.component.html b/apps/gauzy/src/app/@shared/table-components/github/resync-button/resync-button.component.html index 93174669655..7a994ef7351 100644 --- a/apps/gauzy/src/app/@shared/table-components/github/resync-button/resync-button.component.html +++ b/apps/gauzy/src/app/@shared/table-components/github/resync-button/resync-button.component.html @@ -2,13 +2,12 @@ nbButton status="primary" class="mr-2" - size="tiny" debounceClick (throttledClick)="onClicked($event)" [disabled]="!rowData?.repository?.hasSyncEnabled" >
- + {{ 'BUTTONS.RESYNC' | translate }}
diff --git a/apps/gauzy/src/app/@shared/table-components/github/resync-button/resync-button.component.ts b/apps/gauzy/src/app/@shared/table-components/github/resync-button/resync-button.component.ts index fc0e459efd3..73da728a28b 100644 --- a/apps/gauzy/src/app/@shared/table-components/github/resync-button/resync-button.component.ts +++ b/apps/gauzy/src/app/@shared/table-components/github/resync-button/resync-button.component.ts @@ -4,7 +4,7 @@ import { ViewCell } from 'ng2-smart-table'; @Component({ selector: 'ngx-resync-button', templateUrl: './resync-button.component.html', - styleUrls: [] + styleUrls: [], }) export class ResyncButtonComponent implements ViewCell { @@ -37,31 +37,6 @@ export class ResyncButtonComponent implements ViewCell { */ @Input() rowData: any; - /** - * Associated getter/setter for managing a boolean value indicating synchronization status. - */ - private _syncing: boolean = false; - - /** - * Getter for retrieving the current synchronization status. - * - * @returns The current synchronization status (a boolean value). - */ - get syncing(): boolean { - return this._syncing; - } - - /** - * Setter for updating the synchronization status. - * This setter is decorated with @Input, allowing external components to set and update the synchronization status. - * - * @param value - The new synchronization status (a boolean value). - */ - @Input() set syncing(value: boolean) { - // Update the synchronization status with the provided value. - this._syncing = value; - } - /** * An output property for emitting click events. * @@ -69,8 +44,6 @@ export class ResyncButtonComponent implements ViewCell { */ @Output() clicked: EventEmitter = new EventEmitter(); - constructor() { } - /** * Handle a click event, conditionally emitting it for further processing. * diff --git a/apps/gauzy/src/app/pages/integrations/github/components/view/view.component.ts b/apps/gauzy/src/app/pages/integrations/github/components/view/view.component.ts index 235718682f4..6860a9863a7 100644 --- a/apps/gauzy/src/app/pages/integrations/github/components/view/view.component.ts +++ b/apps/gauzy/src/app/pages/integrations/github/components/view/view.component.ts @@ -411,10 +411,10 @@ export class GithubViewComponent extends PaginationFilterBaseComponent implement type: 'custom', renderComponent: ResyncButtonComponent, filter: false, - onComponentInitFunction: (instance: any) => { + onComponentInitFunction: (instance: ResyncButtonComponent) => { instance.clicked.subscribe({ - next: (event: Event) => { - // this.reSyncIssues(instance.rowData).subscribe(); + next: () => { + this.resyncIssues(instance.rowData); }, error: (error: any) => { // Handle and log errors @@ -746,6 +746,51 @@ export class GithubViewComponent extends PaginationFilterBaseComponent implement } } + /** + * + * @param project + */ + resyncIssues(project: IOrganizationProject) { + try { + // Ensure there is a valid organization, and project + if (!this.organization || !project || !project.repository) { + return; + } + + this.loading = true; + + this.project = project; + const { repository } = project; + + const { id: organizationId, tenantId } = this.organization; + const { id: integrationId } = this.integration; + const { id: projectId } = this.project; + + this._githubService.autoSyncIssues( + integrationId, + repository, + { + projectId, + organizationId, + tenantId + } + ).pipe( + catchError((error) => { + this._errorHandlingService.handleError(error); + return EMPTY; + }), + // Execute the following code block when the observable completes or errors + tap(() => { + this.loading = false; + }), + // Automatically unsubscribe when the component is destroyed + untilDestroyed(this) + ).subscribe();; + } catch (error) { + console.log(error); + } + } + /** * Clears selected items in the table component and resets the 'selectedIssues' array. */ From 6a77572954e8a34ff9b48b372725f51637df2b41 Mon Sep 17 00:00:00 2001 From: RAHUL RATHORE <41804588+rahul-rocket@users.noreply.github.com> Date: Mon, 6 Nov 2023 12:53:07 +0530 Subject: [PATCH 08/15] fix: #7000 Without waiting for sync of labels to finish --- .../integration-list/list.component.ts | 3 + .../github/components/view/view.component.ts | 20 +++- .../integration-map.sync-issue.handler.ts | 1 - .../integration-map.sync-label.handler.ts | 1 - .../integration-map.sync-task.handler.ts | 1 - .../integration-tenant.service.ts | 23 +++- .../integration/github/github-sync.service.ts | 110 ++++++++++++------ .../organization-project.entity.ts | 5 + .../handlers/automation-label.sync.handler.ts | 3 +- .../handlers/automation-task.sync.handler.ts | 4 +- 10 files changed, 124 insertions(+), 47 deletions(-) diff --git a/apps/gauzy/src/app/pages/integrations/components/integration-list/list.component.ts b/apps/gauzy/src/app/pages/integrations/components/integration-list/list.component.ts index b95641f8a30..a04009ef852 100644 --- a/apps/gauzy/src/app/pages/integrations/components/integration-list/list.component.ts +++ b/apps/gauzy/src/app/pages/integrations/components/integration-list/list.component.ts @@ -234,6 +234,9 @@ export class IntegrationListComponent extends TranslationBaseComponent implement * @returns The description of the integration provider, or undefined if the provider is missing or not found. */ getProviderDescription(integration: IIntegration): string | null { + if (!integration) { + return; + } return integration.provider ? this.providers[integration.provider]?.description : null; } diff --git a/apps/gauzy/src/app/pages/integrations/github/components/view/view.component.ts b/apps/gauzy/src/app/pages/integrations/github/components/view/view.component.ts index 6860a9863a7..0ba05e45058 100644 --- a/apps/gauzy/src/app/pages/integrations/github/components/view/view.component.ts +++ b/apps/gauzy/src/app/pages/integrations/github/components/view/view.component.ts @@ -775,14 +775,28 @@ export class GithubViewComponent extends PaginationFilterBaseComponent implement tenantId } ).pipe( + tap((response: any) => { + if (response['status'] == HttpStatus.BAD_REQUEST) { + throw new Error(`${response['message']}`); + } + }), + tap((process: boolean) => { + if (process) { + this._toastrService.success( + this.getTranslation('INTEGRATIONS.GITHUB_PAGE.SYNCED_ISSUES', { + repository: repository.fullName + }), + this.getTranslation('TOASTR.TITLE.SUCCESS') + ); + } + this.subject$.next(true); + }), catchError((error) => { this._errorHandlingService.handleError(error); return EMPTY; }), // Execute the following code block when the observable completes or errors - tap(() => { - this.loading = false; - }), + finalize(() => this.loading = false), // Automatically unsubscribe when the component is destroyed untilDestroyed(this) ).subscribe();; diff --git a/packages/core/src/integration-map/commands/handlers/integration-map.sync-issue.handler.ts b/packages/core/src/integration-map/commands/handlers/integration-map.sync-issue.handler.ts index ef2124be44d..0d4c9d9b167 100644 --- a/packages/core/src/integration-map/commands/handlers/integration-map.sync-issue.handler.ts +++ b/packages/core/src/integration-map/commands/handlers/integration-map.sync-issue.handler.ts @@ -45,7 +45,6 @@ export class IntegrationMapSyncIssueHandler implements ICommandHandler): Promise> { + // Define where conditions by merging provided options with a condition for non-null integrationId. + const whereConditions = { + ...options?.where, + integrationId: Not(IsNull()) + }; + + // Call the superclass's findAll method with merged options and where conditions. + return await super.findAll({ + ...options, + where: whereConditions, + }); + } + /** * Create a new integration tenant with the provided input. * @param input The data for creating the integration tenant. diff --git a/packages/core/src/integration/github/github-sync.service.ts b/packages/core/src/integration/github/github-sync.service.ts index 13c0dca2dd1..2eaa9778108 100644 --- a/packages/core/src/integration/github/github-sync.service.ts +++ b/packages/core/src/integration/github/github-sync.service.ts @@ -25,7 +25,7 @@ import { SYNC_TAG_GAUZY, SYNC_TAG_GITHUB } from '@gauzy/contracts'; -import { isNotEmpty } from '@gauzy/common'; +import { isNotEmpty, sleep } from '@gauzy/common'; import { RequestContext } from 'core/context'; import { arrayToObject } from 'core/utils'; import { IntegrationTenantService } from 'integration-tenant/integration-tenant.service'; @@ -49,15 +49,18 @@ export class GithubSyncService { ) { } /** - * Initiates an automatic synchronization of GitHub issues. - * @param integrationId - The unique identifier of the GitHub integration. - * @param input - An object containing data needed for the synchronization. + * Automatically synchronize GitHub issues with a repository. + * + * @param {IIntegrationTenant['id']} integrationId - The ID of the integration tenant. + * @param {IGithubSyncIssuePayload} input - The payload containing GitHub repository details and issues. + * @param {Request} request - The HTTP request object. + * @returns {Promise} A Promise that indicates whether the synchronization was successful. */ public async autoSyncGithubIssues( integrationId: IIntegrationTenant['id'], input: IGithubSyncIssuePayload, request: Request - ) { + ): Promise { // Check if the request contains integration settings const settings = request['integration']?.settings; if (!settings || !settings.installation_id) { @@ -76,30 +79,38 @@ export class GithubSyncService { const { name: repo, owner } = repository; // Retrieve GitHub issues for the repository - const issues = await this.getRepositoryAllIssues(installation_id, owner, repo); - console.log(`Automatically syncing ${issues.length} issues`); + this.getRepositoryAllIssues(installation_id, owner, repo, (issues: IGithubIssue[]) => { + console.log(`Automatically syncing ${issues.length} issues`); - // Map the issues to the desired format using '_mapIssuePayload' method - input.issues = this._mapIssuePayload(Array.isArray(issues) ? issues : [issues]); + // Map the issues to the desired format using '_mapIssuePayload' method + input.issues = this._mapIssuePayload(Array.isArray(issues) ? issues : [issues]); - // Attempt to synchronize GitHub issues using the syncGithubIssues method. - await this.syncingGithubIssues(integrationId, input); + // Define a delay of 100 milliseconds + const delay: number = 100; - // Update the status of the GitHub repository to "Success" (GithubRepositoryStatusEnum.SUCCESSFULLY). - await this._githubRepositoryService.update(repository.id, { - status: GithubRepositoryStatusEnum.SUCCESSFULLY + // Attempt to synchronize GitHub issues using the 'syncingGithubIssues' method + this.syncingGithubIssues(integrationId, input, delay, async () => { + // Update the status of the GitHub repository to "Success" (GithubRepositoryStatusEnum.SUCCESSFULLY). + await this._githubRepositoryService.update(repository.id, { + status: GithubRepositoryStatusEnum.SUCCESSFULLY + }); + }, async () => { + // Handle the error by updating the status of the GitHub repository to "Error" (GithubRepositoryStatusEnum.ERROR). + await this._githubRepositoryService.update(repository.id, { + status: GithubRepositoryStatusEnum.ERROR + }); + }); }); - // Return true to indicate a successful synchronization. - return true; + return true; // Return true to indicate a successful synchronization. } catch (error) { + console.log('Error while syncing github issues automatically: %s', error.message); // Handle the error by updating the status of the GitHub repository to "Error" (GithubRepositoryStatusEnum.ERROR). await this._githubRepositoryService.update(repository.id, { status: GithubRepositoryStatusEnum.ERROR }); - // Return false to indicate that an error occurred during synchronization. - return false; + return false; // Return false to indicate that an error occurred during synchronization. } } catch (error) { // Handle errors gracefully, for example, log them @@ -109,13 +120,12 @@ export class GithubSyncService { } /** - * Manually synchronize GitHub issues and handle errors. + * Manually synchronize GitHub issues with a repository. * - * @param integrationId - The ID of the GitHub integration. - * @param input - Payload for GitHub issue synchronization. - * @param request - The HTTP request object. - * @returns `true` on successful synchronization, `false` on failure. - * @throws HttpException if synchronization encounters an error. + * @param {IIntegrationTenant['id']} integrationId - The ID of the integration tenant. + * @param {IGithubSyncIssuePayload} input - The payload containing GitHub repository details and issues. + * @param {Request} request - The HTTP request object. + * @returns {Promise} A Promise indicating whether the synchronization was successful. */ public async manualSyncGithubIssues( integrationId: IIntegrationTenant['id'], @@ -133,24 +143,30 @@ export class GithubSyncService { const repository: IOrganizationGithubRepository = input.repository; try { - // Attempt to synchronize GitHub issues using the syncGithubIssues method. - await this.syncingGithubIssues(integrationId, input); + // Set a delay of 0 milliseconds + const delay: number = 0; - // Update the status of the GitHub repository to "Success" (GithubRepositoryStatusEnum.SUCCESSFULLY). - await this._githubRepositoryService.update(repository.id, { - status: GithubRepositoryStatusEnum.SUCCESSFULLY + // Attempt to synchronize GitHub issues using the syncGithubIssues method. + this.syncingGithubIssues(integrationId, input, delay, async () => { + // Update the status of the GitHub repository to "Success" (GithubRepositoryStatusEnum.SUCCESSFULLY). + await this._githubRepositoryService.update(repository.id, { + status: GithubRepositoryStatusEnum.SUCCESSFULLY + }); + }, async () => { + // Handle the error by updating the status of the GitHub repository to "Error" (GithubRepositoryStatusEnum.ERROR). + await this._githubRepositoryService.update(repository.id, { + status: GithubRepositoryStatusEnum.ERROR + }); }); - // Return true to indicate a successful synchronization. - return true; + return true; // Return true to indicate a successful synchronization. } catch (error) { // Handle the error by updating the status of the GitHub repository to "Error" (GithubRepositoryStatusEnum.ERROR). await this._githubRepositoryService.update(repository.id, { status: GithubRepositoryStatusEnum.ERROR }); - // Return false to indicate that an error occurred during synchronization. - return false; + return false; // Return false to indicate that an error occurred during synchronization. } } catch (error) { // Handle errors gracefully, for example, log them @@ -170,7 +186,10 @@ export class GithubSyncService { */ public async syncingGithubIssues( integrationId: IIntegrationTenant['id'], - input: IGithubSyncIssuePayload + input: IGithubSyncIssuePayload, + delay: number = 100, + successCallback?: (success: boolean) => void, + errorCallback?: (error: boolean) => void, ): Promise { try { const { organizationId, repository } = input; @@ -205,6 +224,7 @@ export class GithubSyncService { const issueSetting: IIntegrationEntitySetting = entitySetting; if (!!issueSetting.sync) { for await (const issue of issues) { + console.info('Processing Issue Sync: %s', issue.id); const { id, number: issue_number, title, state, body } = issue; let tags: ITag[] = []; @@ -262,6 +282,9 @@ export class GithubSyncService { }, triggeredEvent) ); integrationMaps.push(integrationMap); + + /** 100ms Pause or Delay for sync new sync issue */ + await sleep(delay); } } break; @@ -273,10 +296,20 @@ export class GithubSyncService { lastSyncedAt: moment() }); + // Call the success callback function if provided + if (successCallback) { + successCallback(true); + } + // Step 10: Return integration mapping return integrationMaps; } catch (error) { console.log('Error while syncing github issues: ', error.message); + // Call the error callback function if provided + if (errorCallback) { + errorCallback(false); + } + return false; } } catch (error) { @@ -638,7 +671,8 @@ export class GithubSyncService { async getRepositoryAllIssues( installation_id: number, owner: string, - repo: string + repo: string, + callback?: (issues: IGithubIssue[]) => void ): Promise { const per_page = 100; // Number of issues per page (GitHub API maximum is 100) const issues: IGithubIssue[] = []; @@ -671,6 +705,12 @@ export class GithubSyncService { break; // Exit the loop on error } } + + // Call the callback function if provided + if (callback) { + callback(issues); + } + return issues; } } diff --git a/packages/core/src/organization-project/organization-project.entity.ts b/packages/core/src/organization-project/organization-project.entity.ts index 33a2048dda4..d466c8ccf5e 100644 --- a/packages/core/src/organization-project/organization-project.entity.ts +++ b/packages/core/src/organization-project/organization-project.entity.ts @@ -180,8 +180,13 @@ export class OrganizationProject extends TenantOrganizationBaseEntity implements * Organization Contact */ @ManyToOne(() => OrganizationContact, (it) => it.projects, { + /** Indicates if relation column value can be nullable or not. */ nullable: true, + + /** Database cascade action on update. */ onUpdate: 'CASCADE', + + /** Database cascade action on delete. */ onDelete: 'SET NULL', }) @JoinColumn() diff --git a/packages/core/src/tags/commands/handlers/automation-label.sync.handler.ts b/packages/core/src/tags/commands/handlers/automation-label.sync.handler.ts index 5a48448f677..28c1241fa18 100644 --- a/packages/core/src/tags/commands/handlers/automation-label.sync.handler.ts +++ b/packages/core/src/tags/commands/handlers/automation-label.sync.handler.ts @@ -1,7 +1,7 @@ import { ICommandHandler, CommandHandler } from '@nestjs/cqrs'; import { InjectRepository } from '@nestjs/typeorm'; import { Repository } from 'typeorm'; -import { IOrganization, ITag, ITagCreateInput, ITagUpdateInput, IntegrationEntity } from '@gauzy/contracts'; +import { IOrganization, ITag, ITagCreateInput, ITagUpdateInput } from '@gauzy/contracts'; import { IntegrationMap } from 'core/entities/internal'; import { RequestContext } from 'core/context'; import { Tag } from './../../tag.entity'; @@ -44,7 +44,6 @@ export class AutomationLabelSyncHandler implements ICommandHandler Date: Mon, 6 Nov 2023 15:01:00 +0530 Subject: [PATCH 09/15] fix: organization project wrong relationship --- .../project-mutation.component.html | 2 +- packages/core/src/expense/expense.entity.ts | 43 ++++--- .../repository/github-repository.entity.ts | 8 +- .../src/invoice-item/invoice-item.entity.ts | 26 ++-- .../organization-contact.entity.ts | 8 +- .../organization-project.entity.ts | 120 ++++++++++-------- .../organization-project.seed.ts | 23 ++-- .../organization-sprint.entity.ts | 29 +++-- packages/core/src/payment/payment.entity.ts | 34 +++-- packages/core/src/tags/tag.entity.ts | 5 +- .../src/tasks/priorities/priority.entity.ts | 20 ++- .../related-issue-type.entity.ts | 25 +++- packages/core/src/tasks/sizes/size.entity.ts | 19 ++- .../core/src/tasks/statuses/status.entity.ts | 11 +- packages/core/src/tasks/task.entity.ts | 27 ++-- .../core/src/tasks/versions/version.entity.ts | 23 +++- .../time-tracking/activity/activity.entity.ts | 11 +- .../time-tracking/time-log/time-log.entity.ts | 12 +- 18 files changed, 295 insertions(+), 151 deletions(-) diff --git a/apps/gauzy/src/app/@shared/project/project-mutation/project-mutation.component.html b/apps/gauzy/src/app/@shared/project/project-mutation/project-mutation.component.html index 03890df853d..c2da290272e 100644 --- a/apps/gauzy/src/app/@shared/project/project-mutation/project-mutation.component.html +++ b/apps/gauzy/src/app/@shared/project/project-mutation/project-mutation.component.html @@ -667,7 +667,7 @@ - +
-
- - diff --git a/apps/gauzy/src/app/pages/integrations/github/components/settings-dialog/settings-dialog.component.scss b/apps/gauzy/src/app/pages/integrations/github/components/settings-dialog/settings-dialog.component.scss deleted file mode 100644 index 11f8086a799..00000000000 --- a/apps/gauzy/src/app/pages/integrations/github/components/settings-dialog/settings-dialog.component.scss +++ /dev/null @@ -1,41 +0,0 @@ -.switcher-wrapper { - display: flex; - flex-direction: column; - nb-toggle { - display: flex; - ::ng-deep .toggle-label { - display: flex; - justify-content: space-between; - flex-grow: 1; - } - } -} -.dialog-footer { - display: flex; - justify-content: flex-end; -} -.tied-entities-wrapper { - padding-left: 15px; -} -.visible { - height: 50px; - transition: 0.2s all ease-in-out; -} -.not-visible { - transition: 0.2s all ease-in-out; - height: 0px; - visibility: hidden; -} -.tied-entity { - display: flex; - align-items: center; - justify-content: space-between; -} -.entity-more-options { - display: flex; - align-items: center; - margin-left: -20px; - .label { - margin: 0 5px 0 0; - } -} diff --git a/apps/gauzy/src/app/pages/integrations/github/components/settings-dialog/settings-dialog.component.ts b/apps/gauzy/src/app/pages/integrations/github/components/settings-dialog/settings-dialog.component.ts deleted file mode 100644 index daec466a735..00000000000 --- a/apps/gauzy/src/app/pages/integrations/github/components/settings-dialog/settings-dialog.component.ts +++ /dev/null @@ -1,91 +0,0 @@ -import { - AfterViewInit, - ChangeDetectorRef, - Component, - Input, - OnInit -} from '@angular/core'; -import { Observable, finalize } from 'rxjs'; -import { map } from 'rxjs/operators'; -import { NbDialogRef } from '@nebular/theme'; -import { UntilDestroy, untilDestroyed } from '@ngneat/until-destroy'; -import { IEntitySettingToSync, IIntegrationTenant, IntegrationEntity } from '@gauzy/contracts'; -import { IntegrationEntitySettingService, IntegrationEntitySettingServiceStoreService } from './../../../../../@core/services'; - -@UntilDestroy() -@Component({ - selector: 'ngx-github-settings-dialog', - templateUrl: './settings-dialog.component.html', - styleUrls: ['./settings-dialog.component.scss'] -}) -export class GithubSettingsDialogComponent implements OnInit, AfterViewInit { - - // Define a public property 'IntegrationEntity' that appears to be an enum. - public IntegrationEntity = IntegrationEntity; - - // Define a public property 'loading' of type boolean to track loading state. - public loading: boolean; - - // Define a public property 'entitiesToSync$' of type Observable. - // It's initialized with a property from '_integrationsService', possibly an observable. - public entitiesToSync$: Observable = this._integrationEntitySettingServiceStoreService.entitiesToSync$; - - // Define a private property to hold the integration tenant - private _integration: IIntegrationTenant; - // Define a getter to retrieve the integration tenant - get integration(): IIntegrationTenant { - return this._integration; - } - // Define an @Input setter to set the integration tenant from external sources - @Input() set integration(value: IIntegrationTenant) { - // Set the private integration tenant property when the value is provided - this._integration = value; - } - - constructor( - public readonly dialogRef: NbDialogRef, - private readonly _cdRef: ChangeDetectorRef, - private readonly _integrationEntitySettingService: IntegrationEntitySettingService, - private readonly _integrationEntitySettingServiceStoreService: IntegrationEntitySettingServiceStoreService, - ) { } - - ngOnInit(): void { - this.getEntitySettings(); - } - - ngAfterViewInit(): void { - // Trigger change detection to update the view - this._cdRef.detectChanges(); - } - - /** - * Fetch entity settings for a given integration. - */ - getEntitySettings() { - // Check if the 'integration' object is falsy and return early if it is - if (!this.integration) { - return; - } - - // Set the 'loading' flag to true to indicate that data is being loaded - this.loading = true; - - // Extract the 'id' property from the 'integration' object - const { id: integrationId } = this.integration; - - // Fetch entity settings by integration ID and handle the result as an observable - this._integrationEntitySettingService.getEntitySettings(integrationId).pipe( - // Map the result to the desired format using '_setSettingsValue' function - map(({ items }) => this._integrationEntitySettingServiceStoreService.setEntitySettingsValue(items)), - - // Execute the following code block when the observable completes or errors - finalize(() => { - // Set the 'loading' flag to false to indicate that data loading is complete - this.loading = false; - }), - - // Automatically unsubscribe when the component is destroyed - untilDestroyed(this) - ).subscribe(); - } -} diff --git a/apps/gauzy/src/app/pages/integrations/github/components/settings/settings.component.html b/apps/gauzy/src/app/pages/integrations/github/components/settings/settings.component.html new file mode 100644 index 00000000000..eb3962c0893 --- /dev/null +++ b/apps/gauzy/src/app/pages/integrations/github/components/settings/settings.component.html @@ -0,0 +1,57 @@ + +
+
+ + + +
+
+ + {{ 'INTEGRATIONS.SETTINGS' | translate }} + +
+
+
+ + + {{ entity.entity }} + +
+ +
+ + {{ tiedEntity.entity }} + +
+
+
+
+
+
+
+ +
+
+
diff --git a/apps/gauzy/src/app/pages/integrations/github/components/settings/settings.component.scss b/apps/gauzy/src/app/pages/integrations/github/components/settings/settings.component.scss new file mode 100644 index 00000000000..5c7056275fa --- /dev/null +++ b/apps/gauzy/src/app/pages/integrations/github/components/settings/settings.component.scss @@ -0,0 +1,42 @@ +@import 'gauzy/_gauzy-overrides'; + +:host { + .popover-container { + display: flex; + flex-direction: column; + justify-items: flex-start; + align-items: flex-start; + padding: 12.5px 14px 12.5px 18px; + border-radius: $default-radius; + width: 250px; + .cursor { + cursor: pointer; + } + .title { + color: nb-theme(text-primary-color); + font-size: 16px; + font-weight: 600; + line-height: 16px; + letter-spacing: 0em; + } + .switcher-wrapper { + display: flex; + flex-direction: column; + nb-toggle { + display: flex; + ::ng-deep .toggle-label { + display: flex; + justify-content: space-between; + flex-grow: 1; + } + } + .tied-entities-wrapper { + .tied-entity { + display: flex; + align-items: center; + justify-content: space-between; + } + } + } + } +} diff --git a/apps/gauzy/src/app/pages/integrations/github/components/settings-dialog/settings-dialog.component.spec.ts b/apps/gauzy/src/app/pages/integrations/github/components/settings/settings.component.spec.ts similarity index 52% rename from apps/gauzy/src/app/pages/integrations/github/components/settings-dialog/settings-dialog.component.spec.ts rename to apps/gauzy/src/app/pages/integrations/github/components/settings/settings.component.spec.ts index 0c291b402be..87af93618bb 100644 --- a/apps/gauzy/src/app/pages/integrations/github/components/settings-dialog/settings-dialog.component.spec.ts +++ b/apps/gauzy/src/app/pages/integrations/github/components/settings/settings.component.spec.ts @@ -1,20 +1,19 @@ import { waitForAsync, ComponentFixture, TestBed } from '@angular/core/testing'; +import { GithubSettingsComponent } from './settings.component'; -import { GithubSettingsDialogComponent } from './settings-dialog.component'; - -describe('GithubSettingsDialogComponent', () => { - let component: GithubSettingsDialogComponent; - let fixture: ComponentFixture; +describe('GithubSettingsComponent', () => { + let component: GithubSettingsComponent; + let fixture: ComponentFixture; beforeEach(waitForAsync(() => { TestBed.configureTestingModule({ - declarations: [GithubSettingsDialogComponent], + declarations: [GithubSettingsComponent], teardown: { destroyAfterEach: false } }).compileComponents(); })); beforeEach(() => { - fixture = TestBed.createComponent(GithubSettingsDialogComponent); + fixture = TestBed.createComponent(GithubSettingsComponent); component = fixture.componentInstance; fixture.detectChanges(); }); diff --git a/apps/gauzy/src/app/pages/integrations/github/components/settings/settings.component.ts b/apps/gauzy/src/app/pages/integrations/github/components/settings/settings.component.ts new file mode 100644 index 00000000000..0af2781bc12 --- /dev/null +++ b/apps/gauzy/src/app/pages/integrations/github/components/settings/settings.component.ts @@ -0,0 +1,175 @@ +import { + AfterViewInit, + ChangeDetectorRef, + Component, + EventEmitter, + Input, + OnInit, + Output +} from '@angular/core'; +import { EMPTY, Observable, catchError } from 'rxjs'; +import { finalize, map, tap } from 'rxjs/operators'; +import { UntilDestroy, untilDestroyed } from '@ngneat/until-destroy'; +import { TranslateService } from '@ngx-translate/core'; +import { + HttpStatus, + IEntitySettingToSync, + IIntegrationTenant, + IntegrationEntity, + IntegrationEnum +} from '@gauzy/contracts'; +import { + ErrorHandlingService, + IntegrationEntitySettingService, + IntegrationEntitySettingServiceStoreService, + ToastrService +} from '../../../../../@core/services'; +import { TranslationBaseComponent } from '../../../../../@shared/language-base/translation-base.component'; + +@UntilDestroy() +@Component({ + selector: 'ngx-github-settings', + templateUrl: './settings.component.html', + styleUrls: ['./settings.component.scss'] +}) +export class GithubSettingsComponent extends TranslationBaseComponent implements OnInit, AfterViewInit { + + // Define a public property 'IntegrationEntity' that appears to be an enum. + public IntegrationEntity = IntegrationEntity; + + // Define a public property 'loading' of type boolean to track loading state. + public loading: boolean; + + // Define a public property 'entitiesToSync$' of type Observable. + // It's initialized with a property from '_integrationsService', possibly an observable. + public entitiesToSync$: Observable = this._integrationEntitySettingServiceStoreService.entitiesToSync$; + + // Define a private property to hold the integration tenant + private _integration: IIntegrationTenant; + // Define a getter to retrieve the integration tenant + get integration(): IIntegrationTenant { + return this._integration; + } + // Define an @Input setter to set the integration tenant from external sources + @Input() set integration(value: IIntegrationTenant) { + // Set the private integration tenant property when the value is provided + this._integration = value; + } + + /** + * + */ + @Output() canceled = new EventEmitter(); + + constructor( + public readonly _translateService: TranslateService, + private readonly _cdRef: ChangeDetectorRef, + private readonly _toastrService: ToastrService, + private readonly _errorHandlingService: ErrorHandlingService, + private readonly _integrationEntitySettingService: IntegrationEntitySettingService, + private readonly _integrationEntitySettingServiceStoreService: IntegrationEntitySettingServiceStoreService, + ) { + super(_translateService); + } + + ngOnInit(): void { + this.getEntitySettings(); + } + + ngAfterViewInit(): void { + // Trigger change detection to update the view + this._cdRef.detectChanges(); + } + + /** + * Fetch entity settings for a given integration. + */ + getEntitySettings() { + // Check if the 'integration' object is falsy and return early if it is + if (!this.integration) { + return; + } + + // Set the 'loading' flag to true to indicate that data is being loaded + this.loading = true; + + // Extract the 'id' property from the 'integration' object + const { id: integrationId } = this.integration; + + // Fetch entity settings by integration ID and handle the result as an observable + this._integrationEntitySettingService.getEntitySettings(integrationId).pipe( + // Map the result to the desired format using '_setSettingsValue' function + map(({ items }) => this._integrationEntitySettingServiceStoreService.setEntitySettingsValue(items)), + + // Execute the following code block when the observable completes or errors + finalize(() => this.loading = false), + + // Automatically unsubscribe when the component is destroyed + untilDestroyed(this) + ).subscribe(); + } + + /** + * Saves the integration settings if the 'integration' object is defined. + * + * @returns + */ + saveIntegrationSettings() { + // Check if the 'integration' object is falsy and return early if it is + if (!this.integration) { + return; + } + + // Extract the 'id' property from the 'integration' object + const { id: integrationId } = this.integration; + + // Use try-catch for better error handling + try { + // Retrieve the current settings from the service + const { currentValue: settings }: IEntitySettingToSync = this._integrationEntitySettingServiceStoreService.getEntitySettingsValue(); + + // Set the 'loading' flag to true to indicate that data is being loaded + this.loading = true; + + // Update entity settings if needed + this._integrationEntitySettingService.updateEntitySettings(integrationId, settings).pipe( + tap((response: any) => { + if (response['status'] == HttpStatus.BAD_REQUEST) { + throw new Error(`${response['message']}`); + } + }), + tap(() => { + // Display a success message + this._toastrService.success( + this.getTranslation('INTEGRATIONS.MESSAGE.SETTINGS_UPDATED', { + provider: IntegrationEnum.GITHUB + }), + this.getTranslation('TOASTR.TITLE.SUCCESS') + ); + }), + catchError((error) => { + this._errorHandlingService.handleError(error); + return EMPTY; + }), + // Execute the following code block when the observable completes or errors + finalize(() => this.loading = false), + // Automatically unsubscribe when the component is destroyed + untilDestroyed(this) + ).subscribe() + // Optionally, you can provide feedback or handle success here + } catch (error) { + // Handle errors (e.g., display an error message or log the error) + console.error('Error updating entity settings:', error); + + // Optionally, you can provide error feedback to the user + this._errorHandlingService.handleError(error); + } + } + + /** + * + */ + cancel($event: MouseEvent) { + this.canceled.emit($event); + } +} diff --git a/apps/gauzy/src/app/pages/integrations/github/components/view/view.component.html b/apps/gauzy/src/app/pages/integrations/github/components/view/view.component.html index fae94e211b0..ec2c1931e92 100644 --- a/apps/gauzy/src/app/pages/integrations/github/components/view/view.component.html +++ b/apps/gauzy/src/app/pages/integrations/github/components/view/view.component.html @@ -32,7 +32,10 @@
@@ -200,3 +203,11 @@
+ + + + + diff --git a/apps/gauzy/src/app/pages/integrations/github/components/view/view.component.ts b/apps/gauzy/src/app/pages/integrations/github/components/view/view.component.ts index 0ba05e45058..bdcf7733b82 100644 --- a/apps/gauzy/src/app/pages/integrations/github/components/view/view.component.ts +++ b/apps/gauzy/src/app/pages/integrations/github/components/view/view.component.ts @@ -1,17 +1,16 @@ -import { AfterViewInit, Component, OnInit, ViewChild } from '@angular/core'; +import { AfterViewInit, Component, OnInit, QueryList, ViewChild, ViewChildren } from '@angular/core'; import { TitleCasePipe } from '@angular/common'; import { ActivatedRoute, Data, Router } from '@angular/router'; -import { BehaviorSubject, EMPTY, Subject, debounceTime, finalize, first, firstValueFrom, of } from 'rxjs'; +import { BehaviorSubject, EMPTY, Subject, debounceTime, finalize, of } from 'rxjs'; import { Observable } from 'rxjs/internal/Observable'; import { catchError, filter, map, mergeMap, switchMap, tap } from 'rxjs/operators'; import { TranslateService } from '@ngx-translate/core'; -import { NbDialogService, NbTabComponent } from '@nebular/theme'; +import { NbPopoverDirective, NbTabComponent } from '@nebular/theme'; import { UntilDestroy, untilDestroyed } from '@ngneat/until-destroy'; import { Ng2SmartTableComponent } from 'ng2-smart-table'; import { GithubRepositoryStatusEnum, HttpStatus, - IEntitySettingToSync, IGithubIssue, IGithubRepository, IIntegrationMapSyncRepository, @@ -20,7 +19,6 @@ import { IOrganizationGithubRepository, IOrganizationProject, IUser, - IntegrationEnum, SYNC_TAG_GAUZY, TaskStatusEnum } from '@gauzy/contracts'; @@ -28,8 +26,6 @@ import { distinctUntilChange } from '@gauzy/common-angular'; import { ErrorHandlingService, GithubService, - IntegrationEntitySettingService, - IntegrationEntitySettingServiceStoreService, OrganizationProjectsService, Store, ToastrService @@ -45,7 +41,6 @@ import { ToggleSwitchComponent, ResyncButtonComponent } from './../../../../../@shared/table-components'; -import { GithubSettingsDialogComponent } from '../settings-dialog/settings-dialog.component'; export enum SyncTabsEnum { AUTO_SYNC = 'AUTO_SYNC', @@ -93,19 +88,18 @@ export class GithubViewComponent extends PaginationFilterBaseComponent implement } } + @ViewChildren(NbPopoverDirective) public popups: QueryList; + constructor( private readonly _router: Router, public readonly _translateService: TranslateService, private readonly _activatedRoute: ActivatedRoute, private readonly _titlecasePipe: TitleCasePipe, private readonly _hashNumberPipe: HashNumberPipe, - private readonly _dialogService: NbDialogService, private readonly _toastrService: ToastrService, private readonly _errorHandlingService: ErrorHandlingService, private readonly _store: Store, private readonly _githubService: GithubService, - private readonly _integrationEntitySettingService: IntegrationEntitySettingService, - private readonly _integrationEntitySettingServiceStoreService: IntegrationEntitySettingServiceStoreService, private readonly _organizationProjectsService: OrganizationProjectsService ) { super(_translateService); @@ -487,61 +481,16 @@ export class GithubViewComponent extends PaginationFilterBaseComponent implement } /** - * Open a dialog to set GitHub integration settings. - * - * @returns + * Opens a modal popover for integration settings if the 'integration' object is defined. */ - private openDialog(): Observable { - // Open a dialog to configure GitHub settings - const dialogRef = this._dialogService.open(GithubSettingsDialogComponent, { - context: { - integration: this.integration // Pass the 'integration' object to the dialog component - } - }); - // Return an Observable that emits a boolean when the dialog is closed - return dialogRef.onClose.pipe(first()); - } - - /** - * Open a dialog to set GitHub integration settings. - */ - async openSettingModal() { + openSettingModalPopover() { // Check if the 'integration' object is falsy and return early if it is if (!this.integration) { return; } - // Wait for the dialog to close and retrieve the data returned from the dialog - const data = await firstValueFrom(this.openDialog()); - if (data) { - // Extract the 'id' property from the 'integration' object - const { id: integrationId } = this.integration; - - // Use try-catch for better error handling - try { - // Retrieve the current settings from the service - const { currentValue: settings }: IEntitySettingToSync = this._integrationEntitySettingServiceStoreService.getEntitySettingsValue(); - - // Update entity settings if needed - await firstValueFrom( - this._integrationEntitySettingService.updateEntitySettings( - integrationId, - settings - ) - ); - - this._toastrService.success( - this.getTranslation('INTEGRATIONS.MESSAGE.SETTINGS_UPDATED', { provider: IntegrationEnum.GITHUB }), - this.getTranslation('TOASTR.TITLE.SUCCESS') - ); - // Optionally, you can provide feedback or handle success here - } catch (error) { - // Handle errors (e.g., display an error message or log the error) - console.error('Error updating entity settings:', error); - // Optionally, you can provide error feedback to the user - this._errorHandlingService.handleError(error); - } - } + // Open the modal popover (assuming `popups` is an array or collection of popovers) + this.popups.first.toggle(); } /** diff --git a/apps/gauzy/src/app/pages/integrations/github/github.module.ts b/apps/gauzy/src/app/pages/integrations/github/github.module.ts index 8f1b60017e9..49363f7024a 100644 --- a/apps/gauzy/src/app/pages/integrations/github/github.module.ts +++ b/apps/gauzy/src/app/pages/integrations/github/github.module.ts @@ -5,6 +5,7 @@ import { NbCardModule, NbDialogModule, NbIconModule, + NbPopoverModule, NbSpinnerModule, NbTabsetModule, NbToggleModule @@ -18,7 +19,7 @@ import { GithubComponent } from './github.component'; import { GithubWizardComponent } from './components/wizard/wizard.component'; import { GithubInstallationComponent } from './components/installation/installation.component'; import { GithubViewComponent } from './components/view/view.component'; -import { GithubSettingsDialogComponent } from './components/settings-dialog/settings-dialog.component'; +import { GithubSettingsComponent } from './components/settings/settings.component import { RepositorySelectorModule } from '../../../@shared/integrations/github'; import { DirectivesModule } from '../../../@shared/directives/directives.module'; import { ProjectSelectModule } from '../../../@shared/project-select/project-select.module'; @@ -30,7 +31,7 @@ import { PaginationModule } from '../../../@shared/pagination/pagination.module' GithubWizardComponent, GithubInstallationComponent, GithubViewComponent, - GithubSettingsDialogComponent + GithubSettingsComponent ], imports: [ CommonModule, @@ -38,6 +39,7 @@ import { PaginationModule } from '../../../@shared/pagination/pagination.module' NbCardModule, NbDialogModule, NbIconModule, + NbPopoverModule, NbSpinnerModule, NbTabsetModule, NbToggleModule, From 607bc8d4241d3b88dedcbdec3b58ee682ab2398d Mon Sep 17 00:00:00 2001 From: RAHUL RATHORE <41804588+rahul-rocket@users.noreply.github.com> Date: Tue, 7 Nov 2023 13:15:01 +0530 Subject: [PATCH 13/15] fix: #7000 missing import path --- .../github/components/settings/settings.component.ts | 4 ++-- apps/gauzy/src/app/pages/integrations/github/github.module.ts | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/gauzy/src/app/pages/integrations/github/components/settings/settings.component.ts b/apps/gauzy/src/app/pages/integrations/github/components/settings/settings.component.ts index 0af2781bc12..3ed4b1c545c 100644 --- a/apps/gauzy/src/app/pages/integrations/github/components/settings/settings.component.ts +++ b/apps/gauzy/src/app/pages/integrations/github/components/settings/settings.component.ts @@ -23,8 +23,8 @@ import { IntegrationEntitySettingService, IntegrationEntitySettingServiceStoreService, ToastrService -} from '../../../../../@core/services'; -import { TranslationBaseComponent } from '../../../../../@shared/language-base/translation-base.component'; +} from './../../../../../@core/services'; +import { TranslationBaseComponent } from './../../../../../@shared/language-base/translation-base.component'; @UntilDestroy() @Component({ diff --git a/apps/gauzy/src/app/pages/integrations/github/github.module.ts b/apps/gauzy/src/app/pages/integrations/github/github.module.ts index 49363f7024a..01fb2b3deae 100644 --- a/apps/gauzy/src/app/pages/integrations/github/github.module.ts +++ b/apps/gauzy/src/app/pages/integrations/github/github.module.ts @@ -19,7 +19,7 @@ import { GithubComponent } from './github.component'; import { GithubWizardComponent } from './components/wizard/wizard.component'; import { GithubInstallationComponent } from './components/installation/installation.component'; import { GithubViewComponent } from './components/view/view.component'; -import { GithubSettingsComponent } from './components/settings/settings.component +import { GithubSettingsComponent } from './components/settings/settings.component'; import { RepositorySelectorModule } from '../../../@shared/integrations/github'; import { DirectivesModule } from '../../../@shared/directives/directives.module'; import { ProjectSelectModule } from '../../../@shared/project-select/project-select.module'; From 1e7eb01315fc455c040bc5e75b1c2776df216d95 Mon Sep 17 00:00:00 2001 From: RAHUL RATHORE <41804588+rahul-rocket@users.noreply.github.com> Date: Tue, 7 Nov 2023 13:19:57 +0530 Subject: [PATCH 14/15] fix: rename selector --- .../integrations/github/components/view/view.component.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/gauzy/src/app/pages/integrations/github/components/view/view.component.html b/apps/gauzy/src/app/pages/integrations/github/components/view/view.component.html index ec2c1931e92..6e6d60f579d 100644 --- a/apps/gauzy/src/app/pages/integrations/github/components/view/view.component.html +++ b/apps/gauzy/src/app/pages/integrations/github/components/view/view.component.html @@ -206,8 +206,8 @@
- + > From 5cea9db6296810a445aeef42d250ff8531148e3b Mon Sep 17 00:00:00 2001 From: RAHUL RATHORE <41804588+rahul-rocket@users.noreply.github.com> Date: Tue, 7 Nov 2023 13:21:55 +0530 Subject: [PATCH 15/15] fix: removed top margin --- .../github/components/settings/settings.component.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/gauzy/src/app/pages/integrations/github/components/settings/settings.component.html b/apps/gauzy/src/app/pages/integrations/github/components/settings/settings.component.html index eb3962c0893..73a5fcd2b13 100644 --- a/apps/gauzy/src/app/pages/integrations/github/components/settings/settings.component.html +++ b/apps/gauzy/src/app/pages/integrations/github/components/settings/settings.component.html @@ -9,7 +9,7 @@
-
+
{{ 'INTEGRATIONS.SETTINGS' | translate }}