diff --git a/src/app/features/metadata/components/metadata-date-info/metadata-date-info.component.html b/src/app/features/metadata/components/metadata-date-info/metadata-date-info.component.html index b4c1d5fa2..b83f434ad 100644 --- a/src/app/features/metadata/components/metadata-date-info/metadata-date-info.component.html +++ b/src/app/features/metadata/components/metadata-date-info/metadata-date-info.component.html @@ -5,7 +5,7 @@

{{ 'project.overview.metadata.dateCreated' | translate }}

-

+

{{ dateCreated() | date: dateFormat }}

@@ -15,7 +15,7 @@

{{ 'project.overview.metadata.dateUpdated' | translate }}

-

+

{{ dateModified() | date: dateFormat }}

diff --git a/src/app/features/metadata/components/metadata-registry-info/metadata-registry-info.component.html b/src/app/features/metadata/components/metadata-registry-info/metadata-registry-info.component.html new file mode 100644 index 000000000..3f1465c0d --- /dev/null +++ b/src/app/features/metadata/components/metadata-registry-info/metadata-registry-info.component.html @@ -0,0 +1,13 @@ + +
+
+

{{ 'registry.overview.metadata.type' | translate }}

+

{{ type() }}

+
+ +
+

{{ 'registry.overview.metadata.registry' | translate }}

+

{{ provider()?.name }}

