From 3203f81cfb92be69388989b520b2fa491c6f53f7 Mon Sep 17 00:00:00 2001 From: NazarMykhalkevych Date: Wed, 10 Sep 2025 16:32:18 +0300 Subject: [PATCH 01/10] fix(registration): fixed 594 --- .../registries-landing/registries-landing.component.ts | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/app/features/registries/pages/registries-landing/registries-landing.component.ts b/src/app/features/registries/pages/registries-landing/registries-landing.component.ts index 9e79c0402..dd70711cb 100644 --- a/src/app/features/registries/pages/registries-landing/registries-landing.component.ts +++ b/src/app/features/registries/pages/registries-landing/registries-landing.component.ts @@ -8,6 +8,8 @@ import { ChangeDetectionStrategy, Component, inject, OnInit } from '@angular/cor import { FormControl } from '@angular/forms'; import { Router } from '@angular/router'; +import { AuthService } from '@core/services'; +import { UserSelectors } from '@core/store/user'; import { RegistryServicesComponent } from '@osf/features/registries/components'; import { GetRegistries, RegistriesSelectors } from '@osf/features/registries/store'; import { @@ -37,6 +39,8 @@ import { environment } from 'src/environments/environment'; }) export class RegistriesLandingComponent implements OnInit { private router = inject(Router); + private readonly isAuthenticated = select(UserSelectors.isAuthenticated); + private readonly authService = inject(AuthService); searchControl = new FormControl(''); @@ -66,6 +70,10 @@ export class RegistriesLandingComponent implements OnInit { } goToCreateRegistration(): void { - this.router.navigate([`/registries/${environment.defaultProvider}/new`]); + if (this.isAuthenticated()) { + this.router.navigate([`/registries/${environment.defaultProvider}/new`]); + } else { + this.authService.navigateToSignIn(); + } } } From de8debf0ed84558d9c0cce736782418117b98e23 Mon Sep 17 00:00:00 2001 From: NazarMykhalkevych Date: Wed, 10 Sep 2025 22:04:15 +0300 Subject: [PATCH 02/10] fix(registration-bugs): fixed some registrations bugs --- .../file-detail/file-detail.component.ts | 4 +-- src/app/features/files/store/files.actions.ts | 5 +--- src/app/features/files/store/files.state.ts | 2 +- .../add-metadata/add-metadata.component.ts | 6 +++- ...confirm-registration-dialog.component.html | 1 + .../custom-step/custom-step.component.html | 9 ++++-- .../metadata/metadata.component.html | 3 +- .../registries-license.component.html | 1 + .../new-registration.component.html | 9 +++++- .../new-registration.component.scss | 1 + .../new-registration.component.ts | 23 +++++++++++++-- .../components/review/review.component.html | 11 ++++---- .../registries/services/projects.service.ts | 28 ------------------- .../store/handlers/projects.handlers.ts | 16 +++++++---- .../registries/store/registries.actions.ts | 5 +++- .../registries/store/registries.state.ts | 4 +-- .../generic-filter.component.html | 2 +- .../components/license/license.component.html | 3 +- .../sub-header/sub-header.component.html | 2 +- .../sub-header/sub-header.component.scss | 1 + .../truncated-text.component.ts | 10 ++++++- src/app/shared/services/files.service.ts | 4 +-- src/styles/_fonts.scss | 4 +++ 23 files changed, 91 insertions(+), 63 deletions(-) delete mode 100644 src/app/features/registries/services/projects.service.ts diff --git a/src/app/features/files/pages/file-detail/file-detail.component.ts b/src/app/features/files/pages/file-detail/file-detail.component.ts index 40ff066d9..d4c60db1a 100644 --- a/src/app/features/files/pages/file-detail/file-detail.component.ts +++ b/src/app/features/files/pages/file-detail/file-detail.component.ts @@ -183,8 +183,8 @@ export class FileDetailComponent { this.actions.getFileResourceMetadata(this.resourceId, this.resourceType); this.actions.getFileResourceContributors(this.resourceId, this.resourceType); if (fileId) { - const storageLink = this.file()?.links.download || ''; - this.actions.getFileRevisions(storageLink, fileId); + const storageLink = this.file()?.links.upload || ''; + this.actions.getFileRevisions(storageLink); this.actions.getCedarTemplates(); this.actions.getCedarRecords(fileId, ResourceType.File); } diff --git a/src/app/features/files/store/files.actions.ts b/src/app/features/files/store/files.actions.ts index 96ef709fa..a6fc56f71 100644 --- a/src/app/features/files/store/files.actions.ts +++ b/src/app/features/files/store/files.actions.ts @@ -114,10 +114,7 @@ export class SetFileMetadata { export class GetFileRevisions { static readonly type = '[Files] Get Revisions'; - constructor( - public link: string, - public fileId: string - ) {} + constructor(public link: string) {} } export class UpdateTags { diff --git a/src/app/features/files/store/files.state.ts b/src/app/features/files/store/files.state.ts index d0e977eb2..d9e572374 100644 --- a/src/app/features/files/store/files.state.ts +++ b/src/app/features/files/store/files.state.ts @@ -257,7 +257,7 @@ export class FilesState { const state = ctx.getState(); ctx.patchState({ fileRevisions: { ...state.fileRevisions, isLoading: true, error: null } }); - return this.filesService.getFileRevisions(action.link, action.fileId).pipe( + return this.filesService.getFileRevisions(action.link).pipe( tap({ next: (revisions) => { ctx.patchState({ fileRevisions: { data: revisions, isLoading: false, error: null } }); diff --git a/src/app/features/metadata/pages/add-metadata/add-metadata.component.ts b/src/app/features/metadata/pages/add-metadata/add-metadata.component.ts index 0c9b31a1f..c27330578 100644 --- a/src/app/features/metadata/pages/add-metadata/add-metadata.component.ts +++ b/src/app/features/metadata/pages/add-metadata/add-metadata.component.ts @@ -123,7 +123,11 @@ export class AddMetadataComponent implements OnInit { if (templates?.links?.first && templates?.links?.last && templates.links.first !== templates.links.last) { this.actions.getCedarTemplates(); } else { - this.router.navigate(['..'], { relativeTo: this.activatedRoute }); + if (this.resourceType() === ResourceType.File) { + this.router.navigate([this.resourceId]); + } else { + this.router.navigate(['..'], { relativeTo: this.activatedRoute }); + } } } diff --git a/src/app/features/registries/components/confirm-registration-dialog/confirm-registration-dialog.component.html b/src/app/features/registries/components/confirm-registration-dialog/confirm-registration-dialog.component.html index 9379b23c6..f4df946e2 100644 --- a/src/app/features/registries/components/confirm-registration-dialog/confirm-registration-dialog.component.html +++ b/src/app/features/registries/components/confirm-registration-dialog/confirm-registration-dialog.component.html @@ -58,6 +58,7 @@ [disabled]="isRegistrationSubmitting()" /> (keyup.enter)="inplaceRef.deactivate()" (keyup.space)="inplaceRef.deactivate()" > - @@ -207,6 +207,11 @@

