diff --git a/src/app/features/moderation/components/collection-submission-item/collection-submission-item.component.html b/src/app/features/moderation/components/collection-submission-item/collection-submission-item.component.html index b5bfb2c65..e411a6cc2 100644 --- a/src/app/features/moderation/components/collection-submission-item/collection-submission-item.component.html +++ b/src/app/features/moderation/components/collection-submission-item/collection-submission-item.component.html @@ -2,53 +2,81 @@ @let attributes = currentSubmissionAttributes(); @if (action && attributes) { -
-
- +
+ + + +
+
+
+ - -
+ +
-

- @switch (action.toState) { - @case (SubmissionReviewStatus.Pending) { - {{ 'moderation.submissionReview.submitted' | translate }} - } - @case (SubmissionReviewStatus.Accepted) { - @if (!collectionProvider()?.reviewsWorkflow) { - {{ 'moderation.submissionReview.submitted' | translate }} - } @else { - {{ 'moderation.submissionReview.accepted' | translate }} - } - } - @case (SubmissionReviewStatus.Rejected) { - {{ 'moderation.submissionReview.rejected' | translate }} - } - @case (SubmissionReviewStatus.Removed) { - {{ 'moderation.submissionReview.withdrawn' | translate }} - } - } - {{ action.dateCreated | dateAgo }} - {{ 'moderation.submissionReview.by' | translate }} - {{ action.createdBy }} -

+

+ @switch (action.toState) { + @case (SubmissionReviewStatus.Pending) { + {{ 'moderation.submissionReview.submitted' | translate }} + } + @case (SubmissionReviewStatus.Accepted) { + @if (!collectionProvider()?.reviewsWorkflow) { + {{ 'moderation.submissionReview.submitted' | translate }} + } @else { + {{ 'moderation.submissionReview.accepted' | translate }} + } + } + @case (SubmissionReviewStatus.Rejected) { + {{ 'moderation.submissionReview.rejected' | translate }} + } + @case (SubmissionReviewStatus.Removed) { + {{ 'moderation.submissionReview.withdrawn' | translate }} + } + } + {{ action.dateCreated | dateAgo }} + {{ 'moderation.submissionReview.by' | translate }} + {{ action.createdBy }} +

-
- @for (attribute of attributes; track attribute.key) { - @if (!$first) { - | - } -

- {{ attribute.label }}: {{ attribute.value }} -

- } -
+
+ @for (attribute of attributes; track attribute.key) { + @if (!$first) { + | + } +

+ {{ attribute.label }}: {{ attribute.value }} +

+ } +
- @if (action.comment) { - - } + @if (action.comment) { + + } +
+
+ + + +
+

{{ 'common.labels.contributors' | translate }}:

+ + +
+
+ +
} 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 c24ba1577..0f5d0a3ae 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 @@ -2,12 +2,14 @@ import { select } from '@ngxs/store'; import { TranslatePipe } from '@ngx-translate/core'; +import { Accordion, AccordionContent, AccordionHeader, AccordionPanel } from 'primeng/accordion'; import { Button } from 'primeng/button'; -import { ChangeDetectionStrategy, Component, computed, inject, input } from '@angular/core'; +import { ChangeDetectionStrategy, Component, computed, inject, input, output } from '@angular/core'; import { ActivatedRoute, Router } from '@angular/router'; import { collectionFilterNames } from '@osf/features/collections/constants'; +import { ContributorsListComponent } from '@osf/shared/components/contributors-list/contributors-list.component'; import { IconComponent } from '@osf/shared/components/icon/icon.component'; import { TruncatedTextComponent } from '@osf/shared/components/truncated-text/truncated-text.component'; import { CollectionSubmissionWithGuid } from '@osf/shared/models/collections/collections.models'; @@ -19,7 +21,18 @@ import { SubmissionReviewStatus } from '../../enums'; @Component({ selector: 'osf-submission-item', - imports: [TranslatePipe, IconComponent, DateAgoPipe, Button, TruncatedTextComponent], + imports: [ + TranslatePipe, + IconComponent, + DateAgoPipe, + Button, + TruncatedTextComponent, + Accordion, + AccordionPanel, + AccordionHeader, + AccordionContent, + ContributorsListComponent, + ], templateUrl: './collection-submission-item.component.html', styleUrl: './collection-submission-item.component.scss', changeDetection: ChangeDetectionStrategy.OnPush, @@ -29,7 +42,8 @@ export class CollectionSubmissionItemComponent { private activatedRoute = inject(ActivatedRoute); submission = input.required(); - + loadContributors = output(); + loadMoreContributors = output(); collectionProvider = select(CollectionsSelectors.getCollectionProvider); readonly reviewStatusIcon = ReviewStatusIcon; @@ -55,6 +69,15 @@ export class CollectionSubmissionItemComponent { .filter((attribute) => attribute.value); }); + hasMoreContributors = computed(() => { + const submission = this.submission(); + if (submission.contributors && submission.totalContributors) { + return submission.contributors.length < submission.totalContributors; + } + + return false; + }); + handleNavigation() { const currentStatus = this.activatedRoute.snapshot.queryParams['status']; const queryParams = currentStatus ? { status: currentStatus, mode: 'moderation' } : {}; @@ -68,4 +91,8 @@ export class CollectionSubmissionItemComponent { window.open(url, '_blank'); } + + handleOpen() { + this.loadContributors.emit(); + } } diff --git a/src/app/features/moderation/components/collection-submissions-list/collection-submissions-list.component.html b/src/app/features/moderation/components/collection-submissions-list/collection-submissions-list.component.html index 5e6d11a21..fc38d9de7 100644 --- a/src/app/features/moderation/components/collection-submissions-list/collection-submissions-list.component.html +++ b/src/app/features/moderation/components/collection-submissions-list/collection-submissions-list.component.html @@ -2,7 +2,12 @@
@for (item of submissions(); track $index) {
- + +
}
diff --git a/src/app/features/moderation/components/collection-submissions-list/collection-submissions-list.component.ts b/src/app/features/moderation/components/collection-submissions-list/collection-submissions-list.component.ts index 1bf63272c..e87f597fd 100644 --- a/src/app/features/moderation/components/collection-submissions-list/collection-submissions-list.component.ts +++ b/src/app/features/moderation/components/collection-submissions-list/collection-submissions-list.component.ts @@ -1,9 +1,15 @@ -import { select } from '@ngxs/store'; +import { createDispatchMap, select } from '@ngxs/store'; import { TranslatePipe } from '@ngx-translate/core'; import { ChangeDetectionStrategy, Component } from '@angular/core'; +import { + GetCollectionSubmissionContributors, + LoadMoreCollectionSubmissionContributors, +} from '@osf/features/moderation/store/collections-moderation'; +import { CollectionSubmissionWithGuid } from '@shared/models/collections/collections.models'; + import { CollectionsModerationSelectors } from '../../store/collections-moderation'; import { CollectionSubmissionItemComponent } from '../collection-submission-item/collection-submission-item.component'; @@ -16,4 +22,17 @@ import { CollectionSubmissionItemComponent } from '../collection-submission-item }) export class CollectionSubmissionsListComponent { submissions = select(CollectionsModerationSelectors.getCollectionSubmissions); + + readonly actions = createDispatchMap({ + getCollectionSubmissionContributors: GetCollectionSubmissionContributors, + loadMoreCollectionSubmissionContributors: LoadMoreCollectionSubmissionContributors, + }); + + loadContributors(item: CollectionSubmissionWithGuid) { + this.actions.getCollectionSubmissionContributors(item.id, 1); + } + + loadMoreContributors(item: CollectionSubmissionWithGuid) { + this.actions.loadMoreCollectionSubmissionContributors(item.id); + } } diff --git a/src/app/features/moderation/components/registry-pending-submissions/registry-pending-submissions.component.html b/src/app/features/moderation/components/registry-pending-submissions/registry-pending-submissions.component.html index 8c9be9cf7..77f120b97 100644 --- a/src/app/features/moderation/components/registry-pending-submissions/registry-pending-submissions.component.html +++ b/src/app/features/moderation/components/registry-pending-submissions/registry-pending-submissions.component.html @@ -39,6 +39,8 @@ [submission]="item" [status]="selectedReviewOption()" (selected)="navigateToRegistration(item)" + (loadContributors)="loadContributors(item)" + (loadMoreContributors)="loadMoreContributors(item)" >
} diff --git a/src/app/features/moderation/components/registry-pending-submissions/registry-pending-submissions.component.ts b/src/app/features/moderation/components/registry-pending-submissions/registry-pending-submissions.component.ts index 7f21e3fc5..ac0f00e76 100644 --- a/src/app/features/moderation/components/registry-pending-submissions/registry-pending-submissions.component.ts +++ b/src/app/features/moderation/components/registry-pending-submissions/registry-pending-submissions.component.ts @@ -24,7 +24,12 @@ import { Primitive } from '@osf/shared/helpers/types.helper'; import { PENDING_SUBMISSION_REVIEW_OPTIONS, REGISTRY_SORT_OPTIONS } from '../../constants'; import { RegistrySort, SubmissionReviewStatus } from '../../enums'; import { RegistryModeration } from '../../models'; -import { GetRegistrySubmissions, RegistryModerationSelectors } from '../../store/registry-moderation'; +import { + GetRegistrySubmissionContributors, + GetRegistrySubmissions, + LoadMoreRegistrySubmissionContributors, + RegistryModerationSelectors, +} from '../../store/registry-moderation'; import { RegistrySubmissionItemComponent } from '../registry-submission-item/registry-submission-item.component'; @Component({ @@ -54,7 +59,11 @@ export class RegistryPendingSubmissionsComponent implements OnInit { this.route.parent?.params.pipe(map((params) => params['providerId'])) ?? of(undefined) ); - readonly actions = createDispatchMap({ getRegistrySubmissions: GetRegistrySubmissions }); + readonly actions = createDispatchMap({ + getRegistrySubmissions: GetRegistrySubmissions, + getRegistrySubmissionContributors: GetRegistrySubmissionContributors, + loadMoreRegistrySubmissionContributors: LoadMoreRegistrySubmissionContributors, + }); readonly submissions = select(RegistryModerationSelectors.getRegistrySubmissions); readonly isLoading = select(RegistryModerationSelectors.areRegistrySubmissionLoading); @@ -112,6 +121,14 @@ export class RegistryPendingSubmissionsComponent implements OnInit { window.open(url, '_blank'); } + loadContributors(item: RegistryModeration) { + this.actions.getRegistrySubmissionContributors(item.id); + } + + loadMoreContributors(item: RegistryModeration) { + this.actions.loadMoreRegistrySubmissionContributors(item.id); + } + private getStatusFromQueryParams() { const queryParams = this.route.snapshot.queryParams; const statusValues = Object.values(SubmissionReviewStatus); diff --git a/src/app/features/moderation/components/registry-submission-item/registry-submission-item.component.html b/src/app/features/moderation/components/registry-submission-item/registry-submission-item.component.html index 86c6bbef2..006ce75fc 100644 --- a/src/app/features/moderation/components/registry-submission-item/registry-submission-item.component.html +++ b/src/app/features/moderation/components/registry-submission-item/registry-submission-item.component.html @@ -1,54 +1,76 @@ -
- - -
- @if (isRejected) { -

{{ submission().title }}

- } @else { - - } - - @if (submission().public && !submission().embargoEndDate) { -

{{ 'registry.overview.statuses.accepted.text' | translate }}

- } - - @if (submission().embargoEndDate) { -

- {{ 'moderation.registryEmbargoedWithEndDate' | translate }} - {{ submission().embargoEndDate | date: 'MMM d, y' }} -

- } - - @for (action of showAll ? submission().actions : submission().actions.slice(0, limitValue); track $index) { -
- {{ registryActionLabel[action.toState] | translate }} - {{ action.dateModified | dateAgo }} - {{ 'moderation.submissionReview.by' | translate }} - {{ action.creator?.name }} - - @if (action.toState === registryActionState.Accepted) { - {{ 'moderation.withNoEmbargo' | translate }} - } - - @if (action.comment.length) { - - } -
- } - - @if (submission().actions.length > 1) { - - } -
+
+ + + +
+ + +
+ @if (isRejected) { +

{{ submission().title }}

+ } @else { + + } + + @if (submission().public && !submission().embargoEndDate) { +

{{ 'registry.overview.statuses.accepted.text' | translate }}

+ } + + @if (submission().embargoEndDate) { +

+ {{ 'moderation.registryEmbargoedWithEndDate' | translate }} + {{ submission().embargoEndDate | date: 'MMM d, y' }} +

+ } + + @for (action of showAll ? submission().actions : submission().actions.slice(0, limitValue); track $index) { +
+ {{ registryActionLabel[action.toState] | translate }} + {{ action.dateModified | dateAgo }} + {{ 'moderation.submissionReview.by' | translate }} + {{ action.creator?.name }} + + @if (action.toState === registryActionState.Accepted) { + {{ 'moderation.withNoEmbargo' | translate }} + } + + @if (action.comment.length) { + + } +
+ } + + @if (submission().actions.length > 1) { + + } +
+
+
+ + +
+

{{ 'common.labels.contributors' | translate }}:

+ + + +
+
+
+
diff --git a/src/app/features/moderation/components/registry-submission-item/registry-submission-item.component.ts b/src/app/features/moderation/components/registry-submission-item/registry-submission-item.component.ts index 38f265cf8..770db64d4 100644 --- a/src/app/features/moderation/components/registry-submission-item/registry-submission-item.component.ts +++ b/src/app/features/moderation/components/registry-submission-item/registry-submission-item.component.ts @@ -1,10 +1,12 @@ import { TranslatePipe } from '@ngx-translate/core'; +import { Accordion, AccordionContent, AccordionHeader, AccordionPanel } from 'primeng/accordion'; import { Button } from 'primeng/button'; import { DatePipe } from '@angular/common'; -import { ChangeDetectionStrategy, Component, input, output } from '@angular/core'; +import { ChangeDetectionStrategy, Component, computed, input, output } from '@angular/core'; +import { ContributorsListComponent } from '@osf/shared/components/contributors-list/contributors-list.component'; import { IconComponent } from '@osf/shared/components/icon/icon.component'; import { TruncatedTextComponent } from '@osf/shared/components/truncated-text/truncated-text.component'; import { DateAgoPipe } from '@osf/shared/pipes/date-ago.pipe'; @@ -15,7 +17,19 @@ import { RegistryModeration } from '../../models'; @Component({ selector: 'osf-registry-submission-item', - imports: [IconComponent, DateAgoPipe, Button, TranslatePipe, DatePipe, TruncatedTextComponent], + imports: [ + IconComponent, + DateAgoPipe, + Button, + TranslatePipe, + DatePipe, + TruncatedTextComponent, + Accordion, + AccordionPanel, + AccordionHeader, + AccordionContent, + ContributorsListComponent, + ], templateUrl: './registry-submission-item.component.html', styleUrl: './registry-submission-item.component.scss', changeDetection: ChangeDetectionStrategy.OnPush, @@ -23,6 +37,8 @@ import { RegistryModeration } from '../../models'; export class RegistrySubmissionItemComponent { status = input.required(); submission = input.required(); + loadContributors = output(); + loadMoreContributors = output(); selected = output(); @@ -40,4 +56,17 @@ export class RegistrySubmissionItemComponent { toggleHistory() { this.showAll = !this.showAll; } + + hasMoreContributors = computed(() => { + const submission = this.submission(); + if (submission.contributors && submission.totalContributors) { + return submission.contributors.length < submission.totalContributors; + } + + return false; + }); + + handleOpen() { + this.loadContributors.emit(); + } } diff --git a/src/app/features/moderation/components/registry-submissions/registry-submissions.component.html b/src/app/features/moderation/components/registry-submissions/registry-submissions.component.html index 53a842857..5066d15f7 100644 --- a/src/app/features/moderation/components/registry-submissions/registry-submissions.component.html +++ b/src/app/features/moderation/components/registry-submissions/registry-submissions.component.html @@ -39,6 +39,8 @@ [submission]="item" [status]="selectedReviewOption()" (selected)="navigateToRegistration(item)" + (loadContributors)="loadContributors(item)" + (loadMoreContributors)="loadMoreContributors(item)" >
} diff --git a/src/app/features/moderation/components/registry-submissions/registry-submissions.component.ts b/src/app/features/moderation/components/registry-submissions/registry-submissions.component.ts index 86afd1975..e664daacc 100644 --- a/src/app/features/moderation/components/registry-submissions/registry-submissions.component.ts +++ b/src/app/features/moderation/components/registry-submissions/registry-submissions.component.ts @@ -24,7 +24,12 @@ import { Primitive } from '@osf/shared/helpers/types.helper'; import { REGISTRY_SORT_OPTIONS, SUBMITTED_SUBMISSION_REVIEW_OPTIONS } from '../../constants'; import { RegistrySort, SubmissionReviewStatus } from '../../enums'; import { RegistryModeration } from '../../models'; -import { GetRegistrySubmissions, RegistryModerationSelectors } from '../../store/registry-moderation'; +import { + GetRegistrySubmissionContributors, + GetRegistrySubmissions, + LoadMoreRegistrySubmissionContributors, + RegistryModerationSelectors, +} from '../../store/registry-moderation'; import { RegistrySubmissionItemComponent } from '..'; @Component({ @@ -54,7 +59,11 @@ export class RegistrySubmissionsComponent implements OnInit { this.route.parent?.params.pipe(map((params) => params['providerId'])) ?? of(undefined) ); - readonly actions = createDispatchMap({ getRegistrySubmissions: GetRegistrySubmissions }); + readonly actions = createDispatchMap({ + getRegistrySubmissions: GetRegistrySubmissions, + getRegistrySubmissionContributors: GetRegistrySubmissionContributors, + loadMoreRegistrySubmissionContributors: LoadMoreRegistrySubmissionContributors, + }); readonly submissions = select(RegistryModerationSelectors.getRegistrySubmissions); readonly isLoading = select(RegistryModerationSelectors.areRegistrySubmissionLoading); @@ -112,6 +121,14 @@ export class RegistrySubmissionsComponent implements OnInit { window.open(url, '_blank'); } + loadContributors(item: RegistryModeration) { + this.actions.getRegistrySubmissionContributors(item.id); + } + + loadMoreContributors(item: RegistryModeration) { + this.actions.loadMoreRegistrySubmissionContributors(item.id); + } + private getStatusFromQueryParams() { const queryParams = this.route.snapshot.queryParams; const statusValues = Object.values(SubmissionReviewStatus); diff --git a/src/app/features/moderation/models/registry-moderation.model.ts b/src/app/features/moderation/models/registry-moderation.model.ts index cf2cc9662..2d59b2681 100644 --- a/src/app/features/moderation/models/registry-moderation.model.ts +++ b/src/app/features/moderation/models/registry-moderation.model.ts @@ -1,5 +1,6 @@ import { RegistrationReviewStates } from '@osf/shared/enums/registration-review-states.enum'; import { RevisionReviewStates } from '@osf/shared/enums/revision-review-states.enum'; +import { ContributorModel } from '@shared/models/contributors/contributor.model'; import { ReviewAction } from './review-action.model'; @@ -13,4 +14,8 @@ export interface RegistryModeration { embargoEndDate: string | null; actions: ReviewAction[]; revisionId?: string | null; + contributorsLoading?: boolean; + contributors?: ContributorModel[]; + totalContributors?: number; + contributorsPage?: number; } diff --git a/src/app/features/moderation/store/collections-moderation/collections-moderation.actions.ts b/src/app/features/moderation/store/collections-moderation/collections-moderation.actions.ts index dc44a8e29..a71fbfd7b 100644 --- a/src/app/features/moderation/store/collections-moderation/collections-moderation.actions.ts +++ b/src/app/features/moderation/store/collections-moderation/collections-moderation.actions.ts @@ -29,3 +29,18 @@ export class CreateCollectionSubmissionAction { export class ClearCollectionModeration { static readonly type = '[Collections Moderation] ClearCollectionModeration'; } + +export class GetCollectionSubmissionContributors { + static readonly type = `[Collections Moderation] Get Registry Submission Contributors`; + + constructor( + public collectionId: string, + public page = 1 + ) {} +} + +export class LoadMoreCollectionSubmissionContributors { + static readonly type = `[Collections Moderation] Load More Collection Submission Contributors`; + + constructor(public collectionId: string) {} +} diff --git a/src/app/features/moderation/store/collections-moderation/collections-moderation.state.ts b/src/app/features/moderation/store/collections-moderation/collections-moderation.state.ts index 5e4d94010..fb5132c9b 100644 --- a/src/app/features/moderation/store/collections-moderation/collections-moderation.state.ts +++ b/src/app/features/moderation/store/collections-moderation/collections-moderation.state.ts @@ -6,13 +6,18 @@ import { catchError, forkJoin, map, of, switchMap, tap } from 'rxjs'; import { inject, Injectable } from '@angular/core'; import { CollectionsService } from '@osf/shared/services/collections.service'; +import { DEFAULT_TABLE_PARAMS } from '@shared/constants/default-table-params.constants'; +import { ResourceType } from '@shared/enums/resource-type.enum'; import { handleSectionError } from '@shared/helpers/state-error.handler'; +import { ContributorsService } from '@shared/services/contributors.service'; import { ClearCollectionModeration, CreateCollectionSubmissionAction, + GetCollectionSubmissionContributors, GetCollectionSubmissions, GetSubmissionsReviewActions, + LoadMoreCollectionSubmissionContributors, } from './collections-moderation.actions'; import { COLLECTIONS_MODERATION_STATE_DEFAULTS, CollectionsModerationStateModel } from './collections-moderation.model'; @@ -23,6 +28,7 @@ import { COLLECTIONS_MODERATION_STATE_DEFAULTS, CollectionsModerationStateModel @Injectable() export class CollectionsModerationState { collectionsService = inject(CollectionsService); + contributorsService = inject(ContributorsService); @Action(GetCollectionSubmissions) getCollectionSubmissions(ctx: StateContext, action: GetCollectionSubmissions) { @@ -117,4 +123,81 @@ export class CollectionsModerationState { clearCollectionModeration(ctx: StateContext) { ctx.setState(COLLECTIONS_MODERATION_STATE_DEFAULTS); } + + @Action(GetCollectionSubmissionContributors) + getCollectionSubmissionContributors( + ctx: StateContext, + action: GetCollectionSubmissionContributors + ) { + const state = ctx.getState(); + const submission = state.collectionSubmissions.data.find((s) => s.id === action.collectionId); + + if (submission?.contributors && submission.contributors.length > 0 && action.page === 1) { + return; + } + + ctx.setState( + patch({ + collectionSubmissions: { + ...state.collectionSubmissions, + isSubmitting: true, + }, + }) + ); + return this.contributorsService + .getBibliographicContributors(ResourceType.Project, action.collectionId, action.page, DEFAULT_TABLE_PARAMS.rows) + .pipe( + tap((res) => { + const currentSubmission = state.collectionSubmissions.data.find((s) => s.id === action.collectionId); + const existingContributors = currentSubmission?.contributors || []; + const newContributors = action.page === 1 ? res.data : [...existingContributors, ...res.data]; + + ctx.patchState({ + collectionSubmissions: { + ...state.collectionSubmissions, + data: state.collectionSubmissions.data.map((submission) => + submission.id === action.collectionId + ? { + ...submission, + contributors: newContributors, + totalContributors: res.totalCount, + contributorsLoading: false, + contributorsPage: action.page, + } + : submission + ), + }, + }); + }), + catchError((error) => { + ctx.patchState({ + collectionSubmissions: { + ...state.collectionSubmissions, + data: state.collectionSubmissions.data.map((submission) => + submission.id === action.collectionId + ? { + ...submission, + contributorsLoading: false, + } + : submission + ), + }, + }); + return handleSectionError(ctx, 'collectionSubmissions', error); + }) + ); + } + + @Action(LoadMoreCollectionSubmissionContributors) + loadMoreCollectionSubmissionContributors( + ctx: StateContext, + action: LoadMoreCollectionSubmissionContributors + ) { + const state = ctx.getState(); + const submission = state.collectionSubmissions.data.find((s) => s.id === action.collectionId); + const currentPage = submission?.contributorsPage || 1; + const nextPage = currentPage + 1; + + return ctx.dispatch(new GetCollectionSubmissionContributors(action.collectionId, nextPage)); + } } diff --git a/src/app/features/moderation/store/registry-moderation/registry-moderation.actions.ts b/src/app/features/moderation/store/registry-moderation/registry-moderation.actions.ts index 3f7026c0a..3e350142c 100644 --- a/src/app/features/moderation/store/registry-moderation/registry-moderation.actions.ts +++ b/src/app/features/moderation/store/registry-moderation/registry-moderation.actions.ts @@ -12,3 +12,18 @@ export class GetRegistrySubmissions { public sort?: RegistrySort ) {} } + +export class GetRegistrySubmissionContributors { + static readonly type = `${ACTION_SCOPE} Get Registry Submission Contributors`; + + constructor( + public registryId: string, + public page = 1 + ) {} +} + +export class LoadMoreRegistrySubmissionContributors { + static readonly type = `${ACTION_SCOPE} Load More Registry Submission Contributors`; + + constructor(public registryId: string) {} +} diff --git a/src/app/features/moderation/store/registry-moderation/registry-moderation.state.ts b/src/app/features/moderation/store/registry-moderation/registry-moderation.state.ts index a7ac4561b..52b068e89 100644 --- a/src/app/features/moderation/store/registry-moderation/registry-moderation.state.ts +++ b/src/app/features/moderation/store/registry-moderation/registry-moderation.state.ts @@ -1,5 +1,5 @@ import { Action, State, StateContext } from '@ngxs/store'; -import { patch } from '@ngxs/store/operators'; +import { patch, updateItem } from '@ngxs/store/operators'; import { catchError, forkJoin, map, of, switchMap, tap } from 'rxjs'; @@ -7,11 +7,18 @@ import { inject, Injectable } from '@angular/core'; import { handleSectionError } from '@osf/shared/helpers/state-error.handler'; import { PaginatedData } from '@osf/shared/models/paginated-data.model'; +import { DEFAULT_TABLE_PARAMS } from '@shared/constants/default-table-params.constants'; +import { ResourceType } from '@shared/enums/resource-type.enum'; +import { ContributorsService } from '@shared/services/contributors.service'; import { RegistryModeration } from '../../models'; import { RegistryModerationService } from '../../services'; -import { GetRegistrySubmissions } from './registry-moderation.actions'; +import { + GetRegistrySubmissionContributors, + GetRegistrySubmissions, + LoadMoreRegistrySubmissionContributors, +} from './registry-moderation.actions'; import { REGISTRY_MODERATION_STATE_DEFAULTS, RegistryModerationStateModel } from './registry-moderation.model'; @State({ @@ -21,6 +28,84 @@ import { REGISTRY_MODERATION_STATE_DEFAULTS, RegistryModerationStateModel } from @Injectable() export class RegistryModerationState { private readonly registryModerationService = inject(RegistryModerationService); + private readonly contributorsService = inject(ContributorsService); + + @Action(GetRegistrySubmissionContributors) + getRegistrySubmissionContributors( + ctx: StateContext, + { registryId, page }: GetRegistrySubmissionContributors + ) { + const state = ctx.getState(); + const submission = state.submissions.data.find((s) => s.id === registryId); + + if (submission?.contributors && submission.contributors.length > 0 && page === 1) { + return; + } + + ctx.setState( + patch({ + submissions: patch({ + data: updateItem( + (submission) => submission.id === registryId, + patch({ contributorsLoading: true }) + ), + }), + }) + ); + + return this.contributorsService + .getBibliographicContributors(ResourceType.Registration, registryId, page, DEFAULT_TABLE_PARAMS.rows) + .pipe( + tap((res) => { + const currentSubmission = state.submissions.data.find((s) => s.id === registryId); + const existingContributors = currentSubmission?.contributors || []; + const newContributors = page === 1 ? res.data : [...existingContributors, ...res.data]; + + ctx.setState( + patch({ + submissions: patch({ + data: updateItem( + (submission) => submission.id === registryId, + patch({ + contributors: newContributors, + totalContributors: res.totalCount, + contributorsLoading: false, + contributorsPage: page, + }) + ), + }), + }) + ); + }), + catchError((error) => { + ctx.setState( + patch({ + submissions: patch({ + data: updateItem( + (submission) => submission.id === registryId, + patch({ contributorsLoading: false }) + ), + }), + }) + ); + + return handleSectionError(ctx, 'submissions', error); + }) + ); + } + + @Action(LoadMoreRegistrySubmissionContributors) + loadMoreRegistrySubmissionContributors( + ctx: StateContext, + { registryId }: LoadMoreRegistrySubmissionContributors + ) { + const state = ctx.getState(); + const submission = state.submissions.data.find((s) => s.id === registryId); + const currentPage = submission?.contributorsPage || 1; + const nextPage = currentPage + 1; + + return ctx.dispatch(new GetRegistrySubmissionContributors(registryId, nextPage)); + } @Action(GetRegistrySubmissions) getRegistrySubmissions( diff --git a/src/app/shared/models/collections/collections.models.ts b/src/app/shared/models/collections/collections.models.ts index 887851807..b50125b8c 100644 --- a/src/app/shared/models/collections/collections.models.ts +++ b/src/app/shared/models/collections/collections.models.ts @@ -89,6 +89,9 @@ export interface CollectionSubmissionWithGuid { fullName: string; }; actions?: CollectionSubmissionReviewAction[]; + totalContributors?: number; + contributorsLoading?: boolean; + contributorsPage?: number; } export type CollectionSubmissionActionType = 'collection_submission_actions';