diff --git a/src/app/features/project/overview/components/fork-dialog/fork-dialog.component.html b/src/app/features/project/overview/components/fork-dialog/fork-dialog.component.html index aab189f9e..03cd469a4 100644 --- a/src/app/features/project/overview/components/fork-dialog/fork-dialog.component.html +++ b/src/app/features/project/overview/components/fork-dialog/fork-dialog.component.html @@ -1,5 +1,9 @@

- {{ 'project.overview.dialog.fork.message' | translate }} + @if (config.data.resource.resourceType === ResourceType.Project) { + {{ 'project.overview.dialog.fork.messageProject' | translate }} + } @else if (config.data.resource.resourceType === ResourceType.Registration) { + {{ 'project.overview.dialog.fork.messageRegistry' | translate }} + }

diff --git a/src/app/features/project/overview/components/fork-dialog/fork-dialog.component.ts b/src/app/features/project/overview/components/fork-dialog/fork-dialog.component.ts index b8e262ac5..2d4a1c2dc 100644 --- a/src/app/features/project/overview/components/fork-dialog/fork-dialog.component.ts +++ b/src/app/features/project/overview/components/fork-dialog/fork-dialog.component.ts @@ -5,10 +5,13 @@ import { TranslatePipe } from '@ngx-translate/core'; import { Button } from 'primeng/button'; import { DynamicDialogConfig, DynamicDialogRef } from 'primeng/dynamicdialog'; +import { finalize } from 'rxjs'; + import { ChangeDetectionStrategy, Component, DestroyRef, inject } from '@angular/core'; import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; import { ForkResource, ProjectOverviewSelectors } from '@osf/features/project/overview/store'; +import { ResourceType } from '@shared/enums'; import { ToolbarResource } from '@shared/models'; import { ToastService } from '@shared/services'; @@ -33,12 +36,15 @@ export class ForkDialogComponent { this.store .dispatch(new ForkResource(resource.id, resource.resourceType)) - .pipe(takeUntilDestroyed(this.destroyRef)) - .subscribe({ - next: () => { + .pipe( + takeUntilDestroyed(this.destroyRef), + finalize(() => { this.dialogRef.close(); this.toastService.showSuccess('project.overview.dialog.toast.fork.success'); - }, - }); + }) + ) + .subscribe(); } + + protected readonly ResourceType = ResourceType; } diff --git a/src/app/features/project/overview/components/overview-toolbar/overview-toolbar.component.ts b/src/app/features/project/overview/components/overview-toolbar/overview-toolbar.component.ts index f84dea1a2..0b682b9d9 100644 --- a/src/app/features/project/overview/components/overview-toolbar/overview-toolbar.component.ts +++ b/src/app/features/project/overview/components/overview-toolbar/overview-toolbar.component.ts @@ -132,15 +132,21 @@ export class OverviewToolbarComponent { private handleForkResource(): void { const resource = this.currentResource(); + const headerTranslation = + resource?.resourceType === ResourceType.Project + ? 'project.overview.dialog.fork.headerProject' + : resource?.resourceType === ResourceType.Registration + ? 'project.overview.dialog.fork.headerRegistry' + : ''; if (resource) { this.dialogService.open(ForkDialogComponent, { focusOnShow: false, - header: this.translateService.instant('project.overview.dialog.fork.header'), + header: this.translateService.instant(headerTranslation), closeOnEscape: true, modal: true, closable: true, data: { - resourceId: resource, + resource: resource, }, }); } diff --git a/src/app/features/project/overview/services/project-overview.service.ts b/src/app/features/project/overview/services/project-overview.service.ts index 2775e166c..c66ffaf90 100644 --- a/src/app/features/project/overview/services/project-overview.service.ts +++ b/src/app/features/project/overview/services/project-overview.service.ts @@ -59,7 +59,7 @@ export class ProjectOverviewService { forkResource(projectId: string, resourceType: string): Observable { const payload = { data: { - type: resourceType, + type: 'nodes', }, }; diff --git a/src/app/features/project/overview/store/project-overview.actions.ts b/src/app/features/project/overview/store/project-overview.actions.ts index 8c5f24957..c7a5fd259 100644 --- a/src/app/features/project/overview/store/project-overview.actions.ts +++ b/src/app/features/project/overview/store/project-overview.actions.ts @@ -19,7 +19,7 @@ export class ForkResource { static readonly type = '[Project Overview] Fork Resource'; constructor( - public projectId: string, + public resourceId: string, public resourceType: ResourceType ) {} } diff --git a/src/app/features/project/overview/store/project-overview.state.ts b/src/app/features/project/overview/store/project-overview.state.ts index 67194a748..dfa899118 100644 --- a/src/app/features/project/overview/store/project-overview.state.ts +++ b/src/app/features/project/overview/store/project-overview.state.ts @@ -127,12 +127,9 @@ export class ProjectOverviewState { case ResourceType.Registration: resourceType = 'registrations'; break; - case ResourceType.Preprint: - resourceType = 'preprints'; - break; } - return this.projectOverviewService.forkResource(action.projectId, resourceType).pipe( + return this.projectOverviewService.forkResource(action.resourceId, resourceType).pipe( tap(() => { ctx.patchState({ project: { diff --git a/src/app/features/registries/pages/registries-landing/registries-landing.component.html b/src/app/features/registries/pages/registries-landing/registries-landing.component.html index 506e40dcb..14366d157 100644 --- a/src/app/features/registries/pages/registries-landing/registries-landing.component.html +++ b/src/app/features/registries/pages/registries-landing/registries-landing.component.html @@ -1,5 +1,5 @@
-
+
+ + + {{ 'registry.overview.updates' | translate }} + + +
+ @if (registry() && registry()!.schemaResponses.length > 0) { + + @for (revision of registry()!.schemaResponses.slice(1); track $index; let last = $last) { + @if (last) { + + } @else { + + } + } + + + @if (registry()?.revisionStatus === RevisionReviewStates.Approved) { + + } + @if (registry()?.revisionStatus === RevisionReviewStates.RevisionInProgress) { + + } + } +
+
+
+ diff --git a/src/app/features/registry/components/registry-revisions/registry-revisions.component.scss b/src/app/features/registry/components/registry-revisions/registry-revisions.component.scss new file mode 100644 index 000000000..24e8f025d --- /dev/null +++ b/src/app/features/registry/components/registry-revisions/registry-revisions.component.scss @@ -0,0 +1,18 @@ +@use "/assets/styles/variables" as var; +@use "assets/styles/mixins" as mix; + +.accordion-border { + border: 1px solid var.$grey-2; + border-radius: mix.rem(12px); + height: max-content !important; +} + +.no-padding { + --p-accordion-header-padding: 0; +} + +.current-revision { + --p-button-info-background: var(--bg-blue-3); + --p-button-info-hover-background: var(--bg-blue-2); + --p-button-info-hover-border-color: transparent; +} diff --git a/src/app/features/registry/components/registry-revisions/registry-revisions.component.spec.ts b/src/app/features/registry/components/registry-revisions/registry-revisions.component.spec.ts new file mode 100644 index 000000000..4d0d74e21 --- /dev/null +++ b/src/app/features/registry/components/registry-revisions/registry-revisions.component.spec.ts @@ -0,0 +1,22 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { RegistryRevisionsComponent } from './registry-revisions.component'; + +describe('RegistryRevisionsComponent', () => { + let component: RegistryRevisionsComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [RegistryRevisionsComponent], + }).compileComponents(); + + fixture = TestBed.createComponent(RegistryRevisionsComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/features/registry/components/registry-revisions/registry-revisions.component.ts b/src/app/features/registry/components/registry-revisions/registry-revisions.component.ts new file mode 100644 index 000000000..a1f90b92a --- /dev/null +++ b/src/app/features/registry/components/registry-revisions/registry-revisions.component.ts @@ -0,0 +1,29 @@ +import { TranslatePipe } from '@ngx-translate/core'; + +import { Accordion, AccordionContent, AccordionHeader, AccordionPanel } from 'primeng/accordion'; +import { Button } from 'primeng/button'; + +import { ChangeDetectionStrategy, Component, HostBinding, input, output } from '@angular/core'; + +import { RegistryOverview } from '@osf/features/registry/models'; +import { RevisionReviewStates } from '@shared/enums'; + +@Component({ + selector: 'osf-registry-revisions', + imports: [Accordion, AccordionPanel, AccordionHeader, AccordionContent, Button, TranslatePipe], + templateUrl: './registry-revisions.component.html', + styleUrl: './registry-revisions.component.scss', + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class RegistryRevisionsComponent { + @HostBinding('class') classes = 'flex-1 flex'; + registry = input.required(); + selectedRevisionIndex = input.required(); + openRevision = output(); + + emitOpenRevision(index: number) { + this.openRevision.emit(index); + } + + protected readonly RevisionReviewStates = RevisionReviewStates; +} diff --git a/src/app/features/registry/components/registry-statuses/registry-statuses.component.html b/src/app/features/registry/components/registry-statuses/registry-statuses.component.html new file mode 100644 index 000000000..7aa6a3204 --- /dev/null +++ b/src/app/features/registry/components/registry-statuses/registry-statuses.component.html @@ -0,0 +1,28 @@ + + + +

{{ 'registry.overview.statuses.' + registry()?.status + '.text' | translate }}

+
+ +
+

{{ 'registry.overview.statuses.' + registry()?.status + '.short' | translate }}

+

{{ 'registry.overview.statuses.' + registry()?.status + '.long' | translate }}

+ @if (registry()?.status === RegistryStatus.InProgress && registry()?.status === RegistryStatus.Accepted) { + + } + @if (registry()?.status === RegistryStatus.Embargo) { + + } +
+
+
+
diff --git a/src/app/features/registry/components/registry-statuses/registry-statuses.component.scss b/src/app/features/registry/components/registry-statuses/registry-statuses.component.scss new file mode 100644 index 000000000..8af844a39 --- /dev/null +++ b/src/app/features/registry/components/registry-statuses/registry-statuses.component.scss @@ -0,0 +1,12 @@ +@use "/assets/styles/variables" as var; +@use "assets/styles/mixins" as mix; + +.accordion-border { + border: 1px solid var.$grey-2; + border-radius: mix.rem(12px); + height: max-content !important; +} + +.no-padding { + --p-accordion-header-padding: 0; +} diff --git a/src/app/features/registry/components/registry-statuses/registry-statuses.component.spec.ts b/src/app/features/registry/components/registry-statuses/registry-statuses.component.spec.ts new file mode 100644 index 000000000..0ed8cdc6f --- /dev/null +++ b/src/app/features/registry/components/registry-statuses/registry-statuses.component.spec.ts @@ -0,0 +1,22 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { RegistryStatusesComponent } from './registry-statuses.component'; + +describe('RegistryStatusesComponent', () => { + let component: RegistryStatusesComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [RegistryStatusesComponent], + }).compileComponents(); + + fixture = TestBed.createComponent(RegistryStatusesComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/features/registry/components/registry-statuses/registry-statuses.component.ts b/src/app/features/registry/components/registry-statuses/registry-statuses.component.ts new file mode 100644 index 000000000..76a7b76bf --- /dev/null +++ b/src/app/features/registry/components/registry-statuses/registry-statuses.component.ts @@ -0,0 +1,64 @@ +import { createDispatchMap, Store } from '@ngxs/store'; + +import { TranslatePipe, TranslateService } from '@ngx-translate/core'; + +import { Accordion, AccordionContent, AccordionHeader, AccordionPanel } from 'primeng/accordion'; +import { Button } from 'primeng/button'; +import { DialogService } from 'primeng/dynamicdialog'; + +import { ChangeDetectionStrategy, Component, HostBinding, inject, input } from '@angular/core'; + +import { WithdrawDialogComponent } from '@osf/features/registry/components'; +import { RegistryOverview } from '@osf/features/registry/models'; +import { MakePublic } from '@osf/features/registry/store/registry-overview'; +import { RegistryStatus } from '@shared/enums'; +import { CustomConfirmationService } from '@shared/services'; + +@Component({ + selector: 'osf-registry-statuses', + imports: [Accordion, AccordionContent, AccordionHeader, AccordionPanel, TranslatePipe, Button], + templateUrl: './registry-statuses.component.html', + styleUrl: './registry-statuses.component.scss', + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class RegistryStatusesComponent { + @HostBinding('class') classes = 'flex-1 flex'; + private readonly store = inject(Store); + registry = input.required(); + private readonly dialogService = inject(DialogService); + private readonly translateService = inject(TranslateService); + protected readonly RegistryStatus = RegistryStatus; + protected readonly customConfirmationService = inject(CustomConfirmationService); + protected readonly actions = createDispatchMap({ + makePublic: MakePublic, + }); + + openWithdrawDialog(): void { + const registry = this.registry(); + if (registry) { + this.dialogService.open(WithdrawDialogComponent, { + width: '552px', + focusOnShow: false, + header: this.translateService.instant('registry.overview.withdrawRegistration'), + closeOnEscape: true, + modal: true, + closable: true, + data: { + registryId: registry.id, + }, + }); + } + } + + openMakePublicDialog(): void { + const registry = this.registry(); + if (registry) { + this.customConfirmationService.confirmAccept({ + headerKey: 'common.labels.makePublic', + messageKey: 'registry.overview.makePublicMessage', + acceptLabelKey: 'common.labels.makePublic', + onConfirm: () => this.actions.makePublic(registry.id), + }); + } + } +} diff --git a/src/app/features/registry/components/withdraw-dialog/withdraw-dialog.component.html b/src/app/features/registry/components/withdraw-dialog/withdraw-dialog.component.html new file mode 100644 index 000000000..6d95fc1dd --- /dev/null +++ b/src/app/features/registry/components/withdraw-dialog/withdraw-dialog.component.html @@ -0,0 +1,19 @@ +
+

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

+ + + + +
+ + +
+
diff --git a/src/app/features/registry/components/withdraw-dialog/withdraw-dialog.component.scss b/src/app/features/registry/components/withdraw-dialog/withdraw-dialog.component.scss new file mode 100644 index 000000000..e69de29bb diff --git a/src/app/features/registry/components/withdraw-dialog/withdraw-dialog.component.spec.ts b/src/app/features/registry/components/withdraw-dialog/withdraw-dialog.component.spec.ts new file mode 100644 index 000000000..f7b939b63 --- /dev/null +++ b/src/app/features/registry/components/withdraw-dialog/withdraw-dialog.component.spec.ts @@ -0,0 +1,22 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { WithdrawDialogComponent } from './withdraw-dialog.component'; + +describe('WithdrawDialogComponent', () => { + let component: WithdrawDialogComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [WithdrawDialogComponent], + }).compileComponents(); + + fixture = TestBed.createComponent(WithdrawDialogComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/features/registry/components/withdraw-dialog/withdraw-dialog.component.ts b/src/app/features/registry/components/withdraw-dialog/withdraw-dialog.component.ts new file mode 100644 index 000000000..bf0471cc5 --- /dev/null +++ b/src/app/features/registry/components/withdraw-dialog/withdraw-dialog.component.ts @@ -0,0 +1,46 @@ +import { createDispatchMap } from '@ngxs/store'; + +import { TranslatePipe } from '@ngx-translate/core'; + +import { Button } from 'primeng/button'; +import { DynamicDialogConfig, DynamicDialogRef } from 'primeng/dynamicdialog'; + +import { finalize, take } from 'rxjs'; + +import { ChangeDetectionStrategy, Component, inject } from '@angular/core'; +import { FormControl, FormGroup } from '@angular/forms'; + +import { WithdrawRegistration } from '@osf/features/registry/store/registry-overview'; +import { TextInputComponent } from '@shared/components'; + +@Component({ + selector: 'osf-withdraw-dialog', + imports: [TranslatePipe, TextInputComponent, Button], + templateUrl: './withdraw-dialog.component.html', + styleUrl: './withdraw-dialog.component.scss', + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class WithdrawDialogComponent { + protected readonly dialogRef = inject(DynamicDialogRef); + private readonly config = inject(DynamicDialogConfig); + private readonly actions = createDispatchMap({ + withdrawRegistration: WithdrawRegistration, + }); + + protected readonly form = new FormGroup({ + text: new FormControl(''), + }); + + withdrawRegistration(): void { + const registryId = this.config.data.registryId; + if (registryId) { + this.actions + .withdrawRegistration(registryId, this.form.controls.text.value ?? '') + .pipe( + take(1), + finalize(() => this.dialogRef.close()) + ) + .subscribe(); + } + } +} diff --git a/src/app/features/registry/mappers/registry-overview.mapper.ts b/src/app/features/registry/mappers/registry-overview.mapper.ts index 591b914d9..8c841fd64 100644 --- a/src/app/features/registry/mappers/registry-overview.mapper.ts +++ b/src/app/features/registry/mappers/registry-overview.mapper.ts @@ -1,6 +1,7 @@ import { RegistryOverview, RegistryOverviewJsonApiData } from '@osf/features/registry/models'; +import { MapRegistryStatus } from '@shared/mappers/registry/map-registry-status.mapper'; -export function MapRegistryOverview(data: RegistryOverviewJsonApiData): RegistryOverview { +export function MapRegistryOverview(data: RegistryOverviewJsonApiData): RegistryOverview | null { return { id: data.id, type: data.type, @@ -54,5 +55,12 @@ export function MapRegistryOverview(data: RegistryOverviewJsonApiData): Registry questions: data.attributes.registration_responses, registrationSchemaLink: data.relationships.registration_schema.links.related.href, associatedProjectId: data.relationships?.registered_from?.data?.id, + schemaResponses: data.embeds?.schema_responses?.data?.map((schemaResponse) => ({ + id: schemaResponse.id, + revisionResponses: schemaResponse.attributes?.revision_responses, + updatedResponseKeys: schemaResponse.attributes?.updated_response_keys, + })), + status: MapRegistryStatus(data.attributes), + revisionStatus: data.attributes.revision_state, } as RegistryOverview; } diff --git a/src/app/features/registry/mappers/registry-schema-block.mapper.ts b/src/app/features/registry/mappers/registry-schema-block.mapper.ts index f73fded26..8b5325ccc 100644 --- a/src/app/features/registry/mappers/registry-schema-block.mapper.ts +++ b/src/app/features/registry/mappers/registry-schema-block.mapper.ts @@ -1,21 +1,23 @@ -import { RegistrationQuestions, RegistrySchemaBlock, SchemaBlockAttributes } from '@osf/features/registry/models'; +import { + RegistrationQuestions, + RegistrySchemaBlock, + SchemaBlockAttributes, + ViewSchemaBlock, +} from '@osf/features/registry/models'; -export function MapRegistrySchemaBlock( - block: SchemaBlockAttributes, - questions: RegistrationQuestions -): RegistrySchemaBlock { +export function MapViewSchemaBlock(block: RegistrySchemaBlock, questions: RegistrationQuestions): ViewSchemaBlock { const schemaBlock = { required: block.required, - type: block.block_type, + type: block.blockType, value: '', values: [], files: [], - } as RegistrySchemaBlock; + } as ViewSchemaBlock; - if (block.display_text) { - schemaBlock.value = block.display_text; - } else if (block.registration_response_key) { - const questionValue = questions[block.registration_response_key]; + if (block.displayText) { + schemaBlock.value = block.displayText; + } else if (block.registrationResponseKey) { + const questionValue = questions[block.registrationResponseKey]; if (questionValue && Array.isArray(questionValue)) { if (schemaBlock.type === 'multi-select-input') { @@ -33,3 +35,13 @@ export function MapRegistrySchemaBlock( return schemaBlock; } + +export function MapRegistrySchemaBlock(block: SchemaBlockAttributes): RegistrySchemaBlock { + return { + blockType: block.block_type, + displayText: block.display_text, + registrationResponseKey: block?.registration_response_key, + required: block.required, + schemaBlockGroupKey: block.schema_block_group_key, + }; +} diff --git a/src/app/features/registry/models/get-registry-overview-json-api.model.ts b/src/app/features/registry/models/get-registry-overview-json-api.model.ts index 9be0c0156..99d9f1952 100644 --- a/src/app/features/registry/models/get-registry-overview-json-api.model.ts +++ b/src/app/features/registry/models/get-registry-overview-json-api.model.ts @@ -1,4 +1,5 @@ import { ApiData, JsonApiResponse } from '@core/models'; +import { RegistrationReviewStates, RevisionReviewStates } from '@shared/enums'; export type GetRegistryOverviewJsonApi = JsonApiResponse; @@ -39,6 +40,13 @@ export interface RegistryOverviewJsonApiAttributes { has_papers: boolean; has_supplements: boolean; registration_responses: RegistrationQuestions; + pending_embargo_approval: boolean; + pending_embargo_termination_approval: boolean; + pending_registration_approval: boolean; + pending_withdrawal: boolean; + revision_state: RevisionReviewStates; + review_state: RegistrationReviewStates; + embargoed: boolean; } export type RegistrationQuestions = Record; @@ -83,6 +91,15 @@ export interface RegistryOverviewJsonApiEmbed { }; }[]; }; + schema_responses: { + data: { + id: string; + attributes: { + revision_responses: RegistrationQuestions; + updated_response_keys: string[]; + }; + }[]; + }; } export interface RegistryOverviewJsonApiRelationships { diff --git a/src/app/features/registry/models/get-registry-schema-block-json-api.model.ts b/src/app/features/registry/models/get-registry-schema-block-json-api.model.ts index dd4db0c5e..2069e5887 100644 --- a/src/app/features/registry/models/get-registry-schema-block-json-api.model.ts +++ b/src/app/features/registry/models/get-registry-schema-block-json-api.model.ts @@ -7,4 +7,5 @@ export interface SchemaBlockAttributes { display_text: string; registration_response_key: string | null; required: boolean; + schema_block_group_key: string; } diff --git a/src/app/features/registry/models/index.ts b/src/app/features/registry/models/index.ts index c92010cce..05202a94a 100644 --- a/src/app/features/registry/models/index.ts +++ b/src/app/features/registry/models/index.ts @@ -6,3 +6,4 @@ export * from './registry-institution.model'; export * from './registry-overview.models'; export * from './registry-schema-block.model'; export * from './registry-subject.model'; +export * from './view-schema-block.model'; diff --git a/src/app/features/registry/models/registry-overview.models.ts b/src/app/features/registry/models/registry-overview.models.ts index cd1617120..6ff2f5349 100644 --- a/src/app/features/registry/models/registry-overview.models.ts +++ b/src/app/features/registry/models/registry-overview.models.ts @@ -1,5 +1,6 @@ import { ProjectOverviewContributor } from '@osf/features/project/overview/models'; import { RegistrationQuestions, RegistrySubject } from '@osf/features/registry/models'; +import { RegistryStatus, RevisionReviewStates } from '@shared/enums'; export interface RegistryOverview { id: string; @@ -53,4 +54,11 @@ export interface RegistryOverview { questions: RegistrationQuestions; registrationSchemaLink: string; associatedProjectId: string; + schemaResponses: { + id: string; + revisionResponses: RegistrationQuestions; + updatedResponseKeys: string[]; + }[]; + status: RegistryStatus; + revisionStatus: RevisionReviewStates; } diff --git a/src/app/features/registry/models/registry-schema-block.model.ts b/src/app/features/registry/models/registry-schema-block.model.ts index 27dd1e560..433d6c603 100644 --- a/src/app/features/registry/models/registry-schema-block.model.ts +++ b/src/app/features/registry/models/registry-schema-block.model.ts @@ -1,8 +1,7 @@ export interface RegistrySchemaBlock { - value: string; - values: string[]; - files?: { id: string; name: string }[]; + blockType: string; + displayText: string; required: boolean; - html?: string; - type: string; + schemaBlockGroupKey: string; + registrationResponseKey: string | null; } diff --git a/src/app/features/registry/models/view-schema-block.model.ts b/src/app/features/registry/models/view-schema-block.model.ts new file mode 100644 index 000000000..432753f5d --- /dev/null +++ b/src/app/features/registry/models/view-schema-block.model.ts @@ -0,0 +1,8 @@ +export interface ViewSchemaBlock { + value: string; + values: string[]; + files?: { id: string; name: string }[]; + required: boolean; + html?: string; + type: string; +} diff --git a/src/app/features/registry/pages/registry-overview/registry-overview.component.html b/src/app/features/registry/pages/registry-overview/registry-overview.component.html index a492cedd8..6ff52ce7b 100644 --- a/src/app/features/registry/pages/registry-overview/registry-overview.component.html +++ b/src/app/features/registry/pages/registry-overview/registry-overview.component.html @@ -1,8 +1,10 @@ @if (!isRegistryLoading() && !isSubjectsLoading() && !isInstitutionsLoading() && !isSchemaBlocksLoading()) { -
+
-
+
-
- +
+ -
-
- -
- - - -

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

-
- -
-

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

-

{{ 'registry.overview.statuses.frozen' | translate }}

-
-
-
-
- - - - {{ 'registry.overview.updates' | translate }} - - -
- - - -
-
-
-
+
+
+
+ + +
- @for (block of schemaBlocks(); track $index) { + @for (block of mappedSchemaBlocks(); track $index) { @if (block.type === 'file-input' && block.files) {
    @for (file of block.files; track $index) { diff --git a/src/app/features/registry/pages/registry-overview/registry-overview.component.scss b/src/app/features/registry/pages/registry-overview/registry-overview.component.scss index 90ecba76c..b90d683af 100644 --- a/src/app/features/registry/pages/registry-overview/registry-overview.component.scss +++ b/src/app/features/registry/pages/registry-overview/registry-overview.component.scss @@ -1,18 +1,6 @@ @use "/assets/styles/variables" as var; @use "assets/styles/mixins" as mix; -.icon-container { - color: var.$dark-blue-1; - display: flex; - align-items: center; - column-gap: 0.5rem; - - &:hover { - text-decoration: none; - color: var.$pr-blue-1; - } -} - .left-section { flex: 3; display: flex; @@ -41,3 +29,9 @@ .no-padding { --p-accordion-header-padding: 0; } + +.current-revision { + --p-button-info-background: var(--bg-blue-3); + --p-button-info-hover-background: var(--bg-blue-2); + --p-button-info-hover-border-color: transparent; +} 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 ec8840d9e..a27538908 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 @@ -1,23 +1,27 @@ import { createDispatchMap, select } from '@ngxs/store'; -import { TranslatePipe } from '@ngx-translate/core'; - -import { Accordion, AccordionContent, AccordionHeader, AccordionPanel } from 'primeng/accordion'; -import { Button } from 'primeng/button'; import { DialogService } from 'primeng/dynamicdialog'; -import { ChangeDetectionStrategy, Component, computed, HostBinding, inject } from '@angular/core'; +import { ChangeDetectionStrategy, Component, computed, HostBinding, inject, signal } from '@angular/core'; import { ActivatedRoute, Router, RouterLink } from '@angular/router'; import { GetBookmarksCollectionId } from '@osf/features/collections/store'; import { OverviewToolbarComponent } from '@osf/features/project/overview/components'; +import { RegistryRevisionsComponent, RegistryStatusesComponent } from '@osf/features/registry/components'; +import { MapViewSchemaBlock } from '@osf/features/registry/mappers'; +import { RegistrationQuestions } from '@osf/features/registry/models'; import { GetRegistryById, GetRegistryInstitutions, GetRegistrySubjects, RegistryOverviewSelectors, } from '@osf/features/registry/store/registry-overview'; -import { LoadingSpinnerComponent, ResourceMetadataComponent, SubHeaderComponent } from '@shared/components'; +import { + DataResourcesComponent, + LoadingSpinnerComponent, + ResourceMetadataComponent, + SubHeaderComponent, +} from '@shared/components'; import { ResourceType } from '@shared/enums'; import { MapRegistryOverview } from '@shared/mappers'; import { ToolbarResource } from '@shared/models'; @@ -28,14 +32,11 @@ import { ToolbarResource } from '@shared/models'; SubHeaderComponent, OverviewToolbarComponent, LoadingSpinnerComponent, - TranslatePipe, RouterLink, - AccordionContent, - Accordion, - AccordionPanel, - AccordionHeader, ResourceMetadataComponent, - Button, + RegistryRevisionsComponent, + RegistryStatusesComponent, + DataResourcesComponent, ], templateUrl: './registry-overview.component.html', styleUrl: './registry-overview.component.scss', @@ -65,6 +66,23 @@ export class RegistryOverviewComponent { } return null; }); + protected readonly mappedSchemaBlocks = computed(() => { + const schemaBlocks = this.schemaBlocks(); + const index = this.selectedRevisionIndex(); + let questions: RegistrationQuestions | undefined; + if (index === 0) { + questions = this.registry()?.questions; + } else if (this.registry()?.schemaResponses?.length) { + questions = this.registry()?.schemaResponses?.[index]?.revisionResponses; + } + + if (schemaBlocks?.length && questions) { + return schemaBlocks.map((schemaBlock) => MapViewSchemaBlock(schemaBlock, questions)); + } + return []; + }); + + protected readonly selectedRevisionIndex = signal(0); protected toolbarResource = computed(() => { if (this.registry()) { @@ -102,4 +120,8 @@ export class RegistryOverviewComponent { navigateToFile(fileId: string): void { this.router.navigate(['/files', fileId]); } + + openRevision(revisionIndex: number): void { + this.selectedRevisionIndex.set(revisionIndex); + } } diff --git a/src/app/features/registry/services/index.ts b/src/app/features/registry/services/index.ts index 0b2facadf..08edb6ef5 100644 --- a/src/app/features/registry/services/index.ts +++ b/src/app/features/registry/services/index.ts @@ -1 +1 @@ -export * from './project-overview.service'; +export * from './registry-overview.service'; diff --git a/src/app/features/registry/services/project-overview.service.ts b/src/app/features/registry/services/registry-overview.service.ts similarity index 66% rename from src/app/features/registry/services/project-overview.service.ts rename to src/app/features/registry/services/registry-overview.service.ts index 25c9a0d2c..9989bc111 100644 --- a/src/app/features/registry/services/project-overview.service.ts +++ b/src/app/features/registry/services/registry-overview.service.ts @@ -9,9 +9,9 @@ import { GetRegistryOverviewJsonApi, GetRegistrySchemaBlockJsonApi, GetResourceSubjectsJsonApi, - RegistrationQuestions, RegistryInstitution, RegistryOverview, + RegistryOverviewJsonApiData, RegistrySchemaBlock, RegistrySubject, } from '@osf/features/registry/models'; @@ -24,7 +24,7 @@ import { environment } from 'src/environments/environment'; export class RegistryOverviewService { private jsonApiService = inject(JsonApiService); - getRegistrationById(id: string): Observable { + getRegistrationById(id: string): Observable { const params = { related_counts: 'forks,comments,linked_nodes,linked_registrations,children,wikis', 'embed[]': [ @@ -73,7 +73,7 @@ export class RegistryOverviewService { ); } - getSchemaBlocks(schemaLink: string, questions: RegistrationQuestions): Observable { + getSchemaBlocks(schemaLink: string): Observable { const params = { 'page[size]': 100, page: 1, @@ -81,6 +81,41 @@ export class RegistryOverviewService { return this.jsonApiService .get(`${schemaLink}schema_blocks`, params) - .pipe(map((response) => response.data.map((block) => MapRegistrySchemaBlock(block.attributes, questions)))); + .pipe(map((response) => response.data.map((block) => MapRegistrySchemaBlock(block.attributes)))); + } + + withdrawRegistration(registryId: string, justification: string): Observable { + const payload = { + data: { + id: registryId, + attributes: { + withdrawal_justification: justification, + pending_withdrawal: true, + }, + relationships: {}, + type: 'registrations', + }, + }; + + return this.jsonApiService + .patch(`${environment.apiUrl}/registrations/${registryId}`, payload) + .pipe(map((response) => MapRegistryOverview(response))); + } + + makePublic(registryId: string): Observable { + const payload = { + data: { + id: registryId, + attributes: { + public: true, + }, + relationships: {}, + type: 'registrations', + }, + }; + + return this.jsonApiService + .patch(`${environment.apiUrl}/registrations/${registryId}`, payload) + .pipe(map((response) => MapRegistryOverview(response))); } } diff --git a/src/app/features/registry/store/registry-overview/registry-overview.actions.ts b/src/app/features/registry/store/registry-overview/registry-overview.actions.ts index 26754c848..1fb066991 100644 --- a/src/app/features/registry/store/registry-overview/registry-overview.actions.ts +++ b/src/app/features/registry/store/registry-overview/registry-overview.actions.ts @@ -26,3 +26,18 @@ export class GetSchemaBlocks { public questions: RegistrationQuestions ) {} } + +export class WithdrawRegistration { + static readonly type = '[Registry Overview] Withdraw Registration'; + + constructor( + public registryId: string, + public justification: string + ) {} +} + +export class MakePublic { + static readonly type = '[Registry Overview] Make Public'; + + constructor(public registryId: string) {} +} diff --git a/src/app/features/registry/store/registry-overview/registry-overview.state.ts b/src/app/features/registry/store/registry-overview/registry-overview.state.ts index 383178b22..92678450d 100644 --- a/src/app/features/registry/store/registry-overview/registry-overview.state.ts +++ b/src/app/features/registry/store/registry-overview/registry-overview.state.ts @@ -11,6 +11,8 @@ import { GetRegistryInstitutions, GetRegistrySubjects, GetSchemaBlocks, + MakePublic, + WithdrawRegistration, } from '@osf/features/registry/store/registry-overview'; import { RegistryOverviewStateModel } from './registry-overview.model'; @@ -64,7 +66,9 @@ export class RegistryOverviewState { error: null, }, }); - ctx.dispatch(new GetSchemaBlocks(registryOverview.registrationSchemaLink, registryOverview.questions)); + if (registryOverview?.registrationSchemaLink && registryOverview?.questions) { + ctx.dispatch(new GetSchemaBlocks(registryOverview.registrationSchemaLink, registryOverview.questions)); + } }, }), catchError((error) => this.handleError(ctx, 'registry', error)) @@ -133,7 +137,7 @@ export class RegistryOverviewState { }, }); - return this.registryOverviewService.getSchemaBlocks(action.schemaLink, action.questions).pipe( + return this.registryOverviewService.getSchemaBlocks(action.schemaLink).pipe( tap({ next: (schemaBlocks) => { ctx.patchState({ @@ -149,6 +153,64 @@ export class RegistryOverviewState { ); } + @Action(WithdrawRegistration) + withdrawRegistration(ctx: StateContext, action: WithdrawRegistration) { + const state = ctx.getState(); + ctx.patchState({ + registry: { + ...state.registry, + isLoading: true, + }, + }); + + return this.registryOverviewService.withdrawRegistration(action.registryId, action.justification).pipe( + tap({ + next: (registryOverview) => { + ctx.patchState({ + registry: { + data: registryOverview, + isLoading: false, + error: null, + }, + }); + if (registryOverview?.registrationSchemaLink && registryOverview?.questions) { + ctx.dispatch(new GetSchemaBlocks(registryOverview.registrationSchemaLink, registryOverview.questions)); + } + }, + }), + catchError((error) => this.handleError(ctx, 'registry', error)) + ); + } + + @Action(MakePublic) + makePublic(ctx: StateContext, action: MakePublic) { + const state = ctx.getState(); + ctx.patchState({ + registry: { + ...state.registry, + isLoading: true, + }, + }); + + return this.registryOverviewService.makePublic(action.registryId).pipe( + tap({ + next: (registryOverview) => { + ctx.patchState({ + registry: { + data: registryOverview, + isLoading: false, + error: null, + }, + }); + if (registryOverview?.registrationSchemaLink && registryOverview?.questions) { + ctx.dispatch(new GetSchemaBlocks(registryOverview.registrationSchemaLink, registryOverview.questions)); + } + }, + }), + catchError((error) => this.handleError(ctx, 'registry', error)) + ); + } + private handleError( ctx: StateContext, section: 'registry' | 'subjects' | 'institutions' | 'schemaBlocks', diff --git a/src/app/shared/components/data-resources/data-resources.component.html b/src/app/shared/components/data-resources/data-resources.component.html new file mode 100644 index 000000000..08df6fdea --- /dev/null +++ b/src/app/shared/components/data-resources/data-resources.component.html @@ -0,0 +1,42 @@ + diff --git a/src/app/shared/components/data-resources/data-resources.component.scss b/src/app/shared/components/data-resources/data-resources.component.scss new file mode 100644 index 000000000..95189c74d --- /dev/null +++ b/src/app/shared/components/data-resources/data-resources.component.scss @@ -0,0 +1,13 @@ +@use "/assets/styles/variables" as var; + +.icon-container { + color: var.$dark-blue-1; + display: flex; + align-items: center; + column-gap: 0.5rem; + + &:hover { + text-decoration: none; + color: var.$pr-blue-1; + } +} diff --git a/src/app/shared/components/data-resources/data-resources.component.spec.ts b/src/app/shared/components/data-resources/data-resources.component.spec.ts new file mode 100644 index 000000000..199f7f58a --- /dev/null +++ b/src/app/shared/components/data-resources/data-resources.component.spec.ts @@ -0,0 +1,22 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { DataResourcesComponent } from './data-resources.component'; + +describe('DataResourcesComponent', () => { + let component: DataResourcesComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [DataResourcesComponent], + }).compileComponents(); + + fixture = TestBed.createComponent(DataResourcesComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/shared/components/data-resources/data-resources.component.ts b/src/app/shared/components/data-resources/data-resources.component.ts new file mode 100644 index 000000000..8aaf2c13d --- /dev/null +++ b/src/app/shared/components/data-resources/data-resources.component.ts @@ -0,0 +1,20 @@ +import { TranslatePipe } from '@ngx-translate/core'; + +import { ChangeDetectionStrategy, Component, HostBinding, input } from '@angular/core'; + +@Component({ + selector: 'osf-data-resources', + imports: [TranslatePipe], + templateUrl: './data-resources.component.html', + styleUrl: './data-resources.component.scss', + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class DataResourcesComponent { + @HostBinding('class') classes = 'flex-1 flex'; + resourceId = input(); + hasData = input(); + hasAnalyticCode = input(); + hasMaterials = input(); + hasPapers = input(); + hasSupplements = input(); +} diff --git a/src/app/shared/components/index.ts b/src/app/shared/components/index.ts index 80d1049bc..be0ce5306 100644 --- a/src/app/shared/components/index.ts +++ b/src/app/shared/components/index.ts @@ -2,6 +2,7 @@ export { AddProjectFormComponent } from './add-project-form/add-project-form.com export { BarChartComponent } from './bar-chart/bar-chart.component'; export { CopyButtonComponent } from './copy-button/copy-button.component'; export { CustomPaginatorComponent } from './custom-paginator/custom-paginator.component'; +export { DataResourcesComponent } from './data-resources/data-resources.component'; export { EducationHistoryComponent } from './education-history/education-history.component'; export { EducationHistoryDialogComponent } from './education-history-dialog/education-history-dialog.component'; export { EmploymentHistoryComponent } from './employment-history/employment-history.component'; diff --git a/src/app/shared/components/resource-card/resource-card.component.html b/src/app/shared/components/resource-card/resource-card.component.html index b2e26060f..a5685b933 100644 --- a/src/app/shared/components/resource-card/resource-card.component.html +++ b/src/app/shared/components/resource-card/resource-card.component.html @@ -1,7 +1,7 @@
    - +
    +
    + @if (item().description) {

    {{ 'resourceCard.labels.description' | translate }} {{ item().description }}

    } diff --git a/src/app/shared/components/resource-card/resource-card.component.scss b/src/app/shared/components/resource-card/resource-card.component.scss index 572744b3b..77961708f 100644 --- a/src/app/shared/components/resource-card/resource-card.component.scss +++ b/src/app/shared/components/resource-card/resource-card.component.scss @@ -84,8 +84,12 @@ .content { display: flex; flex-direction: column; - row-gap: 1.7rem; + gap: 1.7rem; + padding-top: 1.7rem; + } + + .break-line { + border: none; border-top: 1px solid var.$grey-2; - padding-top: 1rem; } } diff --git a/src/app/shared/components/resource-card/resource-card.component.ts b/src/app/shared/components/resource-card/resource-card.component.ts index 8a2c0bc21..e867f5726 100644 --- a/src/app/shared/components/resource-card/resource-card.component.ts +++ b/src/app/shared/components/resource-card/resource-card.component.ts @@ -10,6 +10,7 @@ import { ChangeDetectionStrategy, Component, inject, model } from '@angular/core import { toSignal } from '@angular/core/rxjs-interop'; import { Router } from '@angular/router'; +import { DataResourcesComponent } from '@shared/components/data-resources/data-resources.component'; import { ResourceType } from '@shared/enums'; import { Resource } from '@shared/models'; import { ResourceCardService } from '@shared/services'; @@ -26,6 +27,7 @@ import { IS_XSMALL } from '@shared/utils'; NgOptimizedImage, Skeleton, TranslatePipe, + DataResourcesComponent, ], templateUrl: './resource-card.component.html', styleUrl: './resource-card.component.scss', diff --git a/src/app/shared/enums/index.ts b/src/app/shared/enums/index.ts index 5af34c16c..c2e42104c 100644 --- a/src/app/shared/enums/index.ts +++ b/src/app/shared/enums/index.ts @@ -9,9 +9,12 @@ export * from './file-menu-type.enum'; export * from './filter-type.enum'; export * from './get-resources-request-type.enum'; export * from './profile-addons-stepper.enum'; +export * from './registration-review-states.enum'; +export * from './registry-status.enum'; export * from './resource-tab.enum'; export * from './resource-type.enum'; export * from './reusable-filter-type.enum'; +export * from './revision-review-states.enum'; export * from './share-indexing.enum'; export * from './sort-order.enum'; export * from './sort-type.enum'; diff --git a/src/app/shared/enums/registration-review-states.enum.ts b/src/app/shared/enums/registration-review-states.enum.ts new file mode 100644 index 000000000..9f03aeed5 --- /dev/null +++ b/src/app/shared/enums/registration-review-states.enum.ts @@ -0,0 +1,10 @@ +export enum RegistrationReviewStates { + Initial = 'initial', + Pending = 'pending', + Accepted = 'accepted', + Rejected = 'rejected', + Withdrawn = 'withdrawn', + PendingEmbargoTermination = 'pending_embargo_termination', + PendingWithdrawRequest = 'pending_withdraw_request', + PendingWithdraw = 'pending_withdraw', +} diff --git a/src/app/shared/enums/registry-status.enum.ts b/src/app/shared/enums/registry-status.enum.ts new file mode 100644 index 000000000..96bc210eb --- /dev/null +++ b/src/app/shared/enums/registry-status.enum.ts @@ -0,0 +1,14 @@ +export enum RegistryStatus { + None = 'None', + PendingRegistrationApproval = 'pendingRegistrationApproval', + PendingEmbargoApproval = 'pendingEmbargoApproval', + Pending = 'pending', + Accepted = 'accepted', + Embargo = 'embargo', + PendingEmbargoTerminationApproval = 'pendingEmbargoTermination', + PendingWithdrawRequest = 'pendingWithdrawRequest', + PendingWithdraw = 'pendingWithdraw', + Unapproved = 'unapproved', + InProgress = 'inProgress', + PendingModeration = 'pendingModeration', +} diff --git a/src/app/shared/enums/revision-review-states.enum.ts b/src/app/shared/enums/revision-review-states.enum.ts new file mode 100644 index 000000000..8ede37c87 --- /dev/null +++ b/src/app/shared/enums/revision-review-states.enum.ts @@ -0,0 +1,6 @@ +export enum RevisionReviewStates { + Unapproved = 'unapproved', + RevisionInProgress = 'in_progress', + RevisionPendingModeration = 'pending_moderation', + Approved = 'approved', +} diff --git a/src/app/shared/mappers/index.ts b/src/app/shared/mappers/index.ts index 711de153a..dd0b841b1 100644 --- a/src/app/shared/mappers/index.ts +++ b/src/app/shared/mappers/index.ts @@ -2,6 +2,7 @@ export * from './addon.mapper'; export * from './filters'; export * from './institutions'; export * from './licenses.mapper'; +export * from './registry'; export * from './resource-card'; export * from './resource-overview.mappers'; export * from './subjects'; diff --git a/src/app/shared/mappers/registry/index.ts b/src/app/shared/mappers/registry/index.ts new file mode 100644 index 000000000..b4034dc67 --- /dev/null +++ b/src/app/shared/mappers/registry/index.ts @@ -0,0 +1 @@ +export * from './map-registry-status.mapper'; diff --git a/src/app/shared/mappers/registry/map-registry-status.mapper.ts b/src/app/shared/mappers/registry/map-registry-status.mapper.ts new file mode 100644 index 000000000..d34b52371 --- /dev/null +++ b/src/app/shared/mappers/registry/map-registry-status.mapper.ts @@ -0,0 +1,30 @@ +import { RegistryOverviewJsonApiAttributes } from '@osf/features/registry/models'; +import { RegistrationReviewStates, RegistryStatus, RevisionReviewStates } from '@shared/enums'; + +export function MapRegistryStatus(registry: RegistryOverviewJsonApiAttributes): RegistryStatus { + if (registry.pending_embargo_approval) { + return RegistryStatus.PendingEmbargoApproval; + } else if (registry.pending_embargo_termination_approval) { + return RegistryStatus.PendingEmbargoTerminationApproval; + } else if (registry.embargoed) { + return RegistryStatus.Embargo; + } else if (registry.pending_registration_approval) { + return RegistryStatus.PendingRegistrationApproval; + } else if (registry.revision_state === RevisionReviewStates.Unapproved) { + return RegistryStatus.Unapproved; + } else if (registry.revision_state === RevisionReviewStates.RevisionInProgress) { + return RegistryStatus.InProgress; + } else if (registry.revision_state === RevisionReviewStates.RevisionPendingModeration) { + return RegistryStatus.PendingModeration; + } else if (registry.review_state === RegistrationReviewStates.Accepted) { + return RegistryStatus.Accepted; + } else if (registry.review_state === RegistrationReviewStates.Pending) { + return RegistryStatus.Pending; + } else if (registry.review_state === RegistrationReviewStates.PendingWithdraw) { + return RegistryStatus.PendingWithdraw; + } else if (registry.review_state === RegistrationReviewStates.PendingWithdrawRequest) { + return RegistryStatus.PendingWithdrawRequest; + } else { + return RegistryStatus.None; + } +} diff --git a/src/app/shared/models/confirmation-options.model.ts b/src/app/shared/models/confirmation-options.model.ts index 01b0d28e8..8afbb57e5 100644 --- a/src/app/shared/models/confirmation-options.model.ts +++ b/src/app/shared/models/confirmation-options.model.ts @@ -19,7 +19,7 @@ export interface AcceptConfirmationOptions { messageParams?: any; acceptLabelKey?: string; onConfirm: () => void; - onReject: () => void; + onReject?: () => void; } export interface ContinueConfirmationOptions { diff --git a/src/app/shared/services/custom-confirmation.service.ts b/src/app/shared/services/custom-confirmation.service.ts index 49028e7d5..51451e050 100644 --- a/src/app/shared/services/custom-confirmation.service.ts +++ b/src/app/shared/services/custom-confirmation.service.ts @@ -50,7 +50,11 @@ export class CustomConfirmationService { options.onConfirm(); }, reject: () => { - options.onReject(); + if (options.onReject) { + options.onReject(); + } else { + this.confirmationService.close(); + } }, }); } diff --git a/src/assets/i18n/en.json b/src/assets/i18n/en.json index 0b763e9d1..6dab2f2a8 100644 --- a/src/assets/i18n/en.json +++ b/src/assets/i18n/en.json @@ -28,7 +28,9 @@ "deselect": "Deselect", "yes": "Yes", "no": "No", - "update": "Update" + "update": "Update", + "continue update": "Continue update", + "withdraw": "Withdraw" }, "search": { "title": "Search", @@ -65,7 +67,9 @@ "public": "Public", "title": "Title", "description": "Description", - "year": "Year" + "year": "Year", + "optional": "Optional", + "makePublic": "Make Public" }, "deleteConfirmation": { "header": "Delete", @@ -658,10 +662,12 @@ "confirmation": "Type the following to continue:" }, "fork": { - "header": "Fork This Project", + "headerProject": "Fork This Project", + "headerRegistry": "Fork This Registry", "confirmButton": "Fork", "cancelButton": "Cancel", - "message": "Are you sure you want to fork this project?" + "messageProject": "Are you sure you want to fork this project?", + "messageRegistry": "Are you sure you want to fork this registry?" }, "duplicate": { "header": "Duplicate Template", @@ -1641,10 +1647,67 @@ }, "registry": { "overview": { - "public": "Public Registration", "updates": "Updates", + "withdrawRegistration": "Withdraw registration", + "withdrawDescription": "Withdrawing a registration will remove its content from the OSF, but leave basic metadata behind. The title of a withdrawn registration and its contributor list will remain, as will justification or explanation of the withdrawal, should you wish to provide it. Withdrawn registrations will be marked with a \"withdrawn\" tag. This action is irreversible.", + "withdrawJustificationLabel": "Please provide your justification for withdrawing this registration.", + "makePublicMessage": "Are you sure you want make this registration public ?", "statuses": { - "frozen": "This public registration is a frozen, non-editable version of this" + "pendingRegistrationApproval": { + "text": "Pending approval", + "short": "Pending registration approval", + "long": "This registration is waiting for approval from its contributors." + }, + "pendingEmbargoApproval": { + "text": "Pending approval", + "short": "Pending embargo approval", + "long": "This registration is waiting for approval from its contributors. Once approved, the registration will remain private until the embargo ends." + }, + "pending": { + "text": "Pending moderation", + "short": "Pending moderator approval", + "long": "This registration is awaiting a decision by the registry moderators. An email will notify all registration contributors of the decision." + }, + "accepted": { + "text": "Public registration", + "short": "Public", + "long": "This public registration is a frozen, non-editable version of this." + }, + "embargo": { + "text": "Embargoed registration", + "short": "Embargoed", + "long": "This registration is embargoed. It will remain private until {embargoEndDate}." + }, + "pendingEmbargoTermination": { + "text": "Embargoed registration", + "short": "Pending embargo termination", + "long": "This registration is waiting for approval from its contributors to terminate its embargo and be made public." + }, + "pendingWithdrawRequest": { + "text": "Pending withdrawal", + "short": "Pending withdraw request", + "long": "This registration is waiting for approval to be withdrawn by its contributors." + }, + "pendingWithdraw": { + "text": "Pending withdrawal", + "short": "Pending withdrawal moderation", + "long": "This registration is waiting for approval to be withdrawn by registry moderators." + }, + "unapproved": { + "text": "Update pending", + "short": "Update pending approval", + "long": "This registration has an update that is waiting for approval from its contributors. Updates will be made available once approved." + }, + "inProgress": { + "text": "Update in progress", + "short": "Updates being made", + "long": "This registration is currently being updated by its contributors. Updates will be made available once approved." + }, + "pendingModeration": { + "text": "Update pending review", + "short": "Update pending moderator approval", + "long": "This registration has an update that is waiting for moderation. Updates will be made available once approved." + } }, "metadata": { "type": "Registration Type", @@ -1653,7 +1716,7 @@ "associatedProject": "Associated project" }, "original": "Original", - "last": "Last" + "latest": "Latest" } }, "truncatedText": {