{{ 'files.actions.uploadFile' | translate }}

class="mr-2" (click)="goBack()" >
- + diff --git a/src/app/features/registries/components/metadata/metadata.component.html b/src/app/features/registries/components/metadata/metadata.component.html index c877f627a..a3bb08450 100644 --- a/src/app/features/registries/components/metadata/metadata.component.html +++ b/src/app/features/registries/components/metadata/metadata.component.html @@ -13,6 +13,7 @@

{{ 'registries.metadata.title' | translate }}

- - + +
diff --git a/src/app/features/metadata/dialogs/funding-dialog/funding-dialog.component.html b/src/app/features/metadata/dialogs/funding-dialog/funding-dialog.component.html index 33a353b0b..734741348 100644 --- a/src/app/features/metadata/dialogs/funding-dialog/funding-dialog.component.html +++ b/src/app/features/metadata/dialogs/funding-dialog/funding-dialog.component.html @@ -47,7 +47,7 @@
- @if (fundingEntries.length > 1) { + @if (configFunders.length || fundingEntries.length > 1) {
- +
diff --git a/src/app/features/metadata/dialogs/funding-dialog/funding-dialog.component.ts b/src/app/features/metadata/dialogs/funding-dialog/funding-dialog.component.ts index da758e84a..b0d97c15d 100644 --- a/src/app/features/metadata/dialogs/funding-dialog/funding-dialog.component.ts +++ b/src/app/features/metadata/dialogs/funding-dialog/funding-dialog.component.ts @@ -39,6 +39,8 @@ export class FundingDialogComponent implements OnInit { private searchSubject = new Subject(); + configFunders = this.config.data?.funders; + constructor() { effect(() => { const funders = this.fundersList() || []; @@ -66,9 +68,8 @@ export class FundingDialogComponent implements OnInit { ngOnInit(): void { this.actions.getFundersList(); - const configFunders = this.config.data?.funders; - if (configFunders?.length > 0) { - configFunders.forEach((funder: Funder) => { + if (this.configFunders?.length > 0) { + this.configFunders.forEach((funder: Funder) => { this.addFundingEntry({ funderName: funder.funderName || '', funderIdentifier: funder.funderIdentifier || '', @@ -104,11 +105,10 @@ export class FundingDialogComponent implements OnInit { }), awardTitle: new FormControl(supplement?.title || supplement?.awardTitle || '', { nonNullable: true, - validators: [Validators.required], }), awardUri: new FormControl(supplement?.url || supplement?.awardUri || '', { nonNullable: true, - validators: [CustomValidators.linkValidator(), CustomValidators.requiredTrimmed()], + validators: [CustomValidators.linkValidator()], }), awardNumber: new FormControl(supplement?.awardNumber || '', { nonNullable: true, @@ -124,6 +124,11 @@ export class FundingDialogComponent implements OnInit { removeFundingEntry(index: number): void { if (this.fundingEntries.length > 1) { this.fundingEntries.removeAt(index); + } else { + const result: FundingDialogResult = { + fundingEntries: [], + }; + this.dialogRef.close(result); } } diff --git a/src/app/features/metadata/dialogs/resource-information-dialog/resource-information-dialog.component.html b/src/app/features/metadata/dialogs/resource-information-dialog/resource-information-dialog.component.html index d5a9d26f5..9882a7e53 100644 --- a/src/app/features/metadata/dialogs/resource-information-dialog/resource-information-dialog.component.html +++ b/src/app/features/metadata/dialogs/resource-information-dialog/resource-information-dialog.component.html @@ -31,10 +31,12 @@ optionValue="value" [placeholder]="'common.buttons.select' | translate" appendTo="body" + [virtualScroll]="true" + [virtualScrollItemSize]="35" class="w-full" > - {{ option.label }} + {{ option.label }} diff --git a/src/styles/overrides/button.scss b/src/styles/overrides/button.scss index 09fd79b4c..d2cf4ebb3 100644 --- a/src/styles/overrides/button.scss +++ b/src/styles/overrides/button.scss @@ -31,7 +31,7 @@ } .btn-icon-only .p-button { - padding: mix.rem(13px) mix.rem(20px); + padding: mix.rem(13px) mix.rem(21px); } .btn-full-width { From 3ef0b52387750501578bea989cdd9a40cc4f21bf Mon Sep 17 00:00:00 2001 From: NazarMykhalkevych Date: Fri, 12 Sep 2025 14:32:38 +0300 Subject: [PATCH 08/10] fix(bugs): add some area labels --- .../pages/file-detail/file-detail.component.html | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/app/features/files/pages/file-detail/file-detail.component.html b/src/app/features/files/pages/file-detail/file-detail.component.html index ffc24acfc..05c85e754 100644 --- a/src/app/features/files/pages/file-detail/file-detail.component.html +++ b/src/app/features/files/pages/file-detail/file-detail.component.html @@ -31,6 +31,7 @@ severity="secondary" class="btn-icon-only" icon="fas fa-download" + [ariaLabel]="'common.buttons.download' | translate" (click)="downloadFile(file()?.links?.download!)" /> } @@ -40,6 +41,7 @@ severity="secondary" class="btn-icon-only" icon="fas fa-file-import" + [ariaLabel]="'common.buttons.embed' | translate" (click)="embedMenu.toggle($event)" /> @@ -56,6 +58,7 @@ severity="secondary" class="btn-icon-only" icon="fas fa-share-nodes" + [ariaLabel]="'common.buttons.share' | translate" (click)="shareMenu.toggle($event)" /> @@ -67,7 +70,13 @@ } @if (file() && !isAnonymous()) { - + } From 26ad3c68c6f350fed31987ecc949b7debeb7bcb4 Mon Sep 17 00:00:00 2001 From: NazarMykhalkevych Date: Fri, 12 Sep 2025 16:00:45 +0300 Subject: [PATCH 09/10] fix(bugs): files upload limits --- .../components/custom-step/custom-step.component.ts | 9 ++++++++- .../files-control/files-control.component.html | 2 +- .../components/files-control/files-control.component.ts | 8 +++++++- .../components/metadata/metadata.component.html | 3 ++- src/app/shared/components/stepper/stepper.component.html | 1 + .../shared/components/subjects/subjects.component.html | 1 + .../components/text-input/text-input.component.html | 4 ++-- .../shared/components/text-input/text-input.component.ts | 2 +- src/app/shared/constants/files-limits.const.ts | 2 ++ src/app/shared/constants/index.ts | 1 + src/app/shared/services/toast.service.ts | 2 +- src/assets/i18n/en.json | 3 ++- 12 files changed, 29 insertions(+), 9 deletions(-) create mode 100644 src/app/shared/constants/files-limits.const.ts diff --git a/src/app/features/registries/components/custom-step/custom-step.component.ts b/src/app/features/registries/components/custom-step/custom-step.component.ts index 59721646c..e32768270 100644 --- a/src/app/features/registries/components/custom-step/custom-step.component.ts +++ b/src/app/features/registries/components/custom-step/custom-step.component.ts @@ -29,10 +29,11 @@ import { FormBuilder, FormControl, FormGroup, FormsModule, ReactiveFormsModule, import { ActivatedRoute, Router } from '@angular/router'; import { InfoIconComponent } from '@osf/shared/components'; -import { INPUT_VALIDATION_MESSAGES } from '@osf/shared/constants'; +import { FILE_COUNT_ATTACHMENTS_LIMIT, INPUT_VALIDATION_MESSAGES } from '@osf/shared/constants'; import { FieldType } from '@osf/shared/enums'; import { CustomValidators, findChangedFields } from '@osf/shared/helpers'; import { FilePayloadJsonApi, OsfFile, PageSchema } from '@osf/shared/models'; +import { ToastService } from '@osf/shared/services'; import { FilesMapper } from '../../mappers/files.mapper'; import { RegistriesSelectors, SetUpdatedFields, UpdateStepValidation } from '../../store'; @@ -77,6 +78,7 @@ export class CustomStepComponent implements OnDestroy { private readonly route = inject(ActivatedRoute); private readonly router = inject(Router); private readonly fb = inject(FormBuilder); + private toastService = inject(ToastService); readonly pages = select(RegistriesSelectors.getPagesSchema); readonly FieldType = FieldType; @@ -180,7 +182,12 @@ export class CustomStepComponent implements OnDestroy { onAttachFile(file: OsfFile, questionKey: string): void { this.attachedFiles[questionKey] = this.attachedFiles[questionKey] || []; + if (!this.attachedFiles[questionKey].some((f) => f.file_id === file.id)) { + if (this.attachedFiles[questionKey].length >= FILE_COUNT_ATTACHMENTS_LIMIT) { + this.toastService.showWarn('shared.files.limitText'); + return; + } this.attachedFiles[questionKey].push(file); this.stepForm.patchValue({ [questionKey]: [...(this.attachedFiles[questionKey] || []), file], diff --git a/src/app/features/registries/components/files-control/files-control.component.html b/src/app/features/registries/components/files-control/files-control.component.html index ea73def20..884f72428 100644 --- a/src/app/features/registries/components/files-control/files-control.component.html +++ b/src/app/features/registries/components/files-control/files-control.component.html @@ -58,7 +58,7 @@ [isLoading]="isFilesLoading()" [actions]="filesTreeActions" [viewOnly]="filesViewOnly()" - [viewOnlyDownloadable]="false" + [viewOnlyDownloadable]="true" [resourceId]="projectId()" [provider]="provider()" (folderIsOpening)="folderIsOpening($event)" diff --git a/src/app/features/registries/components/files-control/files-control.component.ts b/src/app/features/registries/components/files-control/files-control.component.ts index 980a2763e..6bff47341 100644 --- a/src/app/features/registries/components/files-control/files-control.component.ts +++ b/src/app/features/registries/components/files-control/files-control.component.ts @@ -16,8 +16,9 @@ import { FormsModule, ReactiveFormsModule } from '@angular/forms'; import { CreateFolderDialogComponent } from '@osf/features/files/components'; import { FilesTreeComponent, LoadingSpinnerComponent } from '@osf/shared/components'; +import { FILE_SIZE_LIMIT } from '@osf/shared/constants'; import { FilesTreeActions, OsfFile } from '@osf/shared/models'; -import { FilesService } from '@osf/shared/services'; +import { FilesService, ToastService } from '@osf/shared/services'; import { CreateFolder, @@ -57,6 +58,7 @@ export class FilesControlComponent { private readonly dialogService = inject(DialogService); private readonly translateService = inject(TranslateService); private readonly destroyRef = inject(DestroyRef); + private toastService = inject(ToastService); readonly files = select(RegistriesSelectors.getFiles); readonly filesTotalCount = select(RegistriesSelectors.getFilesTotalCount); @@ -103,6 +105,10 @@ export class FilesControlComponent { onFileSelected(event: Event): void { const input = event.target as HTMLInputElement; const file = input.files?.[0]; + if (file && file.size > FILE_SIZE_LIMIT) { + this.toastService.showWarn('shared.files.limitText'); + return; + } if (!file) return; this.uploadFile(file); diff --git a/src/app/features/registries/components/metadata/metadata.component.html b/src/app/features/registries/components/metadata/metadata.component.html index 620c66986..75d6a4897 100644 --- a/src/app/features/registries/components/metadata/metadata.component.html +++ b/src/app/features/registries/components/metadata/metadata.component.html @@ -11,7 +11,7 @@

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

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

@@ -33,6 +33,7 @@

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

rows="5" cols="30" pTextarea + [ariaLabel]="'common.labels.description' | translate" > @if ( metadataForm.controls['description'].errors?.['required'] && diff --git a/src/app/shared/components/stepper/stepper.component.html b/src/app/shared/components/stepper/stepper.component.html index 7089f1300..6e5549af5 100644 --- a/src/app/shared/components/stepper/stepper.component.html +++ b/src/app/shared/components/stepper/stepper.component.html @@ -9,6 +9,7 @@ [class.invalid]="step.invalid" [class.current]="i === currentStep().index" (click)="onStepClick(step)" + [attr.aria-label]="step.label | translate" > @if (step.invalid && i !== currentStep().index) { diff --git a/src/app/shared/components/subjects/subjects.component.html b/src/app/shared/components/subjects/subjects.component.html index e02642187..ccd35c589 100644 --- a/src/app/shared/components/subjects/subjects.component.html +++ b/src/app/shared/components/subjects/subjects.component.html @@ -67,6 +67,7 @@

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

(onNodeSelect)="selectSubject($event.node.data)" (onNodeUnselect)="removeSubject($event.node.data)" [ariaLabel]="'shared.subjects.subjectTree' | translate" + [togglerAriaLabel]="'common.accessibility.toggleTreeNode' | translate" > } diff --git a/src/app/shared/components/text-input/text-input.component.html b/src/app/shared/components/text-input/text-input.component.html index 59f592760..23fdb6139 100644 --- a/src/app/shared/components/text-input/text-input.component.html +++ b/src/app/shared/components/text-input/text-input.component.html @@ -1,8 +1,8 @@ @if (label()) { - + } (); maxLength = input(); - inputId = `input-${Math.random().toString(36).substring(2, 15)}`; + inputId = input(`input-${Math.random().toString(36).substring(2, 15)}`); helpId = `help-${Math.random().toString(36).substring(2, 15)}`; getErrorMessage(): ValidationParams { diff --git a/src/app/shared/constants/files-limits.const.ts b/src/app/shared/constants/files-limits.const.ts new file mode 100644 index 000000000..0f8156270 --- /dev/null +++ b/src/app/shared/constants/files-limits.const.ts @@ -0,0 +1,2 @@ +export const FILE_SIZE_LIMIT = 5 * 1024 * 1024 * 1024; +export const FILE_COUNT_ATTACHMENTS_LIMIT = 1; diff --git a/src/app/shared/constants/index.ts b/src/app/shared/constants/index.ts index 6d9b00431..e8aadc9a5 100644 --- a/src/app/shared/constants/index.ts +++ b/src/app/shared/constants/index.ts @@ -4,6 +4,7 @@ export * from './addons-tab-options.const'; export * from './contributors.constants'; export * from './default-citation-titles.const'; export * from './default-table-params.constants'; +export * from './files-limits.const'; export * from './filter-placeholders'; export * from './input-limits.const'; export * from './input-validation-messages.const'; diff --git a/src/app/shared/services/toast.service.ts b/src/app/shared/services/toast.service.ts index da6ac62e1..dae505bee 100644 --- a/src/app/shared/services/toast.service.ts +++ b/src/app/shared/services/toast.service.ts @@ -13,7 +13,7 @@ export class ToastService { } showWarn(summary: string, params?: unknown) { - this.messageService.add({ severity: 'warn', summary, data: { translationParams: params }, key: 'osf' }); + this.messageService.add({ severity: 'warn', summary, life: 5000, data: { translationParams: params }, key: 'osf' }); } showError(summary: string, params?: unknown) { diff --git a/src/assets/i18n/en.json b/src/assets/i18n/en.json index d64c6ed28..8e549b5d0 100644 --- a/src/assets/i18n/en.json +++ b/src/assets/i18n/en.json @@ -64,7 +64,8 @@ "tooltipBtn": "Tooltip button", "customizeOptions": "Customize options", "toggleProjectVisibility": "Toggle project visibility", - "tagInput": "Tag input" + "tagInput": "Tag input", + "toggleTreeNode": "Toggle tree node" }, "dialogs": { "confirmation": "Confirmation" From 5391f086002bead22f53113bcc5a1034d2c4925b Mon Sep 17 00:00:00 2001 From: NazarMykhalkevych Date: Wed, 17 Sep 2025 23:03:22 +0300 Subject: [PATCH 10/10] fix(registration): show data for admin --- .../components/custom-step/custom-step.component.html | 8 +------- .../registry-overview/registry-overview.component.ts | 3 ++- 2 files changed, 3 insertions(+), 8 deletions(-) diff --git a/src/app/features/registries/components/custom-step/custom-step.component.html b/src/app/features/registries/components/custom-step/custom-step.component.html index e9ef298df..4e54d6ead 100644 --- a/src/app/features/registries/components/custom-step/custom-step.component.html +++ b/src/app/features/registries/components/custom-step/custom-step.component.html @@ -139,13 +139,7 @@

} @case (FieldType.Text) { - + @if ( stepForm.controls[q.responseKey!].errors?.['required'] && (stepForm.controls[q.responseKey!].touched || stepForm.controls[q.responseKey!].dirty) diff --git a/src/app/features/registry/pages/registry-overview/registry-overview.component.ts b/src/app/features/registry/pages/registry-overview/registry-overview.component.ts index a6598507a..87011a718 100644 --- a/src/app/features/registry/pages/registry-overview/registry-overview.component.ts +++ b/src/app/features/registry/pages/registry-overview/registry-overview.component.ts @@ -113,7 +113,8 @@ export class RegistryOverviewComponent { const schemaResponses = (this.isModeration ? registry?.schemaResponses - : registry?.schemaResponses.filter((r) => r.reviewsState === RevisionReviewStates.Approved)) || []; + : registry?.schemaResponses.filter((r) => r.reviewsState === RevisionReviewStates.Approved || this.isAdmin)) || + []; if (index !== null) { return schemaResponses[index]; }