diff --git a/src/app/features/preprints/components/index.ts b/src/app/features/preprints/components/index.ts index c8c2a5544..c22b6221a 100644 --- a/src/app/features/preprints/components/index.ts +++ b/src/app/features/preprints/components/index.ts @@ -4,18 +4,19 @@ export { AdditionalInfoComponent } from './preprint-details/additional-info/addi export { GeneralInformationComponent } from './preprint-details/general-information/general-information.component'; export { ModerationStatusBannerComponent } from './preprint-details/moderation-status-banner/moderation-status-banner.component'; export { PreprintFileSectionComponent } from './preprint-details/preprint-file-section/preprint-file-section.component'; +export { PreprintMakeDecisionComponent } from './preprint-details/preprint-make-decision/preprint-make-decision.component'; +export { PreprintTombstoneComponent } from './preprint-details/preprint-tombstone/preprint-tombstone.component'; +export { PreprintWarningBannerComponent } from './preprint-details/preprint-warning-banner/preprint-warning-banner.component'; export { ShareAndDownloadComponent } from './preprint-details/share-and-download/share-and-download.component'; export { StatusBannerComponent } from './preprint-details/status-banner/status-banner.component'; +export { WithdrawDialogComponent } from './preprint-details/withdraw-dialog/withdraw-dialog.component'; export { PreprintProviderFooterComponent } from './preprint-provider-footer/preprint-provider-footer.component'; export { PreprintProviderHeroComponent } from './preprint-provider-hero/preprint-provider-hero.component'; export { PreprintServicesComponent } from './preprint-services/preprint-services.component'; export { PreprintsHelpDialogComponent } from './preprints-help-dialog/preprints-help-dialog.component'; export { AuthorAssertionsStepComponent } from './stepper/author-assertion-step/author-assertions-step.component'; +export { FileStepComponent } from './stepper/file-step/file-step.component'; export { PreprintsMetadataStepComponent } from './stepper/preprints-metadata-step/preprints-metadata-step.component'; +export { ReviewStepComponent } from './stepper/review-step/review-step.component'; export { SupplementsStepComponent } from './stepper/supplements-step/supplements-step.component'; -export { MakeDecisionComponent } from '@osf/features/preprints/components/preprint-details/make-decision/make-decision.component'; -export { PreprintTombstoneComponent } from '@osf/features/preprints/components/preprint-details/preprint-tombstone/preprint-tombstone.component'; -export { WithdrawDialogComponent } from '@osf/features/preprints/components/preprint-details/withdraw-dialog/withdraw-dialog.component'; -export { FileStepComponent } from '@osf/features/preprints/components/stepper/file-step/file-step.component'; -export { ReviewStepComponent } from '@osf/features/preprints/components/stepper/review-step/review-step.component'; -export { TitleAndAbstractStepComponent } from '@osf/features/preprints/components/stepper/title-and-abstract-step/title-and-abstract-step.component'; +export { TitleAndAbstractStepComponent } from './stepper/title-and-abstract-step/title-and-abstract-step.component'; diff --git a/src/app/features/preprints/components/preprint-details/make-decision/make-decision.component.html b/src/app/features/preprints/components/preprint-details/preprint-make-decision/preprint-make-decision.component.html similarity index 98% rename from src/app/features/preprints/components/preprint-details/make-decision/make-decision.component.html rename to src/app/features/preprints/components/preprint-details/preprint-make-decision/preprint-make-decision.component.html index 6e5e1e74b..623804c96 100644 --- a/src/app/features/preprints/components/preprint-details/make-decision/make-decision.component.html +++ b/src/app/features/preprints/components/preprint-details/preprint-make-decision/preprint-make-decision.component.html @@ -56,6 +56,7 @@

