diff --git a/package-lock.json b/package-lock.json index dfe544253..ac558ab70 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "osf", - "version": "26.7.0", + "version": "26.9.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "osf", - "version": "26.7.0", + "version": "26.9.1", "dependencies": { "@angular/animations": "^21.2.7", "@angular/cdk": "^21.2.6", diff --git a/src/app/features/files/components/edit-file-metadata-dialog/edit-file-metadata-dialog.component.ts b/src/app/features/files/components/edit-file-metadata-dialog/edit-file-metadata-dialog.component.ts index a47be2ff6..b1207e692 100644 --- a/src/app/features/files/components/edit-file-metadata-dialog/edit-file-metadata-dialog.component.ts +++ b/src/app/features/files/components/edit-file-metadata-dialog/edit-file-metadata-dialog.component.ts @@ -8,7 +8,7 @@ import { Select } from 'primeng/select'; import { ChangeDetectionStrategy, Component, inject } from '@angular/core'; import { FormControl, FormGroup, ReactiveFormsModule } from '@angular/forms'; -import { languageCodes } from '@osf/shared/constants/language.const'; +import { LANGUAGE_CODES } from '@osf/shared/constants/language.const'; import { resourceTypes } from '@osf/shared/constants/resource-types.const'; import { OsfFileCustomMetadata, PatchFileMetadata } from '../../models'; @@ -22,7 +22,7 @@ import { OsfFileCustomMetadata, PatchFileMetadata } from '../../models'; }) export class EditFileMetadataDialogComponent { readonly resourceTypes = resourceTypes; - readonly languages = languageCodes; + readonly languages = LANGUAGE_CODES; private readonly dialogRef = inject(DynamicDialogRef); readonly config = inject(DynamicDialogConfig); diff --git a/src/app/features/files/components/file-metadata/file-metadata.component.spec.ts b/src/app/features/files/components/file-metadata/file-metadata.component.spec.ts index e278aa6f0..3a3452604 100644 --- a/src/app/features/files/components/file-metadata/file-metadata.component.spec.ts +++ b/src/app/features/files/components/file-metadata/file-metadata.component.spec.ts @@ -3,7 +3,7 @@ import { MockProvider } from 'ng-mocks'; import { ComponentFixture, TestBed } from '@angular/core/testing'; import { ActivatedRoute, Router } from '@angular/router'; -import { languageCodes } from '@osf/shared/constants/language.const'; +import { LANGUAGE_CODES } from '@osf/shared/constants/language.const'; import { CustomDialogService } from '@osf/shared/services/custom-dialog.service'; import { provideOSFCore } from '@testing/osf.testing.provider'; @@ -64,7 +64,7 @@ describe('FileMetadataComponent', () => { expect(component.fileMetadata).toBeDefined(); expect(component.isLoading).toBeDefined(); expect(component.hasWriteAccess).toBeDefined(); - expect(component.languageCodes).toBe(languageCodes); + expect(component.languageCodes).toBe(LANGUAGE_CODES); expect(component.metadataFields).toBe(FileMetadataFields); }); diff --git a/src/app/features/files/components/file-metadata/file-metadata.component.ts b/src/app/features/files/components/file-metadata/file-metadata.component.ts index 2b9f22052..3838dc86a 100644 --- a/src/app/features/files/components/file-metadata/file-metadata.component.ts +++ b/src/app/features/files/components/file-metadata/file-metadata.component.ts @@ -12,7 +12,7 @@ import { toSignal } from '@angular/core/rxjs-interop'; import { ActivatedRoute, Router } from '@angular/router'; import { ENVIRONMENT } from '@core/provider/environment.provider'; -import { languageCodes } from '@osf/shared/constants/language.const'; +import { LANGUAGE_CODES } from '@osf/shared/constants/language.const'; import { CustomDialogService } from '@osf/shared/services/custom-dialog.service'; import { ViewOnlyLinkHelperService } from '@osf/shared/services/view-only-link-helper.service'; import { LanguageCodeModel } from '@shared/models/language-code.model'; @@ -44,7 +44,7 @@ export class FileMetadataComponent { hasViewOnly = computed(() => this.viewOnlyService.hasViewOnlyParam(this.router)); - readonly languageCodes = languageCodes; + readonly languageCodes = LANGUAGE_CODES; readonly fileGuid = toSignal(this.route.params.pipe(map((params) => params['fileGuid']))); diff --git a/src/app/features/metadata/components/metadata-resource-information/metadata-resource-information.component.html b/src/app/features/metadata/components/metadata-resource-information/metadata-resource-information.component.html index 6f00278b7..9b2e95482 100644 --- a/src/app/features/metadata/components/metadata-resource-information/metadata-resource-information.component.html +++ b/src/app/features/metadata/components/metadata-resource-information/metadata-resource-information.component.html @@ -26,12 +26,12 @@

