Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand All @@ -14,7 +14,7 @@ import { CollectionSubmission } from '@shared/models';
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class CollectionsSearchResultCardComponent {
cardItem = input.required<CollectionSubmission>();
cardItem = input.required<CollectionSubmissionWithGuid>();

protected presentSubmissionAttributes = computed(() => {
const item = this.cardItem();
Expand All @@ -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);
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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';

Expand All @@ -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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -28,7 +28,7 @@ import { ReviewStatusIcon } from '../../constants';
export class CollectionSubmissionItemComponent {
private router = inject(Router);
private activatedRoute = inject(ActivatedRoute);
submission = input.required<CollectionSubmission>();
submission = input.required<CollectionSubmissionWithGuid>();
collectionProvider = select(CollectionsSelectors.getCollectionProvider);

protected readonly reviewStatusIcon = ReviewStatusIcon;
Expand All @@ -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);
});
Expand Down
27 changes: 27 additions & 0 deletions src/app/features/moderation/constants/submission.const.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
Original file line number Diff line number Diff line change
@@ -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<CollectionSubmission[]>;
collectionSubmissions: AsyncStateWithTotalCount<CollectionSubmissionWithGuid[]>;
currentReviewAction: AsyncStateModel<CollectionSubmissionReviewAction | null>;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
@let project = currentProject();
@let submissions = projectSubmissions();

@if (project) {
<h3 class="mb-3">{{ 'project.overview.metadata.collection' | translate }}</h3>
@if (isProjectSubmissionsLoading()) {
<p-skeleton height="3rem" width="100%"></p-skeleton>
} @else {
<div class="flex flex-column gap-3">
@if (submissions.length) {
@for (submission of submissions; track submission.id) {
<div class="metadata-accordion">
<p-accordion>
<p-accordion-panel value="0">
<p-accordion-header class="py-0 gap-2 justify-content-between">
<div class="flex align-items-center gap-3">
<p-button
link
class="link-btn-no-padding"
styleClass="text-left"
(click)="navigateToCollection($event, submission)"
[label]="submission.collectionTitle"
></p-button>

@let status = submission.reviewsState;
@switch (status) {
@case (SubmissionReviewStatus.Accepted) {
<p-tag class="capitalize" [value]="status" severity="success"></p-tag>
}
@case (SubmissionReviewStatus.Rejected) {
<p-tag class="capitalize" [value]="status" severity="danger"></p-tag>
}
@case (SubmissionReviewStatus.Pending) {
<p-tag class="capitalize" [value]="status" severity="warn"></p-tag>
}
@case (SubmissionReviewStatus.Removed) {
<p-tag class="capitalize" [value]="status" severity="secondary"></p-tag>
}
}
</div>
</p-accordion-header>
<p-accordion-content>
@let attributes = submissionAttributes(submission);
@if (attributes.length) {
<div class="flex flex-column gap-2 mt-2">
@for (attribute of attributes; track attribute.key) {
<p class="font-normal">
<span class="font-bold">{{ attribute.label }}:</span> {{ attribute.value }}
</p>
}
</div>
}
</p-accordion-content>
</p-accordion-panel>
</p-accordion>
</div>
}
} @else {
<p>{{ 'project.overview.metadata.noCollections' | translate }}</p>
}
</div>
}
}
Original file line number Diff line number Diff line change
@@ -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<OverviewCollectionsComponent>;

beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [OverviewCollectionsComponent],
}).compileComponents();

fixture = TestBed.createComponent(OverviewCollectionsComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});

it('should create', () => {
expect(component).toBeTruthy();
});
});
Original file line number Diff line number Diff line change
@@ -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<ResourceOverview | null>();
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}/`]);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,10 @@ <h3>{{ 'project.overview.metadata.affiliatedInstitutions' | translate }}</h3>
</div>
</div>

@if (resource.type === resourceTypes.Projects) {
<osf-overview-collections [currentProject]="currentResource()"></osf-overview-collections>
}

<div class="flex flex-column gap-2">
<h3>{{ 'project.overview.metadata.subjects' | translate }}</h3>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,24 @@ 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';
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,
Expand Down
30 changes: 27 additions & 3 deletions src/app/shared/mappers/collections/collections.mapper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ import {
CollectionSubmissionJsonApi,
CollectionSubmissionPayload,
CollectionSubmissionPayloadJsonApi,
CollectionSubmissionWithGuid,
CollectionSubmissionWithGuidJsonApi,
PaginatedData,
ReviewActionPayload,
ReviewActionPayloadJsonApi,
Expand Down Expand Up @@ -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<CollectionSubmissionJsonApi[], null>
): PaginatedData<CollectionSubmission[]> {
response: JsonApiResponseWithPaging<CollectionSubmissionWithGuidJsonApi[], null>
): PaginatedData<CollectionSubmissionWithGuid[]> {
return {
data: response.data.map((submission) => ({
id: submission.id,
Expand Down Expand Up @@ -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,
Expand Down
Loading