diff --git a/src/app/features/preprints/components/stepper/author-assertion-step/author-assertions-step.component.html b/src/app/features/preprints/components/stepper/author-assertion-step/author-assertions-step.component.html index dd7e22bb7..05675671b 100644 --- a/src/app/features/preprints/components/stepper/author-assertion-step/author-assertions-step.component.html +++ b/src/app/features/preprints/components/stepper/author-assertion-step/author-assertions-step.component.html @@ -1,15 +1,11 @@ -

Author Assertions

+

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

-

Conflict of Interest

+

{{ 'preprints.preprintStepper.authorAssertions.conflictOfInterest.title' | translate }}

- The Conflict of Interest (COI) assertion is made on behalf of all the authors listed for this preprint. COIs - include: financial involvement in any entity such as honoraria, grants, speaking fees, employment, consultancies, - stock ownership, expert testimony, and patents or licenses. COIs can also include non-financial interests such as - personal or professional relationships or pre-existing beliefs in the subject matter or materials discussed in - this preprint. + {{ 'preprints.preprintStepper.authorAssertions.conflictOfInterest.description' | translate }}

@@ -20,7 +16,7 @@

Conflict of Interest

[value]="true" [formControl]="authorAssertionsForm.controls['hasCoi']" /> - +
@@ -30,7 +26,7 @@

Conflict of Interest

[value]="false" [formControl]="authorAssertionsForm.controls['hasCoi']" /> - +
@@ -43,8 +39,8 @@

Conflict of Interest

[formControl]="authorAssertionsForm.controls['coiStatement']" [placeholder]=" authorAssertionsForm.value.hasCoi - ? 'Describe' - : 'Author asserted there is no Conflict of Interest with this preprint.' + ? ('preprints.preprintStepper.common.placeholders.describe' | translate) + : ('preprints.preprintStepper.authorAssertions.conflictOfInterest.noCoiPlaceholder' | translate) " > @let coiStatementControl = authorAssertionsForm.controls['coiStatement']; @@ -58,14 +54,10 @@

Conflict of Interest

-

Public Data

+

{{ 'preprints.preprintStepper.authorAssertions.publicData.title' | translate }}

- Data refers to raw and/or processed information (quantitative or qualitative) used for the analyses, case studies, - and/or descriptive interpretation in the preprint. Public data could include data posted to open-access - repositories, public archival library collection, or government archive. For data that is available under limited - circumstances (e.g., after signing a data sharing agreement), choose the ‘No’ option and use the comment box to - explain how others could access the data. + {{ 'preprints.preprintStepper.authorAssertions.publicData.description' | translate }}

@@ -76,7 +68,7 @@

Public Data

[value]="ApplicabilityStatus.Applicable" [formControl]="authorAssertionsForm.controls['hasDataLinks']" /> - +
@@ -86,7 +78,7 @@

Public Data

[value]="ApplicabilityStatus.Unavailable" [formControl]="authorAssertionsForm.controls['hasDataLinks']" /> - +
@@ -96,7 +88,7 @@

Public Data

[value]="ApplicabilityStatus.NotApplicable" [formControl]="authorAssertionsForm.controls['hasDataLinks']" /> - +
@@ -113,8 +105,8 @@

Public Data

[rows]="3" [placeholder]=" hasDataLinks === ApplicabilityStatus.Unavailable - ? 'Describe' - : 'Author asserted there is no data associated with this preprint.' + ? ('preprints.preprintStepper.common.placeholders.describe' | translate) + : ('preprints.preprintStepper.authorAssertions.publicData.notApplicablePlaceholder' | translate) " > @let whyNoDataControl = authorAssertionsForm.controls['whyNoData']; @@ -127,7 +119,7 @@

Public Data

@@ -137,12 +129,10 @@

Public Data

-

Public Preregistration

+

{{ 'preprints.preprintStepper.authorAssertions.publicPreregistration.title' | translate }}

- A preregistration is a description of the research design and/or analysis plan that is created and registered - before researchers collected data or before they have seen/interacted with preexisting data. The description - should appear in a public registry (e.g., clinicaltrials.gov, OSF, AEA registry). + {{ 'preprints.preprintStepper.authorAssertions.publicPreregistration.description' | translate }}

@@ -154,7 +144,7 @@

Public Preregistration

[value]="ApplicabilityStatus.Applicable" [formControl]="authorAssertionsForm.controls['hasPreregLinks']" /> - +
@@ -164,7 +154,7 @@

Public Preregistration

[value]="ApplicabilityStatus.Unavailable" [formControl]="authorAssertionsForm.controls['hasPreregLinks']" /> - +
@@ -174,7 +164,7 @@

Public Preregistration

[value]="ApplicabilityStatus.NotApplicable" [formControl]="authorAssertionsForm.controls['hasPreregLinks']" /> - +
@let hasPreregLinks = authorAssertionsForm.value.hasPreregLinks!; @@ -190,8 +180,8 @@

