From 4ad4f526940c8012136212da4765b5f7a0641917 Mon Sep 17 00:00:00 2001 From: Roman Nastyuk Date: Wed, 20 Aug 2025 17:15:33 +0300 Subject: [PATCH 1/4] feat(add-project-modal-search): added reusable project selector component --- src/app/app.routes.ts | 3 +- .../project-metadata-step.component.html | 2 +- .../create-project-dialog.component.html | 2 +- .../create-project-dialog.component.ts | 21 +-- .../supplements-step.component.html | 2 +- .../add-project-form.component.html | 13 +- .../add-project-form.component.ts | 15 +- src/app/shared/components/index.ts | 1 + .../project-selector.component.html | 20 +++ .../project-selector.component.scss | 0 .../project-selector.component.spec.ts | 62 +++++++++ .../project-selector.component.ts | 129 ++++++++++++++++++ 12 files changed, 236 insertions(+), 34 deletions(-) create mode 100644 src/app/shared/components/project-selector/project-selector.component.html create mode 100644 src/app/shared/components/project-selector/project-selector.component.scss create mode 100644 src/app/shared/components/project-selector/project-selector.component.spec.ts create mode 100644 src/app/shared/components/project-selector/project-selector.component.ts diff --git a/src/app/app.routes.ts b/src/app/app.routes.ts index 35d77fc43..f20059438 100644 --- a/src/app/app.routes.ts +++ b/src/app/app.routes.ts @@ -35,6 +35,7 @@ export const routes: Routes = [ import('./features/home/pages/dashboard/dashboard.component').then((mod) => mod.DashboardComponent), data: { skipBreadcrumbs: true }, canActivate: [authGuard], + providers: [provideStates([ProjectsState])], }, { path: 'confirm/:userId/:token', @@ -73,7 +74,7 @@ export const routes: Routes = [ path: 'my-projects', loadComponent: () => import('./features/my-projects/my-projects.component').then((mod) => mod.MyProjectsComponent), - providers: [provideStates([BookmarksState])], + providers: [provideStates([BookmarksState, ProjectsState])], canActivate: [authGuard], }, { diff --git a/src/app/features/collections/components/add-to-collection/project-metadata-step/project-metadata-step.component.html b/src/app/features/collections/components/add-to-collection/project-metadata-step/project-metadata-step.component.html index 6cb72386c..06d41a51d 100644 --- a/src/app/features/collections/components/add-to-collection/project-metadata-step/project-metadata-step.component.html +++ b/src/app/features/collections/components/add-to-collection/project-metadata-step/project-metadata-step.component.html @@ -7,7 +7,7 @@
-

{{ 'collections.addToCollection.projectMetadata' | translate }}

+

{{ 'collections.addToCollection.resourceMetadata' | translate }}

@if (!isDisabled() && stepperActiveValue() !== targetStepValue()) {

{{ 'collections.addToCollection.form.title' | translate }}

diff --git a/src/app/features/my-projects/components/create-project-dialog/create-project-dialog.component.html b/src/app/features/my-projects/components/create-project-dialog/create-project-dialog.component.html index 2901c5dd2..ad13edece 100644 --- a/src/app/features/my-projects/components/create-project-dialog/create-project-dialog.component.html +++ b/src/app/features/my-projects/components/create-project-dialog/create-project-dialog.component.html @@ -1,4 +1,4 @@ - +
{ - return this.projects().map( - (project) => - ({ - id: project.id, - name: project.title, - }) as IdName - ); - }); readonly isProjectSubmitting = select(MyResourcesSelectors.isProjectSubmitting); readonly projectForm = new FormGroup({ @@ -63,10 +52,6 @@ export class CreateProjectDialogComponent implements OnInit { }), }); - ngOnInit(): void { - this.actions.getMyProjects(1, MY_PROJECTS_TABLE_PARAMS.rows, {}); - } - submitForm(): void { if (this.projectForm.invalid) { this.projectForm.markAllAsTouched(); diff --git a/src/app/features/preprints/components/stepper/supplements-step/supplements-step.component.html b/src/app/features/preprints/components/stepper/supplements-step/supplements-step.component.html index dab3ce395..e63920f5d 100644 --- a/src/app/features/preprints/components/stepper/supplements-step/supplements-step.component.html +++ b/src/app/features/preprints/components/stepper/supplements-step/supplements-step.component.html @@ -58,7 +58,7 @@

{{ 'preprints.preprintStepper.supplements.title' | translate }}

} @else if (selectedSupplementOption() === SupplementOptions.CreateNewProject) { - + } } @else { diff --git a/src/app/shared/components/add-project-form/add-project-form.component.html b/src/app/shared/components/add-project-form/add-project-form.component.html index 055b2b8ac..45df6704c 100644 --- a/src/app/shared/components/add-project-form/add-project-form.component.html +++ b/src/app/shared/components/add-project-form/add-project-form.component.html @@ -69,17 +69,12 @@

-

diff --git a/src/app/shared/components/add-project-form/add-project-form.component.ts b/src/app/shared/components/add-project-form/add-project-form.component.ts index 073593ead..667e7e4b3 100644 --- a/src/app/shared/components/add-project-form/add-project-form.component.ts +++ b/src/app/shared/components/add-project-form/add-project-form.component.ts @@ -13,7 +13,9 @@ import { ChangeDetectionStrategy, Component, input, OnInit, signal } from '@angu import { FormGroup, ReactiveFormsModule } from '@angular/forms'; import { ProjectFormControls } from '@osf/shared/enums'; -import { IdName, ProjectForm } from '@osf/shared/models'; +import { ProjectForm } from '@osf/shared/models'; +import { Project } from '@osf/shared/models/projects'; +import { ProjectSelectorComponent } from '@shared/components/project-selector/project-selector.component'; import { FetchUserInstitutions, InstitutionsSelectors } from '@shared/stores'; import { FetchRegions, RegionsSelectors } from '@shared/stores/regions'; @@ -29,6 +31,7 @@ import { FetchRegions, RegionsSelectors } from '@shared/stores/regions'; Textarea, NgOptimizedImage, TranslatePipe, + ProjectSelectorComponent, ], templateUrl: './add-project-form.component.html', styleUrl: './add-project-form.component.scss', @@ -40,11 +43,10 @@ export class AddProjectFormComponent implements OnInit { fetchRegions: FetchRegions, }); - templates = input.required(); - ProjectFormControls = ProjectFormControls; hasTemplateSelected = signal(false); + selectedTemplate = signal(null); isSubmitting = signal(false); storageLocations = select(RegionsSelectors.getRegions); areStorageLocationsLoading = select(RegionsSelectors.areRegionsLoading); @@ -65,6 +67,13 @@ export class AddProjectFormComponent implements OnInit { }); } + onTemplateChange(project: Project | null): void { + if (!project) return; + this.selectedTemplate.set(project); + this.projectForm().get(ProjectFormControls.Template)?.setValue(project.id); + this.hasTemplateSelected.set(!!project); + } + selectAllAffiliations(): void { const allAffiliationValues = this.affiliations().map((aff) => aff.id); this.projectForm().get(ProjectFormControls.Affiliations)?.setValue(allAffiliationValues); diff --git a/src/app/shared/components/index.ts b/src/app/shared/components/index.ts index e5eabe591..2861d061d 100644 --- a/src/app/shared/components/index.ts +++ b/src/app/shared/components/index.ts @@ -25,6 +25,7 @@ export { MarkdownComponent } from './markdown/markdown.component'; export { MyProjectsTableComponent } from './my-projects-table/my-projects-table.component'; export { PasswordInputHintComponent } from './password-input-hint/password-input-hint.component'; export { PieChartComponent } from './pie-chart/pie-chart.component'; +export { ProjectSelectorComponent } from './project-selector/project-selector.component'; export { ReadonlyInputComponent } from './readonly-input/readonly-input.component'; export { RegistrationBlocksDataComponent } from './registration-blocks-data/registration-blocks-data.component'; export { ResourceCardComponent } from './resource-card/resource-card.component'; diff --git a/src/app/shared/components/project-selector/project-selector.component.html b/src/app/shared/components/project-selector/project-selector.component.html new file mode 100644 index 000000000..d38c2b6a5 --- /dev/null +++ b/src/app/shared/components/project-selector/project-selector.component.html @@ -0,0 +1,20 @@ + + + {{ selectedOption.label }} + + diff --git a/src/app/shared/components/project-selector/project-selector.component.scss b/src/app/shared/components/project-selector/project-selector.component.scss new file mode 100644 index 000000000..e69de29bb diff --git a/src/app/shared/components/project-selector/project-selector.component.spec.ts b/src/app/shared/components/project-selector/project-selector.component.spec.ts new file mode 100644 index 000000000..83efb2529 --- /dev/null +++ b/src/app/shared/components/project-selector/project-selector.component.spec.ts @@ -0,0 +1,62 @@ +import { provideStore } from '@ngxs/store'; + +import { TranslatePipe } from '@ngx-translate/core'; +import { MockPipe, MockProvider } from 'ng-mocks'; + +import { provideHttpClient } from '@angular/common/http'; +import { provideHttpClientTesting } from '@angular/common/http/testing'; +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { UserState } from '@core/store/user'; +import { TranslateServiceMock } from '@shared/mocks'; +import { ToastService } from '@shared/services'; +import { ProjectsState } from '@shared/stores'; + +import { ProjectSelectorComponent } from './project-selector.component'; + +describe('ProjectSelectorComponent', () => { + let component: ProjectSelectorComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [ProjectSelectorComponent, MockPipe(TranslatePipe)], + providers: [ + TranslateServiceMock, + MockProvider(ToastService), + provideStore([ProjectsState, UserState]), + provideHttpClient(), + provideHttpClientTesting(), + ], + }).compileComponents(); + + fixture = TestBed.createComponent(ProjectSelectorComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); + + it('should handle project selection', () => { + spyOn(component.projectChange, 'emit'); + const mockProject = { id: '1', title: 'Test Project' } as any; + const mockEvent = { value: mockProject }; + + component.handleProjectChange(mockEvent as any); + + expect(component.projectChange.emit).toHaveBeenCalledWith(mockProject); + }); + + it('should handle filter search', () => { + const mockEvent = { + originalEvent: { preventDefault: jasmine.createSpy() }, + filter: 'test filter', + }; + + component.handleFilterSearch(mockEvent as any); + + expect(mockEvent.originalEvent.preventDefault).toHaveBeenCalled(); + }); +}); diff --git a/src/app/shared/components/project-selector/project-selector.component.ts b/src/app/shared/components/project-selector/project-selector.component.ts new file mode 100644 index 000000000..4f70a5854 --- /dev/null +++ b/src/app/shared/components/project-selector/project-selector.component.ts @@ -0,0 +1,129 @@ +import { createDispatchMap, select } from '@ngxs/store'; + +import { TranslatePipe, TranslateService } from '@ngx-translate/core'; + +import { Select, SelectChangeEvent, SelectFilterEvent } from 'primeng/select'; + +import { debounceTime, distinctUntilChanged, Subject, takeUntil } from 'rxjs'; + +import { + ChangeDetectionStrategy, + Component, + computed, + DestroyRef, + effect, + inject, + input, + model, + output, + signal, +} from '@angular/core'; +import { FormsModule } from '@angular/forms'; + +import { UserSelectors } from '@core/store/user'; +import { CustomOption } from '@shared/models'; +import { Project } from '@shared/models/projects'; +import { GetProjects } from '@shared/stores'; +import { ProjectsSelectors } from '@shared/stores/projects/projects.selectors'; + +@Component({ + selector: 'osf-project-selector', + imports: [Select, TranslatePipe, FormsModule], + templateUrl: './project-selector.component.html', + styleUrl: './project-selector.component.scss', + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class ProjectSelectorComponent { + private readonly destroyRef = inject(DestroyRef); + private readonly translateService = inject(TranslateService); + private readonly destroy$ = new Subject(); + private readonly filterSubject = new Subject(); + + protected projects = select(ProjectsSelectors.getProjects); + protected isProjectsLoading = select(ProjectsSelectors.getProjectsLoading); + protected currentUser = select(UserSelectors.getCurrentUser); + + placeholder = input('common.buttons.select'); + showClear = input(true); + selectedProject = model(null); + + projectChange = output(); + + protected projectsOptions = signal[]>([]); + + protected filterMessage = computed(() => { + const isLoading = this.isProjectsLoading(); + return isLoading + ? this.translateService.instant('collections.addToCollection.form.loadingPlaceholder') + : this.translateService.instant('collections.addToCollection.form.noProjectsFound'); + }); + + protected actions = createDispatchMap({ + getProjects: GetProjects, + }); + + constructor() { + this.setupEffects(); + this.setupFilterDebounce(); + } + + handleProjectChange(event: SelectChangeEvent): void { + const project = event.value; + this.selectedProject.set(project); + this.projectChange.emit(project); + } + + handleFilterSearch(event: SelectFilterEvent): void { + event.originalEvent.preventDefault(); + this.filterSubject.next(event.filter); + } + + private setupEffects(): void { + effect(() => { + const currentUser = this.currentUser(); + if (currentUser) { + this.actions.getProjects(currentUser.id); + } + }); + + effect(() => { + const isProjectsLoading = this.isProjectsLoading(); + const projects = this.projects(); + + if (isProjectsLoading || !projects.length) { + this.projectsOptions.set([]); + return; + } + + const options = projects.map((project) => ({ + label: project.title, + value: project, + })); + + this.projectsOptions.set(options); + }); + + effect(() => { + this.destroyRef.onDestroy(() => { + this.destroy$.next(); + this.destroy$.complete(); + }); + }); + } + + private setupFilterDebounce(): void { + this.filterSubject + .pipe(debounceTime(300), distinctUntilChanged(), takeUntil(this.destroy$)) + .subscribe((filterValue) => { + const currentUser = this.currentUser(); + if (!currentUser) return; + + const params: Record = { + 'filter[current_user_permissions]': 'admin', + 'filter[title]': filterValue, + }; + + this.actions.getProjects(currentUser.id, params); + }); + } +} From 5a7390b4b600e5150f5c7a99b80802e0a07c2339 Mon Sep 17 00:00:00 2001 From: Roman Nastyuk Date: Wed, 20 Aug 2025 18:16:28 +0300 Subject: [PATCH 2/4] feat(add-project-modal-search): used project selector in the select project step component --- .../select-project-step.component.html | 25 +--- .../select-project-step.component.ts | 130 +++--------------- .../project-selector.component.ts | 10 +- 3 files changed, 33 insertions(+), 132 deletions(-) diff --git a/src/app/features/collections/components/add-to-collection/select-project-step/select-project-step.component.html b/src/app/features/collections/components/add-to-collection/select-project-step/select-project-step.component.html index 3f163dddc..2496b3829 100644 --- a/src/app/features/collections/components/add-to-collection/select-project-step/select-project-step.component.html +++ b/src/app/features/collections/components/add-to-collection/select-project-step/select-project-step.component.html @@ -22,25 +22,12 @@

{{ 'collections.addToCollection.selectProject' | translate }}

- - - {{ selectedOption.label | translate }} - - +
diff --git a/src/app/features/collections/components/add-to-collection/select-project-step/select-project-step.component.ts b/src/app/features/collections/components/add-to-collection/select-project-step/select-project-step.component.ts index 8cb47647b..6e7e7f384 100644 --- a/src/app/features/collections/components/add-to-collection/select-project-step/select-project-step.component.ts +++ b/src/app/features/collections/components/add-to-collection/select-project-step/select-project-step.component.ts @@ -1,53 +1,29 @@ import { createDispatchMap, select } from '@ngxs/store'; -import { TranslatePipe, TranslateService } from '@ngx-translate/core'; +import { TranslatePipe } from '@ngx-translate/core'; import { Button } from 'primeng/button'; -import { Select, SelectChangeEvent, SelectFilterEvent } from 'primeng/select'; import { Step, StepItem, StepPanel } from 'primeng/stepper'; -import { debounceTime, distinctUntilChanged, Subject, takeUntil } from 'rxjs'; +import { ChangeDetectionStrategy, Component, computed, input, output, signal } from '@angular/core'; -import { - ChangeDetectionStrategy, - Component, - computed, - DestroyRef, - effect, - inject, - input, - output, - signal, -} from '@angular/core'; -import { FormsModule } from '@angular/forms'; - -import { UserSelectors } from '@core/store/user'; import { AddToCollectionSteps } from '@osf/features/collections/enums'; -import { GetProjects, SetSelectedProject } from '@osf/shared/stores'; -import { CustomOption } from '@shared/models'; +import { SetSelectedProject } from '@osf/shared/stores'; +import { ProjectSelectorComponent } from '@shared/components'; import { Project } from '@shared/models/projects'; import { CollectionsSelectors, GetUserCollectionSubmissions } from '@shared/stores/collections'; import { ProjectsSelectors } from '@shared/stores/projects/projects.selectors'; @Component({ selector: 'osf-select-project-step', - imports: [Button, TranslatePipe, Select, FormsModule, Step, StepItem, StepPanel], + imports: [Button, TranslatePipe, ProjectSelectorComponent, Step, StepItem, StepPanel], templateUrl: './select-project-step.component.html', styleUrl: './select-project-step.component.scss', changeDetection: ChangeDetectionStrategy.OnPush, }) export class SelectProjectStepComponent { - private readonly destroyRef = inject(DestroyRef); - private readonly translateService = inject(TranslateService); - private readonly destroy$ = new Subject(); - private readonly filterSubject = new Subject(); - - protected projects = select(ProjectsSelectors.getProjects); - protected isProjectsLoading = select(ProjectsSelectors.getProjectsLoading); protected selectedProject = select(ProjectsSelectors.getSelectedProject); - protected currentUser = select(UserSelectors.getCurrentUser); protected currentUserSubmissions = select(CollectionsSelectors.getUserCollectionSubmissions); - protected isSubmissionsLoading = select(CollectionsSelectors.getUserCollectionSubmissionsLoading); stepperActiveValue = input.required(); targetStepValue = input.required(); @@ -56,107 +32,37 @@ export class SelectProjectStepComponent { stepChange = output(); projectSelected = output(); - protected projectsOptions = signal[]>([]); + currentSelectedProject = signal(null); - protected filterMessage = computed(() => { - const isLoading = this.isProjectsLoading() || this.isSubmissionsLoading(); - return isLoading - ? this.translateService.instant('collections.addToCollection.form.loadingPlaceholder') - : this.translateService.instant('collections.addToCollection.form.noProjectsFound'); + // Calculate excluded project IDs (already submitted to this collection) + protected excludedProjectIds = computed(() => { + const submissions = this.currentUserSubmissions(); + return submissions.map((submission) => submission.nodeId); }); protected actions = createDispatchMap({ - getProjects: GetProjects, setSelectedProject: SetSelectedProject, getUserCollectionSubmissions: GetUserCollectionSubmissions, }); - constructor() { - this.setupEffects(); - this.setupFilterDebounce(); - } - - handleProjectChange(event: SelectChangeEvent) { - const project = event.value; + handleProjectChange(project: Project | null): void { if (project) { + this.currentSelectedProject.set(project); this.actions.setSelectedProject(project); this.projectSelected.emit(); this.stepChange.emit(AddToCollectionSteps.ProjectMetadata); } } - handleFilterSearch(event: SelectFilterEvent) { - event.originalEvent.preventDefault(); - this.filterSubject.next(event.filter); + handleProjectsLoaded(projects: Project[]): void { + const collectionId = this.collectionId(); + if (collectionId && projects.length) { + const projectIds = projects.map((project) => project.id); + this.actions.getUserCollectionSubmissions(collectionId, projectIds); + } } handleEditStep() { this.stepChange.emit(this.targetStepValue()); } - - private setupEffects(): void { - effect(() => { - const currentUser = this.currentUser(); - if (currentUser) { - this.actions.getProjects(currentUser.id); - } - }); - - effect(() => { - const projects = this.projects(); - const collectionId = this.collectionId(); - const isProjectsLoading = this.isProjectsLoading(); - - if (projects.length && collectionId && !isProjectsLoading) { - const projectIds = projects.map((project) => project.id); - this.actions.getUserCollectionSubmissions(collectionId, projectIds); - } - }); - - effect(() => { - const isProjectsLoading = this.isProjectsLoading(); - const isSubmissionsLoading = this.isSubmissionsLoading(); - const projects = this.projects(); - const submissions = this.currentUserSubmissions(); - - if (isProjectsLoading || isSubmissionsLoading || !projects.length) { - this.projectsOptions.set([]); - } - - if (!isProjectsLoading && !isSubmissionsLoading && projects.length) { - const submissionProjectIds = new Set(submissions.map((submission) => submission.nodeId)); - const availableProjects = projects.filter((project) => !submissionProjectIds.has(project.id)); - - const options = availableProjects.map((project) => ({ - label: project.title, - value: project, - })); - - this.projectsOptions.set(options); - } - }); - - effect(() => { - this.destroyRef.onDestroy(() => { - this.destroy$.next(); - this.destroy$.complete(); - }); - }); - } - - private setupFilterDebounce(): void { - this.filterSubject - .pipe(debounceTime(300), distinctUntilChanged(), takeUntil(this.destroy$)) - .subscribe((filterValue) => { - const currentUser = this.currentUser(); - if (!currentUser) return; - - const params: Record = { - 'filter[current_user_permissions]': 'admin', - 'filter[title]': filterValue, - }; - - this.actions.getProjects(currentUser.id, params); - }); - } } diff --git a/src/app/shared/components/project-selector/project-selector.component.ts b/src/app/shared/components/project-selector/project-selector.component.ts index 4f70a5854..b4a080948 100644 --- a/src/app/shared/components/project-selector/project-selector.component.ts +++ b/src/app/shared/components/project-selector/project-selector.component.ts @@ -45,9 +45,11 @@ export class ProjectSelectorComponent { placeholder = input('common.buttons.select'); showClear = input(true); + excludeProjectIds = input([]); selectedProject = model(null); projectChange = output(); + projectsLoaded = output(); protected projectsOptions = signal[]>([]); @@ -89,13 +91,19 @@ export class ProjectSelectorComponent { effect(() => { const isProjectsLoading = this.isProjectsLoading(); const projects = this.projects(); + const excludeIds = this.excludeProjectIds(); if (isProjectsLoading || !projects.length) { this.projectsOptions.set([]); return; } - const options = projects.map((project) => ({ + this.projectsLoaded.emit(projects); + + const excludeSet = new Set(excludeIds); + const availableProjects = projects.filter((project) => !excludeSet.has(project.id)); + + const options = availableProjects.map((project) => ({ label: project.title, value: project, })); From b834ea4b1c8dd8249b10bde0ad9e396a1b577616 Mon Sep 17 00:00:00 2001 From: Roman Nastyuk Date: Wed, 20 Aug 2025 18:18:53 +0300 Subject: [PATCH 3/4] feat(add-project-modal-search): removed comment --- .../select-project-step/select-project-step.component.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/app/features/collections/components/add-to-collection/select-project-step/select-project-step.component.ts b/src/app/features/collections/components/add-to-collection/select-project-step/select-project-step.component.ts index 6e7e7f384..ac8e45a60 100644 --- a/src/app/features/collections/components/add-to-collection/select-project-step/select-project-step.component.ts +++ b/src/app/features/collections/components/add-to-collection/select-project-step/select-project-step.component.ts @@ -34,7 +34,6 @@ export class SelectProjectStepComponent { currentSelectedProject = signal(null); - // Calculate excluded project IDs (already submitted to this collection) protected excludedProjectIds = computed(() => { const submissions = this.currentUserSubmissions(); return submissions.map((submission) => submission.nodeId); From dfe27554f8ff48fdf2352d754c02d0e144e44c03 Mon Sep 17 00:00:00 2001 From: Roman Nastyuk Date: Thu, 21 Aug 2025 11:33:18 +0300 Subject: [PATCH 4/4] feat(add-project-modal-search): removed destroy subjet --- .../project-selector/project-selector.component.ts | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/src/app/shared/components/project-selector/project-selector.component.ts b/src/app/shared/components/project-selector/project-selector.component.ts index b4a080948..0dff92456 100644 --- a/src/app/shared/components/project-selector/project-selector.component.ts +++ b/src/app/shared/components/project-selector/project-selector.component.ts @@ -4,7 +4,7 @@ import { TranslatePipe, TranslateService } from '@ngx-translate/core'; import { Select, SelectChangeEvent, SelectFilterEvent } from 'primeng/select'; -import { debounceTime, distinctUntilChanged, Subject, takeUntil } from 'rxjs'; +import { debounceTime, distinctUntilChanged, Subject } from 'rxjs'; import { ChangeDetectionStrategy, @@ -18,6 +18,7 @@ import { output, signal, } from '@angular/core'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; import { FormsModule } from '@angular/forms'; import { UserSelectors } from '@core/store/user'; @@ -36,7 +37,6 @@ import { ProjectsSelectors } from '@shared/stores/projects/projects.selectors'; export class ProjectSelectorComponent { private readonly destroyRef = inject(DestroyRef); private readonly translateService = inject(TranslateService); - private readonly destroy$ = new Subject(); private readonly filterSubject = new Subject(); protected projects = select(ProjectsSelectors.getProjects); @@ -110,18 +110,11 @@ export class ProjectSelectorComponent { this.projectsOptions.set(options); }); - - effect(() => { - this.destroyRef.onDestroy(() => { - this.destroy$.next(); - this.destroy$.complete(); - }); - }); } private setupFilterDebounce(): void { this.filterSubject - .pipe(debounceTime(300), distinctUntilChanged(), takeUntil(this.destroy$)) + .pipe(debounceTime(300), distinctUntilChanged(), takeUntilDestroyed(this.destroyRef)) .subscribe((filterValue) => { const currentUser = this.currentUser(); if (!currentUser) return;