{{ 'preprints.details.decision.decline.label' | transl class="w-full" [rows]="4" [(ngModel)]="requestDecisionJustification" + [maxlength]="decisionCommentLimit" > @if (didValidate() && decision() === ReviewsState.Rejected) { @@ -116,6 +117,7 @@

{{ rejectOptionLabel() | translate }}

[placeholder]="'preprints.details.decision.commentPlaceholder' | translate" [rows]="4" [(ngModel)]="reviewerComment" + [maxlength]="decisionCommentLimit" > @if (commentExceedsLimit()) { diff --git a/src/app/features/preprints/components/preprint-details/make-decision/make-decision.component.scss b/src/app/features/preprints/components/preprint-details/preprint-make-decision/preprint-make-decision.component.scss similarity index 100% rename from src/app/features/preprints/components/preprint-details/make-decision/make-decision.component.scss rename to src/app/features/preprints/components/preprint-details/preprint-make-decision/preprint-make-decision.component.scss diff --git a/src/app/features/preprints/components/preprint-details/make-decision/make-decision.component.spec.ts b/src/app/features/preprints/components/preprint-details/preprint-make-decision/preprint-make-decision.component.spec.ts similarity index 95% rename from src/app/features/preprints/components/preprint-details/make-decision/make-decision.component.spec.ts rename to src/app/features/preprints/components/preprint-details/preprint-make-decision/preprint-make-decision.component.spec.ts index e9ca1062d..1e76617aa 100644 --- a/src/app/features/preprints/components/preprint-details/make-decision/make-decision.component.spec.ts +++ b/src/app/features/preprints/components/preprint-details/preprint-make-decision/preprint-make-decision.component.spec.ts @@ -5,7 +5,7 @@ import { ProviderReviewsWorkflow, ReviewsState } from '@osf/features/preprints/e import { PreprintProviderDetails, PreprintRequest } from '@osf/features/preprints/models'; import { PreprintSelectors } from '@osf/features/preprints/store/preprint'; -import { MakeDecisionComponent } from './make-decision.component'; +import { PreprintMakeDecisionComponent } from './preprint-make-decision.component'; import { PREPRINT_MOCK } from '@testing/mocks/preprint.mock'; import { PREPRINT_PROVIDER_DETAILS_MOCK } from '@testing/mocks/preprint-provider-details'; @@ -14,9 +14,9 @@ import { REVIEW_ACTION_MOCK } from '@testing/mocks/review-action.mock'; import { OSFTestingModule } from '@testing/osf.testing.module'; import { provideMockStore } from '@testing/providers/store-provider.mock'; -describe('MakeDecisionComponent', () => { - let component: MakeDecisionComponent; - let fixture: ComponentFixture; +describe('PreprintMakeDecisionComponent', () => { + let component: PreprintMakeDecisionComponent; + let fixture: ComponentFixture; const mockPreprint = PREPRINT_MOCK; const mockProvider: PreprintProviderDetails = PREPRINT_PROVIDER_DETAILS_MOCK; @@ -25,7 +25,7 @@ describe('MakeDecisionComponent', () => { beforeEach(async () => { await TestBed.configureTestingModule({ - imports: [MakeDecisionComponent, OSFTestingModule], + imports: [PreprintMakeDecisionComponent, OSFTestingModule], providers: [ provideMockStore({ signals: [ @@ -38,7 +38,7 @@ describe('MakeDecisionComponent', () => { ], }).compileComponents(); - fixture = TestBed.createComponent(MakeDecisionComponent); + fixture = TestBed.createComponent(PreprintMakeDecisionComponent); component = fixture.componentInstance; fixture.componentRef.setInput('provider', mockProvider); diff --git a/src/app/features/preprints/components/preprint-details/make-decision/make-decision.component.ts b/src/app/features/preprints/components/preprint-details/preprint-make-decision/preprint-make-decision.component.ts similarity index 89% rename from src/app/features/preprints/components/preprint-details/make-decision/make-decision.component.ts rename to src/app/features/preprints/components/preprint-details/preprint-make-decision/preprint-make-decision.component.ts index 297cfc1ee..0bb81f382 100644 --- a/src/app/features/preprints/components/preprint-details/make-decision/make-decision.component.ts +++ b/src/app/features/preprints/components/preprint-details/preprint-make-decision/preprint-make-decision.component.ts @@ -23,16 +23,17 @@ import { SubmitRequestsDecision, SubmitReviewsDecision, } from '@osf/features/preprints/store/preprint'; -import { StringOrNull } from '@shared/helpers'; +import { InputLimits } from '@osf/shared/constants'; +import { StringOrNull } from '@osf/shared/helpers'; @Component({ - selector: 'osf-make-decision', + selector: 'osf-preprint-make-decision', imports: [Button, TranslatePipe, TitleCasePipe, Dialog, Tooltip, RadioButton, FormsModule, Textarea, Message], - templateUrl: './make-decision.component.html', - styleUrl: './make-decision.component.scss', + templateUrl: './preprint-make-decision.component.html', + styleUrl: './preprint-make-decision.component.scss', changeDetection: ChangeDetectionStrategy.OnPush, }) -export class MakeDecisionComponent { +export class PreprintMakeDecisionComponent { private readonly translateService = inject(TranslateService); private readonly router = inject(Router); private readonly actions = createDispatchMap({ @@ -55,6 +56,7 @@ export class MakeDecisionComponent { reviewerComment = signal(null); requestDecisionJustification = signal(null); saving = signal(false); + decisionCommentLimit = InputLimits.decisionComment.maxLength; labelDecisionButton = computed(() => { const preprint = this.preprint()!; @@ -102,9 +104,9 @@ export class MakeDecisionComponent { return 'preprints.details.decision.submitButton.modifyDecision'; }); - submitButtonDisabled = computed(() => { - return (!this.decisionChanged() && !this.commentEdited()) || this.commentExceedsLimit(); - }); + submitButtonDisabled = computed( + () => (!this.decisionChanged() && !this.commentEdited()) || this.commentExceedsLimit() + ); acceptOptionExplanation = computed(() => { const reviewsWorkflow = this.provider().reviewsWorkflow; @@ -146,9 +148,9 @@ export class MakeDecisionComponent { } }); - rejectRadioButtonValue = computed(() => { - return this.preprint()?.isPublished ? ReviewsState.Withdrawn : ReviewsState.Rejected; - }); + rejectRadioButtonValue = computed(() => + this.preprint()?.isPublished ? ReviewsState.Withdrawn : ReviewsState.Rejected + ); settingsComments = computed(() => { const commentType = this.provider().reviewsCommentsPrivate ? 'private' : 'public'; @@ -160,28 +162,25 @@ export class MakeDecisionComponent { return decisionSettings.names[commentType]; }); - settingsModeration = computed(() => { - return decisionSettings.moderation[this.provider().reviewsWorkflow || ProviderReviewsWorkflow.PreModeration]; - }); + settingsModeration = computed( + () => decisionSettings.moderation[this.provider().reviewsWorkflow || ProviderReviewsWorkflow.PreModeration] + ); - commentEdited = computed(() => { - return this.reviewerComment()?.trim() !== this.initialReviewerComment(); - }); + commentEdited = computed(() => this.reviewerComment()?.trim() !== this.initialReviewerComment()); commentExceedsLimit = computed(() => { const comment = this.reviewerComment(); if (!comment) return false; - return comment.length > formInputLimits.decisionComment.maxLength; + return comment.length > this.decisionCommentLimit; }); - commentLengthErrorMessage = computed(() => { - const limit = formInputLimits.decisionComment.maxLength; - return this.translateService.instant('preprints.details.decision.commentLengthError', { - limit, + commentLengthErrorMessage = computed(() => + this.translateService.instant('preprints.details.decision.commentLengthError', { + limit: this.decisionCommentLimit, length: this.reviewerComment()!.length, - }); - }); + }) + ); requestDecisionJustificationErrorMessage = computed(() => { const justification = this.requestDecisionJustification(); @@ -196,9 +195,7 @@ export class MakeDecisionComponent { return null; }); - decisionChanged = computed(() => { - return this.preprint()?.reviewsState !== this.decision(); - }); + decisionChanged = computed(() => this.preprint()?.reviewsState !== this.decision()); constructor() { effect(() => { diff --git a/src/app/features/preprints/constants/create-new-version-steps.const.ts b/src/app/features/preprints/constants/create-new-version-steps.const.ts index 2b50b9a01..2314ce425 100644 --- a/src/app/features/preprints/constants/create-new-version-steps.const.ts +++ b/src/app/features/preprints/constants/create-new-version-steps.const.ts @@ -1,5 +1,6 @@ -import { PreprintSteps } from '@osf/features/preprints/enums'; -import { StepOption } from '@shared/models'; +import { StepOption } from '@osf/shared/models'; + +import { PreprintSteps } from '../enums'; export const createNewVersionStepsConst: StepOption[] = [ { diff --git a/src/app/features/preprints/constants/form-input-limits.const.ts b/src/app/features/preprints/constants/form-input-limits.const.ts index 8961e773b..da425d9c0 100644 --- a/src/app/features/preprints/constants/form-input-limits.const.ts +++ b/src/app/features/preprints/constants/form-input-limits.const.ts @@ -12,9 +12,6 @@ export const formInputLimits = { withdrawalJustification: { minLength: 25, }, - decisionComment: { - maxLength: 10000, - }, requestDecisionJustification: { minLength: 20, }, diff --git a/src/app/features/preprints/constants/make-decision.const.ts b/src/app/features/preprints/constants/make-decision.const.ts index 04f1d3377..e1592d80f 100644 --- a/src/app/features/preprints/constants/make-decision.const.ts +++ b/src/app/features/preprints/constants/make-decision.const.ts @@ -1,4 +1,4 @@ -import { ProviderReviewsWorkflow } from '@osf/features/preprints/enums'; +import { ProviderReviewsWorkflow } from '../enums'; export const decisionSettings = { comments: { diff --git a/src/app/features/preprints/constants/prereg-link-options.const.ts b/src/app/features/preprints/constants/prereg-link-options.const.ts index 5a1130a2a..0c6cb4f07 100644 --- a/src/app/features/preprints/constants/prereg-link-options.const.ts +++ b/src/app/features/preprints/constants/prereg-link-options.const.ts @@ -1,5 +1,6 @@ -import { PreregLinkInfo } from '@osf/features/preprints/enums'; -import { SelectOption } from '@shared/models'; +import { SelectOption } from '@osf/shared/models'; + +import { PreregLinkInfo } from '../enums'; export const preregLinksOptions: SelectOption[] = [ { diff --git a/src/app/features/preprints/constants/status-banner.const.ts b/src/app/features/preprints/constants/status-banner.const.ts index fa87c962d..02bd3523f 100644 --- a/src/app/features/preprints/constants/status-banner.const.ts +++ b/src/app/features/preprints/constants/status-banner.const.ts @@ -1,4 +1,4 @@ -import { ProviderReviewsWorkflow, ReviewsState } from '@osf/features/preprints/enums'; +import { ProviderReviewsWorkflow, ReviewsState } from '../enums'; export type StatusSeverity = 'warn' | 'secondary' | 'success' | 'error'; diff --git a/src/app/features/preprints/constants/submit-preprint-steps.const.ts b/src/app/features/preprints/constants/submit-preprint-steps.const.ts index e7f30a7d2..978cc991e 100644 --- a/src/app/features/preprints/constants/submit-preprint-steps.const.ts +++ b/src/app/features/preprints/constants/submit-preprint-steps.const.ts @@ -1,5 +1,6 @@ -import { PreprintSteps } from '@osf/features/preprints/enums'; -import { StepOption } from '@shared/models'; +import { StepOption } from '@osf/shared/models'; + +import { PreprintSteps } from '../enums'; export const submitPreprintSteps: StepOption[] = [ { diff --git a/src/app/features/preprints/pages/preprint-details/preprint-details.component.html b/src/app/features/preprints/pages/preprint-details/preprint-details.component.html index 752a592e0..19110e3c9 100644 --- a/src/app/features/preprints/pages/preprint-details/preprint-details.component.html +++ b/src/app/features/preprints/pages/preprint-details/preprint-details.component.html @@ -30,7 +30,7 @@

{{ preprint()?.title }}

) { } @else { - { PreprintTombstoneComponent, PreprintWarningBannerComponent, ModerationStatusBannerComponent, - MakeDecisionComponent + PreprintMakeDecisionComponent ), ], providers: [ diff --git a/src/app/features/preprints/pages/preprint-details/preprint-details.component.ts b/src/app/features/preprints/pages/preprint-details/preprint-details.component.ts index 72d1437b9..a02279cac 100644 --- a/src/app/features/preprints/pages/preprint-details/preprint-details.component.ts +++ b/src/app/features/preprints/pages/preprint-details/preprint-details.component.ts @@ -30,9 +30,9 @@ import { UserSelectors } from '@core/store/user'; import { AdditionalInfoComponent, GeneralInformationComponent, - MakeDecisionComponent, ModerationStatusBannerComponent, PreprintFileSectionComponent, + PreprintMakeDecisionComponent, PreprintTombstoneComponent, ShareAndDownloadComponent, StatusBannerComponent, @@ -72,7 +72,7 @@ import { PreprintWarningBannerComponent } from '../../components/preprint-detail PreprintTombstoneComponent, PreprintWarningBannerComponent, ModerationStatusBannerComponent, - MakeDecisionComponent, + PreprintMakeDecisionComponent, RouterLink, ], templateUrl: './preprint-details.component.html', diff --git a/src/app/features/registry/components/registry-make-decision/registry-make-decision.component.html b/src/app/features/registry/components/registry-make-decision/registry-make-decision.component.html index 67a1aa5c9..e4cf93e20 100644 --- a/src/app/features/registry/components/registry-make-decision/registry-make-decision.component.html +++ b/src/app/features/registry/components/registry-make-decision/registry-make-decision.component.html @@ -71,6 +71,7 @@ [formControlName]="ModerationDecisionFormControls.Comment" [placeholder]="'moderation.makeDecision.remarksPlaceholder' | translate" [rows]="6" + [maxlength]="decisionCommentLimit" class="w-full mt-3" > @@ -94,6 +95,7 @@ [formControlName]="ModerationDecisionFormControls.Comment" [placeholder]="'moderation.makeDecision.justificationPlaceholder' | translate" [rows]="6" + [maxlength]="decisionCommentLimit" class="w-full mt-3" > @if (isCommentInvalid) { diff --git a/src/app/features/registry/components/registry-make-decision/registry-make-decision.component.ts b/src/app/features/registry/components/registry-make-decision/registry-make-decision.component.ts index 962884eb7..028ffa539 100644 --- a/src/app/features/registry/components/registry-make-decision/registry-make-decision.component.ts +++ b/src/app/features/registry/components/registry-make-decision/registry-make-decision.component.ts @@ -16,7 +16,7 @@ import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; import { FormBuilder, FormControl, FormGroup, FormsModule, ReactiveFormsModule, Validators } from '@angular/forms'; import { SubmissionReviewStatus } from '@osf/features/moderation/enums'; -import { INPUT_VALIDATION_MESSAGES } from '@osf/shared/constants'; +import { INPUT_VALIDATION_MESSAGES, InputLimits } from '@osf/shared/constants'; import { ModerationDecisionFormControls, RegistrationReviewStates, @@ -68,6 +68,7 @@ export class RegistryMakeDecisionComponent { RevisionReviewStates = RevisionReviewStates; RegistrationReviewStates = RegistrationReviewStates; + decisionCommentLimit = InputLimits.decisionComment.maxLength; readonly INPUT_VALIDATION_MESSAGES = INPUT_VALIDATION_MESSAGES; get isPendingModeration(): boolean { @@ -165,7 +166,7 @@ export class RegistryMakeDecisionComponent { private initForm(): void { this.requestForm = this.fb.group({ [ModerationDecisionFormControls.Action]: new FormControl('', [Validators.required]), - [ModerationDecisionFormControls.Comment]: new FormControl(''), + [ModerationDecisionFormControls.Comment]: new FormControl('', [Validators.maxLength(this.decisionCommentLimit)]), }); } } diff --git a/src/app/shared/components/make-decision-dialog/make-decision-dialog.component.html b/src/app/shared/components/make-decision-dialog/make-decision-dialog.component.html index f29234a67..796a980c0 100644 --- a/src/app/shared/components/make-decision-dialog/make-decision-dialog.component.html +++ b/src/app/shared/components/make-decision-dialog/make-decision-dialog.component.html @@ -65,6 +65,7 @@ [formControlName]="ModerationDecisionFormControls.Comment" [placeholder]="'moderation.makeDecision.remarksPlaceholder' | translate" [rows]="6" + [maxlength]="decisionCommentLimit" class="w-full mt-3" > diff --git a/src/app/shared/components/make-decision-dialog/make-decision-dialog.component.ts b/src/app/shared/components/make-decision-dialog/make-decision-dialog.component.ts index cae45d2b0..62d6ab181 100644 --- a/src/app/shared/components/make-decision-dialog/make-decision-dialog.component.ts +++ b/src/app/shared/components/make-decision-dialog/make-decision-dialog.component.ts @@ -15,6 +15,7 @@ import { CollectionsModerationSelectors, CreateCollectionSubmissionAction, } from '@osf/features/moderation/store/collections-moderation'; +import { InputLimits } from '@osf/shared/constants'; import { ModerationDecisionFormControls, ModerationSubmitType } from '@osf/shared/enums'; import { DateAgoPipe } from '@osf/shared/pipes'; import { CollectionsSelectors } from '@osf/shared/stores'; @@ -39,6 +40,8 @@ export class MakeDecisionDialogComponent implements OnInit { isSubmitting = select(CollectionsModerationSelectors.getCollectionSubmissionSubmitting); requestForm!: FormGroup; + decisionCommentLimit = InputLimits.decisionComment.maxLength; + actions = createDispatchMap({ createSubmissionAction: CreateCollectionSubmissionAction, }); @@ -98,7 +101,7 @@ export class MakeDecisionDialogComponent implements OnInit { private initForm(): void { this.requestForm = this.fb.group({ [ModerationDecisionFormControls.Action]: new FormControl('', [Validators.required]), - [ModerationDecisionFormControls.Comment]: new FormControl(''), + [ModerationDecisionFormControls.Comment]: new FormControl('', [Validators.maxLength(this.decisionCommentLimit)]), }); } } diff --git a/src/app/shared/constants/input-limits.const.ts b/src/app/shared/constants/input-limits.const.ts index bfcd01c9f..bc96297a0 100644 --- a/src/app/shared/constants/input-limits.const.ts +++ b/src/app/shared/constants/input-limits.const.ts @@ -35,6 +35,9 @@ export const InputLimits = { minLength: 2, maxLength: 512, }, + decisionComment: { + maxLength: 10000, + }, }; export const forbiddenFileNameCharacters = /[()<>~!@$&*#%^:;,"'\\|/?]/; diff --git a/src/assets/i18n/en.json b/src/assets/i18n/en.json index bb773b77a..5383228fe 100644 --- a/src/assets/i18n/en.json +++ b/src/assets/i18n/en.json @@ -2415,7 +2415,7 @@ "label": "Withdraw submission", "post": "Submission will no longer be publicly available." }, - "commentPlaceholder": "Explain the reasoning behind your decision (optional)", + "commentPlaceholder": "Explain the reasoning behind your decision", "commentLengthError": "Comment is {{length}} character(s) too long (maximum is {{limit}}).", "withdrawalJustification": "Reason for withdrawal (optional, will be publicly displayed)", "denialJustification": "Reason for denial (required, not publicly visible)",