Public Preregistration

[formControl]="authorAssertionsForm.controls['whyNoPrereg']" [placeholder]=" hasPreregLinks === ApplicabilityStatus.Unavailable - ? 'Describe' - : 'The author asserts that a preregistration is not applicable because no data collection, extraction, or analysis is reported in the preprint.' + ? ('preprints.preprintStepper.common.placeholders.describe' | translate) + : ('preprints.preprintStepper.authorAssertions.publicPreregistration.notApplicablePlaceholder' | translate) " > @let hasPreregLinksControl = authorAssertionsForm.controls['hasPreregLinks']; @@ -201,22 +191,19 @@

Public Preregistration

} } @else { - +
+ +
@@ -224,12 +211,20 @@

Public Preregistration

- + ({ - label: key, - value, - })); + readonly preregLinkOptions = preregLinksOptions; readonly linkValidators = [CustomValidators.linkValidator(), CustomValidators.requiredTrimmed()]; createdPreprint = select(SubmitPreprintSelectors.getCreatedPreprint); @@ -126,6 +125,7 @@ export class AuthorAssertionsStepComponent { }); nextClicked = output(); + backClicked = output(); constructor() { effect(() => { @@ -193,25 +193,19 @@ export class AuthorAssertionsStepComponent { }); } - @HostListener('window:beforeunload', ['$event']) - public onBeforeUnload($event: BeforeUnloadEvent): boolean { - $event.preventDefault(); - return false; - } - nextButtonClicked() { - const formValue = this.authorAssertionsForm.value; + const formValue = this.authorAssertionsForm.getRawValue(); const hasCoi = formValue.hasCoi; - const coiStatement = formValue.coiStatement || null; + const coiStatement = formValue.coiStatement; const hasDataLinks = formValue.hasDataLinks; - const whyNoData = formValue.whyNoData || null; - const dataLinks: string[] = formValue.dataLinks || []; + const whyNoData = formValue.whyNoData; + const dataLinks = formValue.dataLinks; const hasPreregLinks = formValue.hasPreregLinks; - const whyNoPrereg = formValue.whyNoPrereg || null; - const preregLinks: string[] = formValue.preregLinks || []; + const whyNoPrereg = formValue.whyNoPrereg; + const preregLinks = formValue.preregLinks; const preregLinkInfo = formValue.preregLinkInfo || undefined; this.actions @@ -228,12 +222,31 @@ export class AuthorAssertionsStepComponent { }) .subscribe({ complete: () => { - this.toastService.showSuccess('Preprint saved successfully.'); + this.toastService.showSuccess('preprints.preprintStepper.common.successMessages.preprintSaved'); this.nextClicked.emit(); }, }); } + backButtonClicked() { + const formValue = this.authorAssertionsForm.getRawValue(); + const changedFields = findChangedFields(formValue, this.createdPreprint()!); + + if (!Object.keys(changedFields).length) { + this.backClicked.emit(); + return; + } + + this.confirmationService.confirmContinue({ + headerKey: 'common.discardChanges.header', + messageKey: 'common.discardChanges.message', + onConfirm: () => { + this.backClicked.emit(); + }, + onReject: () => null, + }); + } + private disableAndClearValidators(control: AbstractControl) { if (control instanceof FormArray) { while (control.length !== 0) { diff --git a/src/app/features/preprints/components/stepper/file-step/file-step.component.html b/src/app/features/preprints/components/stepper/file-step/file-step.component.html index aa47143d0..c3ab6ee12 100644 --- a/src/app/features/preprints/components/stepper/file-step/file-step.component.html +++ b/src/app/features/preprints/components/stepper/file-step/file-step.component.html @@ -1,8 +1,13 @@ -

File

+

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

-

Please Upload your Preprint

-

Note: You cannot switch options once a file is attached.

+

+ {{ + 'preprints.preprintStepper.file.uploadDescription' + | translate: { preprintWord: provider()?.preprintWord | titlecase } + }} +

+

{{ 'preprints.preprintStepper.file.note' | translate }}

@@ -12,10 +17,10 @@

File

}" class="file-source-button w-full" [styleClass]="selectedFileSource() ? 'w-full cursor-not-allowed' : 'w-full'" - [label]="'Upload From Your Computer' | titlecase" + [label]="'preprints.preprintStepper.file.uploadFromComputer' | translate | titlecase" severity="secondary" [disabled]="isFileSourceSelected()" - [pTooltip]="isFileSourceSelected() ? 'Start a new preprint to attach a file from your computer.' : ''" + [pTooltip]="isFileSourceSelected() ? ('preprints.preprintStepper.file.tooltips.computerDisabled' | translate) : ''" tooltipPosition="top" (click)="selectFileSource(PreprintFileSource.Computer)" /> @@ -25,10 +30,10 @@

File

}" class="file-source-button w-full" [styleClass]="isFileSourceSelected() ? 'w-full cursor-not-allowed' : 'w-full'" - [label]="'Select From existing OSF project' | titlecase" + [label]="'preprints.preprintStepper.file.selectFromProject' | translate | titlecase" severity="secondary" [disabled]="isFileSourceSelected() || versionFileMode()" - [pTooltip]="isFileSourceSelected() ? 'Start a new preprint to attach a file from your project.' : ''" + [pTooltip]="isFileSourceSelected() ? ('preprints.preprintStepper.file.tooltips.projectDisabled' | translate) : ''" tooltipPosition="top" (click)="selectFileSource(PreprintFileSource.Project)" /> @@ -44,7 +49,7 @@

File

raised severity="success" [icon]="'fas fa-upload'" - [label]="'Upload file'" + [label]="'preprints.preprintStepper.file.uploadFileButton' | translate | titlecase" (click)="fileInput.click()" /> @@ -56,8 +61,10 @@

File

@if (selectedFileSource() === PreprintFileSource.Project && !preprintFiles().length && !arePreprintFilesLoading()) {
-

This will make your project public, if it is not already

-

The projects and components for which you have admin access are listed below.

+

{{ 'preprints.preprintStepper.file.projectSelection.description' | translate }}

+

+ {{ 'preprints.preprintStepper.file.projectSelection.subDescription' | translate }} +

File optionLabel="name" optionValue="id" [formControl]="projectNameControl" - [placeholder]="'Project Title'" + [placeholder]="'preprints.preprintStepper.file.projectSelection.title' | translate" class="w-12 md:w-6" [editable]="true" styleClass="m-t-24" @@ -110,11 +117,17 @@

File

}
- + diff --git a/src/app/features/preprints/components/stepper/file-step/file-step.component.ts b/src/app/features/preprints/components/stepper/file-step/file-step.component.ts index 315e832d2..9d7690adc 100644 --- a/src/app/features/preprints/components/stepper/file-step/file-step.component.ts +++ b/src/app/features/preprints/components/stepper/file-step/file-step.component.ts @@ -1,5 +1,7 @@ import { createDispatchMap, select } from '@ngxs/store'; +import { TranslatePipe } from '@ngx-translate/core'; + import { Button } from 'primeng/button'; import { Card } from 'primeng/card'; import { DialogService } from 'primeng/dynamicdialog'; @@ -15,8 +17,8 @@ import { Component, computed, DestroyRef, - HostListener, inject, + input, OnInit, output, signal, @@ -26,6 +28,7 @@ import { FormControl, ReactiveFormsModule } from '@angular/forms'; import { StringOrNull } from '@core/helpers'; import { PreprintFileSource } from '@osf/features/preprints/enums'; +import { PreprintProviderDetails } from '@osf/features/preprints/models'; import { CopyFileFromProject, GetAvailableProjects, @@ -40,7 +43,7 @@ import { import { FilesTreeActions } from '@osf/features/project/files/models'; import { FilesTreeComponent, IconComponent } from '@shared/components'; import { OsfFile } from '@shared/models'; -import { CustomConfirmationService } from '@shared/services'; +import { CustomConfirmationService, ToastService } from '@shared/services'; @Component({ selector: 'osf-file-step', @@ -55,6 +58,7 @@ import { CustomConfirmationService } from '@shared/services'; Select, ReactiveFormsModule, FilesTreeComponent, + TranslatePipe, ], templateUrl: './file-step.component.html', styleUrl: './file-step.component.scss', @@ -62,6 +66,7 @@ import { CustomConfirmationService } from '@shared/services'; changeDetection: ChangeDetectionStrategy.OnPush, }) export class FileStepComponent implements OnInit { + private toastService = inject(ToastService); private customConfirmationService = inject(CustomConfirmationService); private actions = createDispatchMap({ setSelectedFileSource: SetSelectedPreprintFileSource, @@ -77,6 +82,7 @@ export class FileStepComponent implements OnInit { readonly PreprintFileSource = PreprintFileSource; + provider = input.required(); createdPreprint = select(SubmitPreprintSelectors.getCreatedPreprint); providerId = select(SubmitPreprintSelectors.getSelectedProviderId); selectedFileSource = select(SubmitPreprintSelectors.getSelectedFileSource); @@ -145,6 +151,7 @@ export class FileStepComponent implements OnInit { return; } + this.toastService.showSuccess('preprints.preprintStepper.common.successMessages.preprintSaved'); this.nextClicked.emit(); } @@ -161,12 +168,6 @@ export class FileStepComponent implements OnInit { } } - @HostListener('window:beforeunload', ['$event']) - public onBeforeUnload($event: BeforeUnloadEvent): boolean { - $event.preventDefault(); - return false; - } - selectProject(event: SelectChangeEvent) { if (!(event.originalEvent instanceof PointerEvent)) { return; @@ -182,9 +183,8 @@ export class FileStepComponent implements OnInit { versionFile() { this.customConfirmationService.confirmContinue({ - headerKey: 'Add a new preprint file', - messageKey: - 'This will allow a new version of the preprint file to be uploaded to the preprint. The existing file will be retained as a version of the preprint.', + headerKey: 'preprints.preprintStepper.file.versionFile.header', + messageKey: 'preprints.preprintStepper.file.versionFile.message', onConfirm: () => { this.versionFileMode.set(true); this.actions.setSelectedFileSource(PreprintFileSource.None); diff --git a/src/app/features/preprints/components/stepper/metadata-step/metadata-step.component.html b/src/app/features/preprints/components/stepper/metadata-step/metadata-step.component.html index 83d557535..7286e901f 100644 --- a/src/app/features/preprints/components/stepper/metadata-step/metadata-step.component.html +++ b/src/app/features/preprints/components/stepper/metadata-step/metadata-step.component.html @@ -1,4 +1,4 @@ -

Metadata

+

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

@@ -24,7 +24,7 @@

{{ 'shared.license.title' | translate }}

-

Publication DOI

+

{{ 'preprints.preprintStepper.metadata.publicationDoi.title' | translate }}

@let doiControl = metadataForm.controls['doi']; @@ -35,19 +35,19 @@

Publication DOI

} @if (doiControl.errors?.['pattern'] && (doiControl.touched || doiControl.dirty)) { Please use a valid DOI format (10.xxxx/xxxxx) + >{{ 'preprints.preprintStepper.metadata.publicationDoi.patternError' | translate }} }
- +
-

Tags (optional)

+

{{ 'preprints.preprintStepper.metadata.tagsTitle' | translate }}

@@ -56,7 +56,7 @@

Tags (optional)

-

Publication Date (optional)

+

{{ 'preprints.preprintStepper.metadata.publicationDateTitle' | translate }}

Publication Date (optional)
-

Publication Citation (optional)

+

{{ 'preprints.preprintStepper.metadata.publicationCitationTitle' | translate }}

Publication Citation (optional)
- + (); + backClicked = output(); ngOnInit() { this.actions.fetchLicenses(); @@ -77,7 +81,7 @@ export class MetadataStepComponent implements OnInit { initForm() { const publicationDate = this.createdPreprint()?.originalPublicationDate; this.metadataForm = new FormGroup({ - doi: new FormControl(this.createdPreprint()?.doi || '', { + doi: new FormControl(this.createdPreprint()?.doi || null, { nonNullable: true, validators: [CustomValidators.requiredTrimmed(), Validators.pattern(this.inputLimits.doi.pattern)], }), @@ -93,6 +97,10 @@ export class MetadataStepComponent implements OnInit { nonNullable: true, validators: [], }), + subjects: new FormControl([], { + nonNullable: true, + validators: [Validators.required], + }), }); } @@ -107,17 +115,12 @@ export class MetadataStepComponent implements OnInit { this.actions.updatePreprint(this.createdPreprint()!.id, changedFields).subscribe({ complete: () => { + this.toastService.showSuccess('preprints.preprintStepper.common.successMessages.preprintSaved'); this.nextClicked.emit(); }, }); } - @HostListener('window:beforeunload', ['$event']) - public onBeforeUnload($event: BeforeUnloadEvent): boolean { - $event.preventDefault(); - return false; - } - createLicense(licenseDetails: { id: string; licenseOptions: LicenseOptions }) { this.actions.saveLicense(licenseDetails.id, licenseDetails.licenseOptions); } @@ -131,4 +134,24 @@ export class MetadataStepComponent implements OnInit { tags: updatedTags, }); } + + backButtonClicked() { + const formValue = this.metadataForm.value; + delete formValue.subjects; + const changedFields = findChangedFields(formValue, this.createdPreprint()!); + + if (!Object.keys(changedFields).length) { + this.backClicked.emit(); + return; + } + + this.customConfirmationService.confirmContinue({ + headerKey: 'common.discardChanges.header', + messageKey: 'common.discardChanges.message', + onConfirm: () => { + this.backClicked.emit(); + }, + onReject: () => null, + }); + } } diff --git a/src/app/features/preprints/components/stepper/metadata-step/preprints-subjects/preprints-subjects.component.html b/src/app/features/preprints/components/stepper/metadata-step/preprints-subjects/preprints-subjects.component.html index 97bd7afd0..52de95f26 100644 --- a/src/app/features/preprints/components/stepper/metadata-step/preprints-subjects/preprints-subjects.component.html +++ b/src/app/features/preprints/components/stepper/metadata-step/preprints-subjects/preprints-subjects.component.html @@ -6,4 +6,9 @@ (searchChanged)="searchSubjects($event)" (updateSelection)="updateSelectedSubjects($event)" > + @if (control().errors?.['required'] && (control().touched || control().dirty)) { + + {{ INPUT_VALIDATION_MESSAGES.required | translate }} + + } diff --git a/src/app/features/preprints/components/stepper/metadata-step/preprints-subjects/preprints-subjects.component.ts b/src/app/features/preprints/components/stepper/metadata-step/preprints-subjects/preprints-subjects.component.ts index abbeef477..fa5876c58 100644 --- a/src/app/features/preprints/components/stepper/metadata-step/preprints-subjects/preprints-subjects.component.ts +++ b/src/app/features/preprints/components/stepper/metadata-step/preprints-subjects/preprints-subjects.component.ts @@ -1,8 +1,12 @@ import { createDispatchMap, select } from '@ngxs/store'; +import { TranslatePipe } from '@ngx-translate/core'; + import { Card } from 'primeng/card'; +import { Message } from 'primeng/message'; -import { ChangeDetectionStrategy, Component, input, OnInit } from '@angular/core'; +import { ChangeDetectionStrategy, Component, effect, input, OnInit } from '@angular/core'; +import { FormControl } from '@angular/forms'; import { SubmitPreprintSelectors } from '@osf/features/preprints/store/submit-preprint'; import { SubjectsComponent } from '@osf/shared/components'; @@ -15,10 +19,11 @@ import { SubjectsSelectors, UpdateResourceSubjects, } from '@osf/shared/stores'; +import { INPUT_VALIDATION_MESSAGES } from '@shared/constants'; @Component({ selector: 'osf-preprints-subjects', - imports: [SubjectsComponent, Card], + imports: [SubjectsComponent, Card, Message, TranslatePipe], templateUrl: './preprints-subjects.component.html', styleUrl: './preprints-subjects.component.scss', changeDetection: ChangeDetectionStrategy.OnPush, @@ -29,7 +34,9 @@ export class PreprintsSubjectsComponent implements OnInit { private readonly selectedProviderId = select(SubmitPreprintSelectors.getSelectedProviderId); protected selectedSubjects = select(SubjectsSelectors.getSelectedSubjects); protected isSubjectsUpdating = select(SubjectsSelectors.areSelectedSubjectsLoading); + control = input.required(); + protected readonly INPUT_VALIDATION_MESSAGES = INPUT_VALIDATION_MESSAGES; protected actions = createDispatchMap({ fetchSubjects: FetchSubjects, fetchSelectedSubjects: FetchSelectedSubjects, @@ -37,6 +44,12 @@ export class PreprintsSubjectsComponent implements OnInit { updateResourceSubjects: UpdateResourceSubjects, }); + constructor() { + effect(() => { + this.updateControlState(this.selectedSubjects()); + }); + } + ngOnInit(): void { this.actions.fetchSubjects(ResourceType.Preprint, this.selectedProviderId()!); this.actions.fetchSelectedSubjects(this.preprintId()!, ResourceType.Preprint); @@ -51,6 +64,17 @@ export class PreprintsSubjectsComponent implements OnInit { } updateSelectedSubjects(subjects: SubjectModel[]) { + this.updateControlState(subjects); + this.actions.updateResourceSubjects(this.preprintId()!, ResourceType.Preprint, subjects); } + + updateControlState(value: SubjectModel[]) { + if (this.control()) { + this.control().setValue(value); + this.control().markAsTouched(); + this.control().markAsDirty(); + this.control().updateValueAndValidity(); + } + } } diff --git a/src/app/features/preprints/components/stepper/review-step/review-step.component.html b/src/app/features/preprints/components/stepper/review-step/review-step.component.html new file mode 100644 index 000000000..56fd7c245 --- /dev/null +++ b/src/app/features/preprints/components/stepper/review-step/review-step.component.html @@ -0,0 +1,235 @@ +

{{ 'preprints.preprintStepper.review.title' | translate | titlecase }}

+

+ {{ 'preprints.preprintStepper.review.consentDescription' | translate }} +

+

+ {{ + 'preprints.preprintStepper.review.workflowDescription' + | translate: { providerName: provider()?.name, reviewsWorkflow: provider()?.reviewsWorkflow } + }} +

+

+ {{ 'preprints.preprintStepper.review.learnMore' | translate }} + {{ + 'preprints.preprintStepper.review.moderationPolicies' | translate + }} + {{ 'preprints.preprintStepper.review.supportCenter' | translate }} +

+ + +
+

{{ 'preprints.preprintStepper.review.sections.titleAndAbstract.title' | translate }}

+ +
+

+ {{ + 'preprints.preprintStepper.review.sections.titleAndAbstract.service' + | translate: { preprintWord: provider()?.preprintWord | titlecase } + }} +

+
+ Provider logo +

{{ provider()?.name }}

+
+
+ +
+

{{ 'preprints.preprintStepper.common.labels.title' | translate }}

+

{{ createdPreprint()!.title }}

+
+ +
+

{{ 'preprints.preprintStepper.common.labels.abstract' | translate }}

+ +
+
+
+ + +
+

{{ 'preprints.preprintStepper.review.sections.metadata.title' | translate }}

+ +
+

{{ 'preprints.preprintStepper.review.sections.metadata.contributors' | translate }}

+ +
+ @for (contributor of bibliographicContributors(); track contributor.id) { +
+ {{ contributor.fullName }} + {{ $last ? '' : ',' }} +
+ } +
+
+ + @if (affiliatedInstitutions().length) { +
+

{{ 'preprints.preprintStepper.review.sections.metadata.affiliatedInstitutions' | translate }}

+ +
+ @for (institution of affiliatedInstitutions(); track institution.id) { + Institution logo + } +
+
+ } + +
+

{{ 'preprints.preprintStepper.review.sections.metadata.license' | translate }}

+ +
{{ license()?.name }}
+
+ +
+

{{ 'preprints.preprintStepper.review.sections.metadata.publicationDoi' | translate }}

+ + + {{ 'https://doi.org/' + createdPreprint()?.doi }} + +
+ +
+

{{ 'preprints.preprintStepper.review.sections.metadata.subjects' | translate }}

+ +
+ @for (subject of subjects(); track subject.id) { + + } +
+
+ +
+

{{ 'preprints.preprintStepper.review.sections.metadata.tags' | translate }}

+ +
+ @for (tag of createdPreprint()?.tags; track tag) { + + } @empty { +

{{ 'common.labels.none' | translate }}

+ } +
+
+ +
+

{{ 'preprints.preprintStepper.review.sections.metadata.publicationDate' | translate }}

+ + @if (createdPreprint()?.originalPublicationDate) { + {{ createdPreprint()?.originalPublicationDate | date: 'MMM d, y, h:mm a' }} + } @else { +

{{ 'common.labels.notApplicable' | translate }}

+ } +
+ +
+

{{ 'preprints.preprintStepper.review.sections.metadata.publicationCitation' | translate }}

+ + @if (createdPreprint()?.customPublicationCitation) { + {{ createdPreprint()?.customPublicationCitation }} + } @else { +

{{ 'common.labels.notApplicable' | translate }}

+ } +
+
+
+ +@if (provider()?.assertionsEnabled) { + +
+

{{ 'preprints.preprintStepper.review.sections.authorAssertions.title' | translate }}

+ +
+

{{ 'preprints.preprintStepper.review.sections.authorAssertions.conflictOfInterest' | translate }}

+ + @if (!createdPreprint()?.hasCoi) { +

{{ 'preprints.preprintStepper.review.sections.authorAssertions.noCoi' | translate }}

+ } @else { + {{ createdPreprint()?.coiStatement }} + } +
+ +
+

{{ 'preprints.preprintStepper.review.sections.authorAssertions.publicData' | translate }}

+ + @switch (createdPreprint()?.hasDataLinks) { + @case (ApplicabilityStatus.NotApplicable) { +

{{ 'preprints.preprintStepper.review.sections.authorAssertions.noData' | translate }}

+ } + @case (ApplicabilityStatus.Unavailable) { + {{ createdPreprint()?.whyNoData }} + } + @case (ApplicabilityStatus.Applicable) { + @for (link of createdPreprint()?.dataLinks; track $index) { +

{{ link }}

+ } + } + } +
+ +
+

+ {{ 'preprints.preprintStepper.review.sections.authorAssertions.publicPreregistration' | translate }} +

+ + @switch (createdPreprint()?.hasPreregLinks) { + @case (ApplicabilityStatus.NotApplicable) { +

+ {{ 'preprints.preprintStepper.review.sections.authorAssertions.noPrereg' | translate }} +

+ } + @case (ApplicabilityStatus.Unavailable) { + {{ createdPreprint()?.whyNoPrereg }} + } + @case (ApplicabilityStatus.Applicable) { + @switch (createdPreprint()?.preregLinkInfo) { + @case (PreregLinkInfo.Analysis) { +

+ {{ 'preprints.preprintStepper.common.labels.preregTypes.analysis' | translate }} +

+ } + @case (PreregLinkInfo.Designs) { +

+ {{ 'preprints.preprintStepper.common.labels.preregTypes.designs' | translate }} +

+ } + @case (PreregLinkInfo.Both) { +

+ {{ 'preprints.preprintStepper.common.labels.preregTypes.both' | translate }} +

+ } + } + @for (link of createdPreprint()?.preregLinks; track $index) { +

{{ link }}

+ } + } + } +
+
+
+} + + +
+

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

+ @if (preprintProject()) { +

{{ preprintProject()?.name }}

+ } @else { +

{{ 'preprints.preprintStepper.review.sections.supplements.noSupplements' | translate }}

+ } +
+
+ +
+ + +
diff --git a/src/app/features/preprints/components/stepper/review-step/review-step.component.scss b/src/app/features/preprints/components/stepper/review-step/review-step.component.scss new file mode 100644 index 000000000..243cc50eb --- /dev/null +++ b/src/app/features/preprints/components/stepper/review-step/review-step.component.scss @@ -0,0 +1,7 @@ +@use "assets/styles/variables" as var; + +.card { + @media (max-width: var.$breakpoint-sm) { + --p-card-body-padding: 0.75rem; + } +} diff --git a/src/app/features/preprints/components/stepper/review-step/review-step.component.spec.ts b/src/app/features/preprints/components/stepper/review-step/review-step.component.spec.ts new file mode 100644 index 000000000..b7e9fa071 --- /dev/null +++ b/src/app/features/preprints/components/stepper/review-step/review-step.component.spec.ts @@ -0,0 +1,22 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { ReviewStepComponent } from './review-step.component'; + +describe('ReviewStepComponent', () => { + let component: ReviewStepComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [ReviewStepComponent], + }).compileComponents(); + + fixture = TestBed.createComponent(ReviewStepComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/features/preprints/components/stepper/review-step/review-step.component.ts b/src/app/features/preprints/components/stepper/review-step/review-step.component.ts new file mode 100644 index 000000000..56c4913b6 --- /dev/null +++ b/src/app/features/preprints/components/stepper/review-step/review-step.component.ts @@ -0,0 +1,78 @@ +import { createDispatchMap, select } from '@ngxs/store'; + +import { TranslatePipe } from '@ngx-translate/core'; + +import { Button } from 'primeng/button'; +import { Card } from 'primeng/card'; +import { Tag } from 'primeng/tag'; + +import { DatePipe, TitleCasePipe } from '@angular/common'; +import { ChangeDetectionStrategy, Component, computed, inject, input, OnInit, signal } from '@angular/core'; +import { Router } from '@angular/router'; + +import { ApplicabilityStatus, PreregLinkInfo } from '@osf/features/preprints/enums'; +import { PreprintProviderDetails } from '@osf/features/preprints/models'; +import { + FetchLicenses, + FetchPreprintProject, + SubmitPreprint, + SubmitPreprintSelectors, +} from '@osf/features/preprints/store/submit-preprint'; +import { TruncatedTextComponent } from '@shared/components'; +import { ResourceType } from '@shared/enums'; +import { Institution } from '@shared/models'; +import { ToastService } from '@shared/services'; +import { ContributorsSelectors, FetchSelectedSubjects, GetAllContributors, SubjectsSelectors } from '@shared/stores'; + +@Component({ + selector: 'osf-review-step', + imports: [Card, TruncatedTextComponent, Tag, DatePipe, Button, TitleCasePipe, TranslatePipe], + templateUrl: './review-step.component.html', + styleUrl: './review-step.component.scss', + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class ReviewStepComponent implements OnInit { + private router = inject(Router); + private toastService = inject(ToastService); + private actions = createDispatchMap({ + getContributors: GetAllContributors, + fetchSubjects: FetchSelectedSubjects, + fetchLicenses: FetchLicenses, + fetchPreprintProject: FetchPreprintProject, + submitPreprint: SubmitPreprint, + }); + provider = input.required(); + createdPreprint = select(SubmitPreprintSelectors.getCreatedPreprint); + + contributors = select(ContributorsSelectors.getContributors); + bibliographicContributors = computed(() => { + return this.contributors().filter((contributor) => contributor.isBibliographic); + }); + subjects = select(SubjectsSelectors.getSelectedSubjects); + affiliatedInstitutions = signal([]); + license = select(SubmitPreprintSelectors.getPreprintLicense); + preprintProject = select(SubmitPreprintSelectors.getPreprintProject); + + readonly ApplicabilityStatus = ApplicabilityStatus; + readonly PreregLinkInfo = PreregLinkInfo; + + ngOnInit(): void { + this.actions.getContributors(this.createdPreprint()!.id, ResourceType.Preprint); + this.actions.fetchSubjects(this.createdPreprint()!.id, ResourceType.Preprint); + this.actions.fetchLicenses(); + this.actions.fetchPreprintProject(); + } + + submitPreprint() { + this.actions.submitPreprint().subscribe({ + complete: () => { + this.toastService.showSuccess('preprints.preprintStepper.common.successMessages.preprintSubmitted'); + this.router.navigateByUrl('/preprints'); + }, + }); + } + + cancelSubmission() { + this.router.navigateByUrl('/preprints'); + } +} 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 b942bf210..dab3ce395 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 @@ -1,7 +1,7 @@ -

Supplements (optional)

+

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

-

Connect an OSF project to share data, code, protocols, or other supplemental materials.

+

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

@let createdPreprintNodeId = createdPreprint()?.nodeId; @@ -13,7 +13,7 @@

Supplements (optional)

}" class="supplement-option-button w-full" styleClass="w-full" - [label]="'Connect an existing OSF project' | titlecase" + [label]="'preprints.preprintStepper.supplements.options.connectExisting' | translate | titlecase" severity="secondary" (click)="selectSupplementOption(SupplementOptions.ConnectExistingProject)" /> @@ -23,7 +23,7 @@

Supplements (optional)

}" class="supplement-option-button w-full" styleClass="w-full" - [label]="'Create a new OSF project' | titlecase" + [label]="'preprints.preprintStepper.supplements.options.createNew' | translate | titlecase" severity="secondary" (click)="selectSupplementOption(SupplementOptions.CreateNewProject)" /> @@ -32,15 +32,19 @@

Supplements (optional)

@if (selectedSupplementOption() === SupplementOptions.ConnectExistingProject) {
-

This will make your project public, if it is not already

-

The projects and components for which you have admin access are listed below.

+

+ {{ 'preprints.preprintStepper.supplements.projectSelection.description' | translate }} +

+

+ {{ 'preprints.preprintStepper.supplements.projectSelection.subDescription' | translate }} +

Supplements (optional) }
- + { - if (this.createdPreprint()?.nodeId) { - return false; + if (this.selectedSupplementOption() === SupplementOptions.CreateNewProject) { + return !this.createProjectFormValid(); } - switch (this.selectedSupplementOption()) { - case SupplementOptions.None: - return true; - case SupplementOptions.ConnectExistingProject: - return !this.createdPreprint()?.nodeId; - case SupplementOptions.CreateNewProject: - return !this.createProjectFormValid(); - default: - return false; - } + return false; }); constructor() { @@ -132,6 +134,7 @@ export class SupplementsStepComponent implements OnInit { } nextClicked = output(); + backClicked = output(); ngOnInit() { this.projectNameControl.valueChanges @@ -163,21 +166,20 @@ export class SupplementsStepComponent implements OnInit { this.actions.connectProject(event.value).subscribe({ complete: () => { - this.toastService.showSuccess('Project connected successfully'); + this.toastService.showSuccess('preprints.preprintStepper.supplements.successMessages.projectConnected'); }, }); } disconnectProject() { this.customConfirmationService.confirmDelete({ - headerKey: 'Disconnect supplemental material', - messageKey: - 'This will disconnect the selected project. You can select new supplemental material or re-add the same supplemental material at a later date.', + headerKey: 'preprints.preprintStepper.supplements.disconnectProject.header', + messageKey: 'preprints.preprintStepper.supplements.disconnectProject.message', onConfirm: () => { this.actions.disconnectProject().subscribe({ complete: () => { this.selectedProjectId.set(null); - this.toastService.showSuccess('Project disconnected successfully'); + this.toastService.showSuccess('preprints.preprintStepper.supplements.successMessages.projectDisconnected'); }, }); }, @@ -201,7 +203,7 @@ export class SupplementsStepComponent implements OnInit { ) .subscribe({ complete: () => { - this.toastService.showSuccess('Project created successfully'); + this.toastService.showSuccess('preprints.preprintStepper.supplements.successMessages.projectCreated'); this.nextClicked.emit(); }, }); @@ -213,12 +215,30 @@ export class SupplementsStepComponent implements OnInit { return; } + this.toastService.showSuccess('preprints.preprintStepper.common.successMessages.preprintSaved'); this.nextClicked.emit(); } - @HostListener('window:beforeunload', ['$event']) - public onBeforeUnload($event: BeforeUnloadEvent): boolean { - $event.preventDefault(); - return false; + backButtonClicked() { + const hasData = Object.entries(this.createProjectForm.value).some(([_, value]) => { + if (value instanceof Array) { + return value.length > 0; + } + return !!value; + }); + + if (this.selectedSupplementOption() === SupplementOptions.CreateNewProject && hasData) { + this.customConfirmationService.confirmContinue({ + headerKey: 'preprints.preprintStepper.supplements.discardChanges.header', + messageKey: 'preprints.preprintStepper.supplements.discardChanges.message', + onConfirm: () => { + this.backClicked.emit(); + }, + onReject: () => null, + }); + return; + } + + this.backClicked.emit(); } } diff --git a/src/app/features/preprints/components/stepper/title-and-abstract-step/title-and-abstract-step.component.html b/src/app/features/preprints/components/stepper/title-and-abstract-step/title-and-abstract-step.component.html index 9a8c1520c..7e835e96c 100644 --- a/src/app/features/preprints/components/stepper/title-and-abstract-step/title-and-abstract-step.component.html +++ b/src/app/features/preprints/components/stepper/title-and-abstract-step/title-and-abstract-step.component.html @@ -1,17 +1,17 @@ -

Title and Abstract

+

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

- +