+
+
+
diff --git a/src/app/features/metadata/components/metadata-registry-info/metadata-registry-info.component.scss b/src/app/features/metadata/components/metadata-registry-info/metadata-registry-info.component.scss new file mode 100644 index 000000000..e69de29bb diff --git a/src/app/features/metadata/components/metadata-registry-info/metadata-registry-info.component.spec.ts b/src/app/features/metadata/components/metadata-registry-info/metadata-registry-info.component.spec.ts new file mode 100644 index 000000000..8dacb5d07 --- /dev/null +++ b/src/app/features/metadata/components/metadata-registry-info/metadata-registry-info.component.spec.ts @@ -0,0 +1,130 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { RegistryProviderDetails } from '@osf/shared/models/provider/registry-provider.model'; + +import { MetadataRegistryInfoComponent } from './metadata-registry-info.component'; + +import { OSFTestingModule } from '@testing/osf.testing.module'; + +describe('MetadataRegistryInfoComponent', () => { + let component: MetadataRegistryInfoComponent; + let fixture: ComponentFixture; + + const mockProvider: RegistryProviderDetails = { + id: 'test-provider-id', + name: 'Test Registry Provider', + descriptionHtml: '

Test description

', + permissions: [], + brand: null, + iri: 'https://example.com/registry', + reviewsWorkflow: 'standard', + }; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [MetadataRegistryInfoComponent, OSFTestingModule], + }).compileComponents(); + + fixture = TestBed.createComponent(MetadataRegistryInfoComponent); + component = fixture.componentInstance; + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); + + it('should initialize with default values', () => { + expect(component.type()).toBe(''); + expect(component.provider()).toBeUndefined(); + }); + + it('should set type input', () => { + const mockType = 'Clinical Trial'; + fixture.componentRef.setInput('type', mockType); + fixture.detectChanges(); + + expect(component.type()).toBe(mockType); + }); + + it('should set provider input', () => { + fixture.componentRef.setInput('provider', mockProvider); + fixture.detectChanges(); + + expect(component.provider()).toEqual(mockProvider); + }); + + it('should handle undefined type input', () => { + fixture.componentRef.setInput('type', undefined); + fixture.detectChanges(); + + expect(component.type()).toBeUndefined(); + }); + + it('should handle null provider input', () => { + fixture.componentRef.setInput('provider', null); + fixture.detectChanges(); + + expect(component.provider()).toBeNull(); + }); + + it('should render type in template', () => { + const mockType = 'Preprint'; + fixture.componentRef.setInput('type', mockType); + fixture.detectChanges(); + + const compiled = fixture.nativeElement; + const typeElement = compiled.querySelector('[data-test-display-registry-type]'); + expect(typeElement).toBeTruthy(); + expect(typeElement.textContent.trim()).toBe(mockType); + }); + + it('should render provider name in template', () => { + fixture.componentRef.setInput('provider', mockProvider); + fixture.detectChanges(); + + const compiled = fixture.nativeElement; + const providerElement = compiled.querySelector('[data-test-display-registry-provider]'); + expect(providerElement).toBeTruthy(); + expect(providerElement.textContent.trim()).toBe(mockProvider.name); + }); + + it('should display empty string when type is empty', () => { + fixture.componentRef.setInput('type', ''); + fixture.detectChanges(); + + const compiled = fixture.nativeElement; + const typeElement = compiled.querySelector('[data-test-display-registry-type]'); + expect(typeElement.textContent.trim()).toBe(''); + }); + + it('should display empty string when provider is null', () => { + fixture.componentRef.setInput('provider', null); + fixture.detectChanges(); + + const compiled = fixture.nativeElement; + const providerElement = compiled.querySelector('[data-test-display-registry-provider]'); + expect(providerElement.textContent.trim()).toBe(''); + }); + + it('should display both type and provider when both are set', () => { + const mockType = 'Registered Report'; + fixture.componentRef.setInput('type', mockType); + fixture.componentRef.setInput('provider', mockProvider); + fixture.detectChanges(); + + const compiled = fixture.nativeElement; + const typeElement = compiled.querySelector('[data-test-display-registry-type]'); + const providerElement = compiled.querySelector('[data-test-display-registry-provider]'); + + expect(typeElement.textContent.trim()).toBe(mockType); + expect(providerElement.textContent.trim()).toBe(mockProvider.name); + }); + + it('should display translated labels', () => { + fixture.detectChanges(); + + const compiled = fixture.nativeElement; + const headings = compiled.querySelectorAll('h2'); + expect(headings.length).toBe(2); + }); +}); diff --git a/src/app/features/metadata/components/metadata-registry-info/metadata-registry-info.component.ts b/src/app/features/metadata/components/metadata-registry-info/metadata-registry-info.component.ts new file mode 100644 index 000000000..25c83a0f0 --- /dev/null +++ b/src/app/features/metadata/components/metadata-registry-info/metadata-registry-info.component.ts @@ -0,0 +1,19 @@ +import { TranslatePipe } from '@ngx-translate/core'; + +import { Card } from 'primeng/card'; + +import { ChangeDetectionStrategy, Component, input } from '@angular/core'; + +import { RegistryProviderDetails } from '@osf/shared/models/provider/registry-provider.model'; + +@Component({ + selector: 'osf-metadata-registry-info', + imports: [Card, TranslatePipe], + templateUrl: './metadata-registry-info.component.html', + styleUrl: './metadata-registry-info.component.scss', + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class MetadataRegistryInfoComponent { + type = input(''); + provider = input(); +} diff --git a/src/app/features/metadata/mappers/metadata.mapper.ts b/src/app/features/metadata/mappers/metadata.mapper.ts index 5c166574d..eb6044cb3 100644 --- a/src/app/features/metadata/mappers/metadata.mapper.ts +++ b/src/app/features/metadata/mappers/metadata.mapper.ts @@ -25,6 +25,7 @@ export class MetadataMapper { provider: response.embeds?.provider?.data.id, public: response.attributes.public, currentUserPermissions: response.attributes.current_user_permissions, + registrationSupplement: response.attributes.registration_supplement, }; } diff --git a/src/app/features/metadata/metadata.component.html b/src/app/features/metadata/metadata.component.html index 491ad60dd..f49bd7ff0 100644 --- a/src/app/features/metadata/metadata.component.html +++ b/src/app/features/metadata/metadata.component.html @@ -33,6 +33,10 @@ [readonly]="!hasWriteAccess()" /> + @if (isRegistrationType()) { + + } + { { selector: MetadataSelectors.getSubmitting, value: false }, { selector: MetadataSelectors.getCedarRecords, value: [] }, { selector: MetadataSelectors.getCedarTemplates, value: null }, + { selector: RegistrationProviderSelectors.getBrandedProvider, value: null }, ], }), ], diff --git a/src/app/features/metadata/metadata.component.ts b/src/app/features/metadata/metadata.component.ts index e20e097f7..85958743d 100644 --- a/src/app/features/metadata/metadata.component.ts +++ b/src/app/features/metadata/metadata.component.ts @@ -37,6 +37,7 @@ import { InstitutionsSelectors, UpdateResourceInstitutions, } from '@osf/shared/stores/institutions'; +import { RegistrationProviderSelectors } from '@osf/shared/stores/registration-provider'; import { FetchChildrenSubjects, FetchSelectedSubjects, @@ -48,6 +49,7 @@ import { MetadataTabsModel } from '@shared/models/metadata-tabs.model'; import { SubjectModel } from '@shared/models/subject/subject.model'; import { MetadataCollectionsComponent } from './components/metadata-collections/metadata-collections.component'; +import { MetadataRegistryInfoComponent } from './components/metadata-registry-info/metadata-registry-info.component'; import { EditTitleDialogComponent } from './dialogs/edit-title-dialog/edit-title-dialog.component'; import { MetadataAffiliatedInstitutionsComponent, @@ -112,6 +114,7 @@ import { MetadataTitleComponent, MetadataRegistrationDoiComponent, MetadataCollectionsComponent, + MetadataRegistryInfoComponent, ], templateUrl: './metadata.component.html', styleUrl: './metadata.component.scss', @@ -150,6 +153,7 @@ export class MetadataComponent implements OnInit { affiliatedInstitutions = select(InstitutionsSelectors.getResourceInstitutions); areInstitutionsLoading = select(InstitutionsSelectors.areResourceInstitutionsLoading); areResourceInstitutionsSubmitting = select(InstitutionsSelectors.areResourceInstitutionsSubmitting); + registryProvider = select(RegistrationProviderSelectors.getBrandedProvider); projectSubmissions = select(CollectionsSelectors.getCurrentProjectSubmissions); isProjectSubmissionsLoading = select(CollectionsSelectors.getCurrentProjectSubmissionsLoading); diff --git a/src/app/features/metadata/models/metadata-json-api.model.ts b/src/app/features/metadata/models/metadata-json-api.model.ts index 11dfbd342..802777461 100644 --- a/src/app/features/metadata/models/metadata-json-api.model.ts +++ b/src/app/features/metadata/models/metadata-json-api.model.ts @@ -21,6 +21,7 @@ export interface MetadataAttributesJsonApi { category?: string; node_license?: LicenseRecordJsonApi; public?: boolean; + registration_supplement?: string; current_user_permissions: UserPermissions[]; } diff --git a/src/app/features/metadata/models/metadata.model.ts b/src/app/features/metadata/models/metadata.model.ts index 43c44bf84..b8ce8f5cb 100644 --- a/src/app/features/metadata/models/metadata.model.ts +++ b/src/app/features/metadata/models/metadata.model.ts @@ -24,6 +24,7 @@ export interface MetadataModel { }; public?: boolean; currentUserPermissions: UserPermissions[]; + registrationSupplement?: string; } export interface CustomItemMetadataRecord { diff --git a/src/app/features/registry/components/registry-overview-metadata/registry-overview-metadata.component.html b/src/app/features/registry/components/registry-overview-metadata/registry-overview-metadata.component.html index 2637d5b9b..c8f736206 100644 --- a/src/app/features/registry/components/registry-overview-metadata/registry-overview-metadata.component.html +++ b/src/app/features/registry/components/registry-overview-metadata/registry-overview-metadata.component.html @@ -42,6 +42,11 @@

{{ 'registry.overview.metadata.type' | translate }}

{{ resource.registrationSupplement }}

+
+

{{ 'registry.overview.metadata.registry' | translate }}

+

{{ registryProvider()?.name }}

+
+ @if (resource.associatedProjectId) {

{{ 'registry.overview.metadata.associatedProject' | translate }}

diff --git a/src/app/features/registry/components/registry-overview-metadata/registry-overview-metadata.component.spec.ts b/src/app/features/registry/components/registry-overview-metadata/registry-overview-metadata.component.spec.ts index 697e0a1de..72dca0a1a 100644 --- a/src/app/features/registry/components/registry-overview-metadata/registry-overview-metadata.component.spec.ts +++ b/src/app/features/registry/components/registry-overview-metadata/registry-overview-metadata.component.spec.ts @@ -17,6 +17,7 @@ import { SubjectsListComponent } from '@osf/shared/components/subjects-list/subj import { TagsListComponent } from '@osf/shared/components/tags-list/tags-list.component'; import { CurrentResourceType, ResourceType } from '@osf/shared/enums/resource-type.enum'; import { ContributorsSelectors, LoadMoreBibliographicContributors } from '@osf/shared/stores/contributors'; +import { RegistrationProviderSelectors } from '@osf/shared/stores/registration-provider'; import { FetchSelectedSubjects, SubjectsSelectors } from '@osf/shared/stores/subjects'; import { @@ -79,6 +80,7 @@ describe('RegistryOverviewMetadataComponent', () => { { selector: RegistrySelectors.isIdentifiersLoading, value: false }, { selector: RegistrySelectors.getInstitutions, value: [] }, { selector: RegistrySelectors.isInstitutionsLoading, value: false }, + { selector: RegistrationProviderSelectors.getBrandedProvider, value: null }, { selector: SubjectsSelectors.getSubjects, value: [] }, { selector: SubjectsSelectors.getSubjectsLoading, value: false }, { selector: ContributorsSelectors.getBibliographicContributors, value: [] }, diff --git a/src/app/features/registry/components/registry-overview-metadata/registry-overview-metadata.component.ts b/src/app/features/registry/components/registry-overview-metadata/registry-overview-metadata.component.ts index c2c3b4686..693e8eebd 100644 --- a/src/app/features/registry/components/registry-overview-metadata/registry-overview-metadata.component.ts +++ b/src/app/features/registry/components/registry-overview-metadata/registry-overview-metadata.component.ts @@ -19,6 +19,7 @@ import { TagsListComponent } from '@osf/shared/components/tags-list/tags-list.co import { TruncatedTextComponent } from '@osf/shared/components/truncated-text/truncated-text.component'; import { CurrentResourceType, ResourceType } from '@osf/shared/enums/resource-type.enum'; import { ContributorsSelectors, LoadMoreBibliographicContributors } from '@osf/shared/stores/contributors'; +import { RegistrationProviderSelectors } from '@osf/shared/stores/registration-provider'; import { FetchSelectedSubjects, SubjectsSelectors } from '@osf/shared/stores/subjects'; import { @@ -54,6 +55,7 @@ export class RegistryOverviewMetadataComponent { private readonly router = inject(Router); readonly registry = select(RegistrySelectors.getRegistry); + readonly registryProvider = select(RegistrationProviderSelectors.getBrandedProvider); readonly isAnonymous = select(RegistrySelectors.isRegistryAnonymous); canEdit = select(RegistrySelectors.hasWriteAccess); diff --git a/src/assets/i18n/en.json b/src/assets/i18n/en.json index 13b5ec8f8..644e14ad2 100644 --- a/src/assets/i18n/en.json +++ b/src/assets/i18n/en.json @@ -2644,6 +2644,7 @@ }, "metadata": { "type": "Registration Type", + "registry": "Registry", "registeredDate": "Date registered", "doi": "Registration DOI", "associatedProject": "Associated project"