{{ 'project.overview.metadata.resourceType' | translate }}: - {{ getResourceTypeName(customItemMetadata()?.resourceTypeGeneral!) }} + {{ customItemMetadata()?.resourceTypeGeneral | resourceTypeGeneralLabel }}

{{ 'project.overview.metadata.resourceLanguage' | translate }}: - {{ getLanguageName(customItemMetadata()?.language || '') }} + {{ customItemMetadata()?.language | languageLabel }}

} @else { diff --git a/src/app/features/metadata/components/metadata-resource-information/metadata-resource-information.component.ts b/src/app/features/metadata/components/metadata-resource-information/metadata-resource-information.component.ts index b5a9cb0de..4659ab242 100644 --- a/src/app/features/metadata/components/metadata-resource-information/metadata-resource-information.component.ts +++ b/src/app/features/metadata/components/metadata-resource-information/metadata-resource-information.component.ts @@ -5,14 +5,13 @@ import { Card } from 'primeng/card'; import { ChangeDetectionStrategy, Component, input, output } from '@angular/core'; -import { RESOURCE_TYPE_OPTIONS } from '@osf/features/metadata/constants'; import { CustomItemMetadataRecord } from '@osf/features/metadata/models'; -import { languageCodes } from '@osf/shared/constants/language.const'; -import { LanguageCodeModel } from '@shared/models/language-code.model'; +import { LanguageLabelPipe } from '@osf/shared/pipes/language-label.pipe'; +import { ResourceTypeGeneralLabelPipe } from '@osf/shared/pipes/resource-type-general-label.pipe'; @Component({ selector: 'osf-metadata-resource-information', - imports: [Button, Card, TranslatePipe], + imports: [Button, Card, TranslatePipe, LanguageLabelPipe, ResourceTypeGeneralLabelPipe], templateUrl: './metadata-resource-information.component.html', changeDetection: ChangeDetectionStrategy.OnPush, }) @@ -22,17 +21,4 @@ export class MetadataResourceInformationComponent { customItemMetadata = input.required(); readonly = input(false); showResourceInfo = output(); - - readonly languageCodes = languageCodes; - readonly resourceTypes = RESOURCE_TYPE_OPTIONS; - - getLanguageName(languageCode: string): string { - const language = this.languageCodes.find((lang: LanguageCodeModel) => lang.code === languageCode); - return language ? language.name : languageCode; - } - - getResourceTypeName(resourceType: string): string { - const resource = this.resourceTypes.find((res) => res.value === resourceType); - return resource ? resource.label : resourceType; - } } diff --git a/src/app/features/metadata/constants/index.ts b/src/app/features/metadata/constants/index.ts index ea28ffd12..7fd5997d1 100644 --- a/src/app/features/metadata/constants/index.ts +++ b/src/app/features/metadata/constants/index.ts @@ -1,2 +1 @@ export * from './cedar-config.const'; -export * from './resource-type-options.const'; 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 22b3da58d..406d870f6 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 @@ -6,14 +6,14 @@ @@ -24,22 +24,19 @@ - - {{ option.label }} - diff --git a/src/app/features/metadata/dialogs/resource-information-dialog/resource-information-dialog.component.spec.ts b/src/app/features/metadata/dialogs/resource-information-dialog/resource-information-dialog.component.spec.ts index 65b895efb..5279b776a 100644 --- a/src/app/features/metadata/dialogs/resource-information-dialog/resource-information-dialog.component.spec.ts +++ b/src/app/features/metadata/dialogs/resource-information-dialog/resource-information-dialog.component.spec.ts @@ -12,6 +12,8 @@ import { ResourceInformationDialogComponent } from './resource-information-dialo describe('ResourceInformationDialogComponent', () => { let component: ResourceInformationDialogComponent; let fixture: ComponentFixture; + let dialogRef: DynamicDialogRef; + let config: DynamicDialogConfig; beforeEach(() => { TestBed.configureTestingModule({ @@ -21,83 +23,58 @@ describe('ResourceInformationDialogComponent', () => { fixture = TestBed.createComponent(ResourceInformationDialogComponent); component = fixture.componentInstance; + dialogRef = TestBed.inject(DynamicDialogRef); + config = TestBed.inject(DynamicDialogConfig); }); it('should create', () => { expect(component).toBeTruthy(); }); - it('should have resource type options', () => { - expect(component.resourceTypeOptions).toBeDefined(); - expect(component.resourceTypeOptions.length).toBeGreaterThan(0); - }); - - it('should have language options', () => { - expect(component.languageOptions).toBeDefined(); - expect(component.languageOptions.length).toBeGreaterThan(0); - }); + it('should patch form values on init when metadata is provided', () => { + config.data = { + customItemMetadata: { + resourceTypeGeneral: 'Dataset', + language: 'eng', + }, + }; - it('should not save when form is invalid', () => { - const dialogRef = TestBed.inject(DynamicDialogRef); - const closeSpy = vi.spyOn(dialogRef, 'close'); + component.ngOnInit(); - component.resourceForm.patchValue({ - resourceType: 'dataset', - resourceLanguage: 'en', + expect(component.resourceForm.getRawValue()).toEqual({ + resourceType: 'Dataset', + resourceLanguage: 'eng', }); + }); - component.save(); + it('should keep default empty values on init when metadata is not provided', () => { + config.data = {}; - expect(closeSpy).toHaveBeenCalledWith({ - resourceTypeGeneral: 'dataset', - language: 'en', + component.ngOnInit(); + + expect(component.resourceForm.getRawValue()).toEqual({ + resourceType: '', + resourceLanguage: '', }); }); - it('should not save when resource type is missing', () => { - const dialogRef = TestBed.inject(DynamicDialogRef); - const closeSpy = vi.spyOn(dialogRef, 'close'); - - component.resourceForm.patchValue({ - resourceType: '', - resourceLanguage: 'en', + it('should close dialog with mapped payload on save when form is valid', () => { + component.resourceForm.setValue({ + resourceType: 'JournalArticle', + resourceLanguage: 'deu', }); component.save(); - expect(closeSpy).toHaveBeenCalledWith({ - resourceTypeGeneral: '', - language: 'en', + expect(dialogRef.close).toHaveBeenCalledWith({ + resourceTypeGeneral: 'JournalArticle', + language: 'deu', }); }); - it('should cancel dialog', () => { - const dialogRef = TestBed.inject(DynamicDialogRef); - const closeSpy = vi.spyOn(dialogRef, 'close'); - + it('should close dialog without payload on cancel', () => { component.cancel(); - expect(closeSpy).toHaveBeenCalled(); - }); - - it('should validate required fields', () => { - const resourceTypeControl = component.resourceForm.get('resourceType'); - - expect(resourceTypeControl?.hasError('required')).toBe(false); - - resourceTypeControl?.setValue('dataset'); - - expect(resourceTypeControl?.hasError('required')).toBe(false); - }); - - it('should handle form validation state', () => { - expect(component.resourceForm.valid).toBe(true); - - component.resourceForm.patchValue({ - resourceType: 'dataset', - resourceLanguage: 'en', - }); - - expect(component.resourceForm.valid).toBe(true); + expect(dialogRef.close).toHaveBeenCalledWith(); }); }); diff --git a/src/app/features/metadata/dialogs/resource-information-dialog/resource-information-dialog.component.ts b/src/app/features/metadata/dialogs/resource-information-dialog/resource-information-dialog.component.ts index 51866076d..1a9acf977 100644 --- a/src/app/features/metadata/dialogs/resource-information-dialog/resource-information-dialog.component.ts +++ b/src/app/features/metadata/dialogs/resource-information-dialog/resource-information-dialog.component.ts @@ -7,11 +7,10 @@ import { Select } from 'primeng/select'; import { ChangeDetectionStrategy, Component, inject, OnInit } from '@angular/core'; import { FormControl, FormGroup, ReactiveFormsModule } from '@angular/forms'; -import { languageCodes } from '@osf/shared/constants/language.const'; -import { LanguageCodeModel } from '@shared/models/language-code.model'; +import { LANGUAGE_CODES } from '@osf/shared/constants/language.const'; +import { RESOURCE_TYPE_GENERAL_OPTIONS } from '@osf/shared/constants/resource-type-general-options.const'; -import { RESOURCE_TYPE_OPTIONS } from '../../constants'; -import { CustomItemMetadataRecord, ResourceInformationForm } from '../../models'; +import { ResourceInformationForm } from '../../models'; @Component({ selector: 'osf-resource-information-dialog', @@ -28,26 +27,12 @@ export class ResourceInformationDialogComponent implements OnInit { resourceLanguage: new FormControl(''), }); - resourceTypeOptions = RESOURCE_TYPE_OPTIONS; - languageOptions = languageCodes.map((lang: LanguageCodeModel) => ({ - label: lang.name, - value: lang.code, - })); - - get customItemMetadata(): CustomItemMetadataRecord | null { - return this.config.data?.customItemMetadata || null; - } - - get isEditMode(): boolean { - return !!this.customItemMetadata; - } - - getResourceTypeName(resourceType: string): string { - return Object.fromEntries(RESOURCE_TYPE_OPTIONS.map((item) => [item.value, item.label]))[resourceType]; - } + resourceTypeOptions = RESOURCE_TYPE_GENERAL_OPTIONS; + languageOptions = LANGUAGE_CODES; ngOnInit(): void { - const metadata = this.customItemMetadata; + const metadata = this.config.data?.customItemMetadata; + if (metadata) { this.resourceForm.patchValue({ resourceType: metadata.resourceTypeGeneral || '', @@ -57,13 +42,11 @@ export class ResourceInformationDialogComponent implements OnInit { } save(): void { - if (this.resourceForm.valid) { - const formValue = this.resourceForm.getRawValue(); - this.dialogRef.close({ - resourceTypeGeneral: formValue.resourceType, - language: formValue.resourceLanguage, - }); - } + const formValue = this.resourceForm.getRawValue(); + this.dialogRef.close({ + resourceTypeGeneral: formValue.resourceType, + language: formValue.resourceLanguage, + }); } cancel(): void { diff --git a/src/app/features/metadata/store/metadata.selectors.ts b/src/app/features/metadata/store/metadata.selectors.ts index 7ceca5945..a79fa0a61 100644 --- a/src/app/features/metadata/store/metadata.selectors.ts +++ b/src/app/features/metadata/store/metadata.selectors.ts @@ -16,6 +16,11 @@ export class MetadataSelectors { return state.customMetadata?.data ?? null; } + @Selector([MetadataState]) + static isCustomItemMetadataLoading(state: MetadataStateModel) { + return state.customMetadata?.isLoading ?? false; + } + @Selector([MetadataState]) static getLoading(state: MetadataStateModel) { return state.metadata?.isLoading || state.customMetadata?.isLoading || false; diff --git a/src/app/features/preprints/components/preprint-details/general-information/general-information.component.html b/src/app/features/preprints/components/preprint-details/general-information/general-information.component.html index fc2e0539f..26d233842 100644 --- a/src/app/features/preprints/components/preprint-details/general-information/general-information.component.html +++ b/src/app/features/preprints/components/preprint-details/general-information/general-information.component.html @@ -44,18 +44,6 @@

{{ 'preprints.details.supplementalMaterials' | translate }}

} - @if (preprintProviderValue?.assertionsEnabled) { -
-

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

- - @if (preprintValue.hasCoi) { - {{ preprintValue.coiStatement }} - } @else { -

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

- } -
- } -