diff --git a/src/app/features/collections/components/collections-search-result-card/collections-search-result-card.component.ts b/src/app/features/collections/components/collections-search-result-card/collections-search-result-card.component.ts index 2f5dcb38d..7dfdc3c5a 100644 --- a/src/app/features/collections/components/collections-search-result-card/collections-search-result-card.component.ts +++ b/src/app/features/collections/components/collections-search-result-card/collections-search-result-card.component.ts @@ -4,7 +4,7 @@ import { DatePipe } from '@angular/common'; import { ChangeDetectionStrategy, Component, computed, input } from '@angular/core'; import { collectionFilterNames } from '@osf/features/collections/constants'; -import { CollectionSubmission } from '@shared/models'; +import { CollectionSubmissionWithGuid } from '@shared/models'; @Component({ selector: 'osf-collections-search-result-card', @@ -14,7 +14,7 @@ import { CollectionSubmission } from '@shared/models'; changeDetection: ChangeDetectionStrategy.OnPush, }) export class CollectionsSearchResultCardComponent { - cardItem = input.required(); + cardItem = input.required(); protected presentSubmissionAttributes = computed(() => { const item = this.cardItem(); @@ -23,7 +23,7 @@ export class CollectionsSearchResultCardComponent { return collectionFilterNames .map((attribute) => ({ ...attribute, - value: item[attribute.key as keyof CollectionSubmission] as string, + value: item[attribute.key as keyof CollectionSubmissionWithGuid] as string, })) .filter((attribute) => attribute.value); }); diff --git a/src/app/features/moderation/components/collection-moderation-submissions/collection-moderation-submissions.component.ts b/src/app/features/moderation/components/collection-moderation-submissions/collection-moderation-submissions.component.ts index b3b1bbef7..7b959bcc8 100644 --- a/src/app/features/moderation/components/collection-moderation-submissions/collection-moderation-submissions.component.ts +++ b/src/app/features/moderation/components/collection-moderation-submissions/collection-moderation-submissions.component.ts @@ -32,7 +32,7 @@ import { SetPageNumber, } from '@shared/stores'; -import { SUBMISSION_REVIEW_OPTIONS } from '../../constants'; +import { COLLECTIONS_SUBMISSIONS_REVIEW_OPTIONS } from '../../constants'; import { SubmissionReviewStatus } from '../../enums'; import { CollectionSubmissionsListComponent } from '../collection-submissions-list/collection-submissions-list.component'; @@ -55,7 +55,7 @@ import { CollectionSubmissionsListComponent } from '../collection-submissions-li export class CollectionModerationSubmissionsComponent { private router = inject(Router); private route = inject(ActivatedRoute); - readonly submissionReviewOptions = SUBMISSION_REVIEW_OPTIONS; + readonly submissionReviewOptions = COLLECTIONS_SUBMISSIONS_REVIEW_OPTIONS; protected collectionProvider = select(CollectionsSelectors.getCollectionProvider); protected isCollectionProviderLoading = select(CollectionsSelectors.getCollectionProviderLoading); diff --git a/src/app/features/moderation/components/collection-submission-item/collection-submission-item.component.ts b/src/app/features/moderation/components/collection-submission-item/collection-submission-item.component.ts index 7f392f299..5467686c7 100644 --- a/src/app/features/moderation/components/collection-submission-item/collection-submission-item.component.ts +++ b/src/app/features/moderation/components/collection-submission-item/collection-submission-item.component.ts @@ -12,7 +12,7 @@ import { collectionFilterNames } from '@osf/features/collections/constants'; import { SubmissionReviewStatus } from '@osf/features/moderation/enums'; import { IconComponent } from '@osf/shared/components'; import { DateAgoPipe } from '@osf/shared/pipes'; -import { CollectionSubmission } from '@shared/models'; +import { CollectionSubmissionWithGuid } from '@shared/models'; import { CollectionsSelectors } from '@shared/stores'; import { ReviewStatusIcon } from '../../constants'; @@ -28,7 +28,7 @@ import { ReviewStatusIcon } from '../../constants'; export class CollectionSubmissionItemComponent { private router = inject(Router); private activatedRoute = inject(ActivatedRoute); - submission = input.required(); + submission = input.required(); collectionProvider = select(CollectionsSelectors.getCollectionProvider); protected readonly reviewStatusIcon = ReviewStatusIcon; @@ -48,7 +48,7 @@ export class CollectionSubmissionItemComponent { return collectionFilterNames .map((attribute) => ({ ...attribute, - value: item[attribute.key as keyof CollectionSubmission] as string, + value: item[attribute.key as keyof CollectionSubmissionWithGuid] as string, })) .filter((attribute) => attribute.value); }); diff --git a/src/app/features/moderation/constants/submission.const.ts b/src/app/features/moderation/constants/submission.const.ts index 839fce93f..9ded27f00 100644 --- a/src/app/features/moderation/constants/submission.const.ts +++ b/src/app/features/moderation/constants/submission.const.ts @@ -28,6 +28,33 @@ export const SUBMISSION_REVIEW_OPTIONS: SubmissionReviewOption[] = [ }, ]; +export const COLLECTIONS_SUBMISSIONS_REVIEW_OPTIONS: SubmissionReviewOption[] = [ + { + value: SubmissionReviewStatus.Pending, + icon: 'fas fa-hourglass', + label: 'moderation.submissionReviewStatus.pending', + count: 0, + }, + { + value: SubmissionReviewStatus.Accepted, + icon: 'fas fa-circle-check', + label: 'moderation.submissionReviewStatus.accepted', + count: 0, + }, + { + value: SubmissionReviewStatus.Rejected, + icon: 'fas fa-circle-xmark', + label: 'moderation.submissionReviewStatus.rejected', + count: 0, + }, + { + value: SubmissionReviewStatus.Removed, + icon: 'fas fa-circle-minus', + label: 'moderation.submissionReviewStatus.withdrawn', + count: 0, + }, +]; + export const WITHDRAWAL_SUBMISSION_REVIEW_OPTIONS: SubmissionReviewOption[] = [ { value: SubmissionReviewStatus.Pending, diff --git a/src/app/features/moderation/store/collections-moderation/collections-moderation.model.ts b/src/app/features/moderation/store/collections-moderation/collections-moderation.model.ts index 6f07d7dc2..86a84879e 100644 --- a/src/app/features/moderation/store/collections-moderation/collections-moderation.model.ts +++ b/src/app/features/moderation/store/collections-moderation/collections-moderation.model.ts @@ -1,8 +1,8 @@ import { CollectionSubmissionReviewAction } from '@osf/features/moderation/models'; -import { AsyncStateModel, AsyncStateWithTotalCount, CollectionSubmission } from '@shared/models'; +import { AsyncStateModel, AsyncStateWithTotalCount, CollectionSubmissionWithGuid } from '@shared/models'; export interface CollectionsModerationStateModel { - collectionSubmissions: AsyncStateWithTotalCount; + collectionSubmissions: AsyncStateWithTotalCount; currentReviewAction: AsyncStateModel; } diff --git a/src/app/features/project/overview/components/overview-collections/overview-collections.component.html b/src/app/features/project/overview/components/overview-collections/overview-collections.component.html new file mode 100644 index 000000000..f71a27543 --- /dev/null +++ b/src/app/features/project/overview/components/overview-collections/overview-collections.component.html @@ -0,0 +1,63 @@ +@let project = currentProject(); +@let submissions = projectSubmissions(); + +@if (project) { +

{{ 'project.overview.metadata.collection' | translate }}

+ @if (isProjectSubmissionsLoading()) { + + } @else { +
+ @if (submissions.length) { + @for (submission of submissions; track submission.id) { + + } + } @else { +

{{ 'project.overview.metadata.noCollections' | translate }}

+ } +
+ } +} diff --git a/src/app/features/project/overview/components/overview-collections/overview-collections.component.scss b/src/app/features/project/overview/components/overview-collections/overview-collections.component.scss new file mode 100644 index 000000000..e69de29bb diff --git a/src/app/features/project/overview/components/overview-collections/overview-collections.component.spec.ts b/src/app/features/project/overview/components/overview-collections/overview-collections.component.spec.ts new file mode 100644 index 000000000..781147e68 --- /dev/null +++ b/src/app/features/project/overview/components/overview-collections/overview-collections.component.spec.ts @@ -0,0 +1,22 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { OverviewCollectionsComponent } from './overview-collections.component'; + +describe('OverviewCollectionsComponent', () => { + let component: OverviewCollectionsComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [OverviewCollectionsComponent], + }).compileComponents(); + + fixture = TestBed.createComponent(OverviewCollectionsComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/features/project/overview/components/overview-collections/overview-collections.component.ts b/src/app/features/project/overview/components/overview-collections/overview-collections.component.ts new file mode 100644 index 000000000..ef6ed9315 --- /dev/null +++ b/src/app/features/project/overview/components/overview-collections/overview-collections.component.ts @@ -0,0 +1,68 @@ +import { createDispatchMap, select } from '@ngxs/store'; + +import { TranslatePipe } from '@ngx-translate/core'; + +import { Accordion, AccordionContent, AccordionHeader, AccordionPanel } from 'primeng/accordion'; +import { Button } from 'primeng/button'; +import { Skeleton } from 'primeng/skeleton'; +import { Tag } from 'primeng/tag'; + +import { ChangeDetectionStrategy, Component, computed, effect, inject, input } from '@angular/core'; +import { Router } from '@angular/router'; + +import { collectionFilterNames } from '@osf/features/collections/constants'; +import { SubmissionReviewStatus } from '@osf/features/moderation/enums'; +import { CollectionSubmission, ResourceOverview } from '@shared/models'; +import { CollectionsSelectors, GetProjectSubmissions } from '@shared/stores'; + +@Component({ + selector: 'osf-overview-collections', + imports: [Accordion, AccordionPanel, AccordionHeader, AccordionContent, TranslatePipe, Skeleton, Tag, Button], + templateUrl: './overview-collections.component.html', + styleUrl: './overview-collections.component.scss', + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class OverviewCollectionsComponent { + private readonly router = inject(Router); + protected readonly SubmissionReviewStatus = SubmissionReviewStatus; + currentProject = input.required(); + projectSubmissions = select(CollectionsSelectors.getCurrentProjectSubmissions); + isProjectSubmissionsLoading = select(CollectionsSelectors.getCurrentProjectSubmissionsLoading); + + protected projectId = computed(() => { + const resource = this.currentProject(); + return resource ? resource.id : null; + }); + + protected actions = createDispatchMap({ + getProjectSubmissions: GetProjectSubmissions, + }); + + constructor() { + effect(() => { + const projectId = this.projectId(); + + if (projectId) { + this.actions.getProjectSubmissions(projectId); + } + }); + } + + protected get submissionAttributes() { + return (submission: CollectionSubmission) => { + if (!submission) return []; + + return collectionFilterNames + .map((attribute) => ({ + ...attribute, + value: submission[attribute.key as keyof CollectionSubmission] as string, + })) + .filter((attribute) => attribute.value); + }; + } + + navigateToCollection($event: Event, submission: CollectionSubmission) { + $event.stopPropagation(); + this.router.navigate([`collections/${submission.collectionId}/`]); + } +} diff --git a/src/app/shared/components/resource-metadata/resource-metadata.component.html b/src/app/shared/components/resource-metadata/resource-metadata.component.html index bfc09ba59..e85cf021b 100644 --- a/src/app/shared/components/resource-metadata/resource-metadata.component.html +++ b/src/app/shared/components/resource-metadata/resource-metadata.component.html @@ -138,6 +138,10 @@

{{ 'project.overview.metadata.affiliatedInstitutions' | translate }}

+ @if (resource.type === resourceTypes.Projects) { + + } +

{{ 'project.overview.metadata.subjects' | translate }}

diff --git a/src/app/shared/components/resource-metadata/resource-metadata.component.ts b/src/app/shared/components/resource-metadata/resource-metadata.component.ts index 5f09d3b11..86bf1d419 100644 --- a/src/app/shared/components/resource-metadata/resource-metadata.component.ts +++ b/src/app/shared/components/resource-metadata/resource-metadata.component.ts @@ -7,6 +7,7 @@ import { DatePipe } from '@angular/common'; import { ChangeDetectionStrategy, Component, input, output } from '@angular/core'; import { RouterLink } from '@angular/router'; +import { OverviewCollectionsComponent } from '@osf/features/project/overview/components/overview-collections/overview-collections.component'; import { ResourceCitationsComponent } from '@shared/components/resource-citations/resource-citations.component'; import { TruncatedTextComponent } from '@shared/components/truncated-text/truncated-text.component'; import { OsfResourceTypes } from '@shared/constants'; @@ -14,7 +15,16 @@ import { ResourceOverview } from '@shared/models'; @Component({ selector: 'osf-resource-metadata', - imports: [Button, TranslatePipe, TruncatedTextComponent, RouterLink, Tag, DatePipe, ResourceCitationsComponent], + imports: [ + Button, + TranslatePipe, + TruncatedTextComponent, + RouterLink, + Tag, + DatePipe, + ResourceCitationsComponent, + OverviewCollectionsComponent, + ], templateUrl: './resource-metadata.component.html', styleUrl: './resource-metadata.component.scss', changeDetection: ChangeDetectionStrategy.OnPush, diff --git a/src/app/shared/mappers/collections/collections.mapper.ts b/src/app/shared/mappers/collections/collections.mapper.ts index 2b55caf99..f1388a4de 100644 --- a/src/app/shared/mappers/collections/collections.mapper.ts +++ b/src/app/shared/mappers/collections/collections.mapper.ts @@ -14,6 +14,8 @@ import { CollectionSubmissionJsonApi, CollectionSubmissionPayload, CollectionSubmissionPayloadJsonApi, + CollectionSubmissionWithGuid, + CollectionSubmissionWithGuidJsonApi, PaginatedData, ReviewActionPayload, ReviewActionPayloadJsonApi, @@ -86,9 +88,29 @@ export class CollectionsMapper { }; } + static fromCurrentSubmissionResponse(submission: CollectionSubmissionJsonApi): CollectionSubmission { + return { + id: submission.id, + type: submission.type, + reviewsState: submission.attributes.reviews_state, + collectedType: submission.attributes.collected_type, + status: submission.attributes.status, + volume: submission.attributes.volume, + issue: submission.attributes.issue, + programArea: submission.attributes.program_area, + schoolType: submission.attributes.school_type, + studyDesign: submission.attributes.study_design, + dataType: submission.attributes.data_type, + disease: submission.attributes.disease, + gradeLevels: submission.attributes.grade_levels, + collectionTitle: submission.embeds.collection.data.attributes.title, + collectionId: submission.embeds.collection.data.relationships.provider.data.id, + }; + } + static fromGetCollectionSubmissionsResponse( - response: JsonApiResponseWithPaging - ): PaginatedData { + response: JsonApiResponseWithPaging + ): PaginatedData { return { data: response.data.map((submission) => ({ id: submission.id, @@ -141,7 +163,9 @@ export class CollectionsMapper { })); } - static fromPostCollectionSubmissionsResponse(response: CollectionSubmissionJsonApi[]): CollectionSubmission[] { + static fromPostCollectionSubmissionsResponse( + response: CollectionSubmissionWithGuidJsonApi[] + ): CollectionSubmissionWithGuid[] { return response.map((submission) => ({ id: submission.id, type: submission.type, diff --git a/src/app/shared/models/collections/collections-json-api.models.ts b/src/app/shared/models/collections/collections-json-api.models.ts index dff8a6826..1d86024f6 100644 --- a/src/app/shared/models/collections/collections-json-api.models.ts +++ b/src/app/shared/models/collections/collections-json-api.models.ts @@ -60,6 +60,40 @@ export interface CollectionDetailsResponseJsonApi { } export interface CollectionSubmissionJsonApi { + id: string; + type: string; + attributes: { + reviews_state: string; + collected_type: string; + status: string; + volume: string; + issue: string; + program_area: string; + school_type: string; + study_design: string; + data_type: string; + disease: string; + grade_levels: string; + }; + embeds: { + collection: { + data: { + attributes: { + title: string; + }; + relationships: { + provider: { + data: { + id: string; + }; + }; + }; + }; + }; + }; +} + +export interface CollectionSubmissionWithGuidJsonApi { id: string; type: string; attributes: { diff --git a/src/app/shared/models/collections/collections.models.ts b/src/app/shared/models/collections/collections.models.ts index b19412c04..9631640d1 100644 --- a/src/app/shared/models/collections/collections.models.ts +++ b/src/app/shared/models/collections/collections.models.ts @@ -62,6 +62,24 @@ export interface CollectionContributor { } export interface CollectionSubmission { + id: string; + type: string; + collectionTitle: string; + collectionId: string; + reviewsState: string; + collectedType: string; + status: string; + volume: string; + issue: string; + programArea: string; + schoolType: string; + studyDesign: string; + dataType: string; + disease: string; + gradeLevels: string; +} + +export interface CollectionSubmissionWithGuid { id: string; type: string; nodeId: string; diff --git a/src/app/shared/services/collections.service.ts b/src/app/shared/services/collections.service.ts index 0fc59e059..bf2867f42 100644 --- a/src/app/shared/services/collections.service.ts +++ b/src/app/shared/services/collections.service.ts @@ -15,11 +15,14 @@ import { CollectionContributor, CollectionDetails, CollectionDetailsGetResponseJsonApi, + CollectionDetailsResponseJsonApi, CollectionProvider, CollectionProviderGetResponseJsonApi, CollectionSubmission, CollectionSubmissionJsonApi, CollectionSubmissionsSearchPayloadJsonApi, + CollectionSubmissionWithGuid, + CollectionSubmissionWithGuidJsonApi, ContributorsResponseJsonApi, PaginatedData, ReviewActionPayload, @@ -60,7 +63,7 @@ export class CollectionsService { activeFilters: Record, page = '1', sortBy: string - ): Observable { + ): Observable { const url = `${environment.apiUrl}/search/collections/`; const params: Record = { page, @@ -82,7 +85,7 @@ export class CollectionsService { }; return this.jsonApiService - .post>(url, payload, params) + .post>(url, payload, params) .pipe( switchMap((response) => { if (!response.data.length) { @@ -113,7 +116,7 @@ export class CollectionsService { status: string, page = '1', sortBy: string - ): Observable> { + ): Observable> { const params: Record = { page, 'filter[reviews_state]': status, @@ -124,7 +127,7 @@ export class CollectionsService { return this.jsonApiService .get< - JsonApiResponseWithPaging + JsonApiResponseWithPaging >(`${environment.apiUrl}/collections/${collectionId}/collection_submissions/`, params) .pipe( map((response) => { @@ -133,6 +136,35 @@ export class CollectionsService { ); } + fetchProjectCollections(projectId: string): Observable { + return this.jsonApiService + .get< + JsonApiResponse + >(`${environment.apiUrl}/nodes/${projectId}/collections/`) + .pipe( + map((response) => + response.data.map((collection) => CollectionsMapper.fromGetCollectionDetailsResponse(collection)) + ) + ); + } + + fetchCurrentSubmission(projectId: string, collectionId: string): Observable { + const params: Record = { + 'filter[id]': projectId, + embed: 'collection', + }; + + return this.jsonApiService + .get< + JsonApiResponse + >(`${environment.apiUrl}/collections/${collectionId}/collection_submissions/`, params) + .pipe( + map((response) => { + return CollectionsMapper.fromCurrentSubmissionResponse(response.data[0]); + }) + ); + } + fetchCollectionSubmissionsActions( projectId: string, collectionId: string @@ -148,7 +180,10 @@ export class CollectionsService { .pipe(map((response) => CollectionsMapper.fromGetCollectionSubmissionsActionsResponse(response.data))); } - fetchAllUserCollectionSubmissions(providerId: string, projectIds: string[]): Observable { + fetchAllUserCollectionSubmissions( + providerId: string, + projectIds: string[] + ): Observable { const pendingSubmissions$ = this.fetchUserCollectionSubmissionsByStatus(providerId, projectIds, 'pending'); const acceptedSubmissions$ = this.fetchUserCollectionSubmissionsByStatus(providerId, projectIds, 'accepted'); @@ -182,7 +217,7 @@ export class CollectionsService { providerId: string, projectIds: string[], submissionStatus: string - ): Observable> { + ): Observable> { const params: Record = { 'filter[reviews_state]': submissionStatus, 'filter[id]': projectIds.join(','), @@ -190,7 +225,7 @@ export class CollectionsService { return this.jsonApiService .get< - JsonApiResponseWithPaging + JsonApiResponseWithPaging >(`${environment.apiUrl}/collections/${providerId}/collection_submissions/`, params) .pipe( map((response) => { diff --git a/src/app/shared/stores/collections/collections.actions.ts b/src/app/shared/stores/collections/collections.actions.ts index 03f289aef..73262351d 100644 --- a/src/app/shared/stores/collections/collections.actions.ts +++ b/src/app/shared/stores/collections/collections.actions.ts @@ -12,6 +12,12 @@ export class GetCollectionDetails { constructor(public collectionId: string) {} } +export class GetProjectSubmissions { + static readonly type = '[Collections] Get Project Submissions'; + + constructor(public projectId: string) {} +} + export class ClearCollections { static readonly type = '[Collections] Clear Collections'; } diff --git a/src/app/shared/stores/collections/collections.model.ts b/src/app/shared/stores/collections/collections.model.ts index 219e1dbad..49dee4ffe 100644 --- a/src/app/shared/stores/collections/collections.model.ts +++ b/src/app/shared/stores/collections/collections.model.ts @@ -1,4 +1,10 @@ -import { CollectionDetails, CollectionProvider, CollectionsFilters, CollectionSubmission } from '@shared/models'; +import { + CollectionDetails, + CollectionProvider, + CollectionsFilters, + CollectionSubmission, + CollectionSubmissionWithGuid, +} from '@shared/models'; import { AsyncStateModel } from '@shared/models/store'; export interface CollectionsStateModel { @@ -6,10 +12,63 @@ export interface CollectionsStateModel { filtersOptions: CollectionsFilters; collectionProvider: AsyncStateModel; collectionDetails: AsyncStateModel; - collectionSubmissions: AsyncStateModel; - userCollectionSubmissions: AsyncStateModel; + collectionSubmissions: AsyncStateModel; + userCollectionSubmissions: AsyncStateModel; + currentProjectSubmissions: AsyncStateModel; totalSubmissions: number; sortBy: string; searchText: string; page: string; } + +export const FILTERS_DEFAULTS = { + programArea: [], + status: [], + collectedType: [], + dataType: [], + disease: [], + gradeLevels: [], + issue: [], + reviewsState: [], + schoolType: [], + studyDesign: [], + volume: [], +}; + +export const COLLECTIONS_DEFAULTS: CollectionsStateModel = { + currentFilters: FILTERS_DEFAULTS, + filtersOptions: FILTERS_DEFAULTS, + collectionProvider: { + data: null, + isLoading: false, + error: null, + }, + collectionDetails: { + data: null, + isLoading: false, + isSubmitting: false, + error: null, + }, + collectionSubmissions: { + data: [], + isLoading: false, + isSubmitting: false, + error: null, + }, + userCollectionSubmissions: { + data: [], + isLoading: false, + isSubmitting: false, + error: null, + }, + currentProjectSubmissions: { + data: [], + isLoading: false, + isSubmitting: false, + error: null, + }, + totalSubmissions: 0, + sortBy: '', + searchText: '', + page: '1', +}; diff --git a/src/app/shared/stores/collections/collections.selectors.ts b/src/app/shared/stores/collections/collections.selectors.ts index 047937a55..f2ba6bb69 100644 --- a/src/app/shared/stores/collections/collections.selectors.ts +++ b/src/app/shared/stores/collections/collections.selectors.ts @@ -41,6 +41,16 @@ export class CollectionsSelectors { return state.collectionSubmissions.data; } + @Selector([CollectionsState]) + static getCurrentProjectSubmissions(state: CollectionsStateModel) { + return state.currentProjectSubmissions.data; + } + + @Selector([CollectionsState]) + static getCurrentProjectSubmissionsLoading(state: CollectionsStateModel) { + return state.currentProjectSubmissions.isLoading; + } + @Selector([CollectionsState]) static getCollectionSubmissionsLoading(state: CollectionsStateModel) { return state.collectionSubmissions.isLoading; diff --git a/src/app/shared/stores/collections/collections.state.ts b/src/app/shared/stores/collections/collections.state.ts index b58cabad0..36fe4cef0 100644 --- a/src/app/shared/stores/collections/collections.state.ts +++ b/src/app/shared/stores/collections/collections.state.ts @@ -1,6 +1,6 @@ import { Action, State, StateContext } from '@ngxs/store'; -import { catchError, tap, throwError } from 'rxjs'; +import { catchError, forkJoin, of, switchMap, tap, throwError } from 'rxjs'; import { inject, Injectable } from '@angular/core'; @@ -11,6 +11,7 @@ import { ClearCollectionSubmissions, GetCollectionDetails, GetCollectionProvider, + GetProjectSubmissions, GetUserCollectionSubmissions, SearchCollectionSubmissions, SetAllFilters, @@ -29,53 +30,7 @@ import { SetTotalSubmissions, SetVolumeFilters, } from './collections.actions'; -import { CollectionsStateModel } from './collections.model'; - -const FILTERS_DEFAULTS = { - programArea: [], - status: [], - collectedType: [], - dataType: [], - disease: [], - gradeLevels: [], - issue: [], - reviewsState: [], - schoolType: [], - studyDesign: [], - volume: [], -}; - -const COLLECTIONS_DEFAULTS: CollectionsStateModel = { - currentFilters: FILTERS_DEFAULTS, - filtersOptions: FILTERS_DEFAULTS, - collectionProvider: { - data: null, - isLoading: false, - error: null, - }, - collectionDetails: { - data: null, - isLoading: false, - isSubmitting: false, - error: null, - }, - collectionSubmissions: { - data: [], - isLoading: false, - isSubmitting: false, - error: null, - }, - userCollectionSubmissions: { - data: [], - isLoading: false, - isSubmitting: false, - error: null, - }, - totalSubmissions: 0, - sortBy: '', - searchText: '', - page: '1', -}; +import { COLLECTIONS_DEFAULTS, CollectionsStateModel } from './collections.model'; @State({ name: 'collections', @@ -108,6 +63,44 @@ export class CollectionsState { ); } + @Action(GetProjectSubmissions) + getProjectSubmissions(ctx: StateContext, action: GetProjectSubmissions) { + const state = ctx.getState(); + ctx.patchState({ + currentProjectSubmissions: { + ...state.currentProjectSubmissions, + isLoading: true, + }, + }); + + return this.collectionsService.fetchProjectCollections(action.projectId).pipe( + switchMap((res) => { + const collections = res.filter((collection) => !collection.bookmarks); + + if (!collections.length) { + return of([]); + } + + const submissionRequests = collections.map((collection) => + this.collectionsService.fetchCurrentSubmission(action.projectId, collection.id) + ); + + return forkJoin(submissionRequests); + }), + tap((submissions) => { + ctx.patchState({ + currentProjectSubmissions: { + data: submissions, + isLoading: false, + isSubmitting: false, + error: null, + }, + }); + }), + catchError((error) => this.handleError(ctx, 'currentProjectSubmissions', error)) + ); + } + @Action(GetCollectionDetails) getCollectionDetails(ctx: StateContext, action: GetCollectionDetails) { const state = ctx.getState(); diff --git a/src/assets/i18n/en.json b/src/assets/i18n/en.json index bacb6181e..717560653 100644 --- a/src/assets/i18n/en.json +++ b/src/assets/i18n/en.json @@ -581,6 +581,8 @@ "subjects": "Subjects", "tags": "Tags", "citation": "Citation", + "collection": "Collection", + "noCollections": "No collections", "getMoreCitations": "Get More Citations", "citationInputPlaceholder": "Select citation style or start typing", "citationLoadingPlaceholder": "Loading options...",