diff --git a/src/app/core/constants/nav-items.constant.ts b/src/app/core/constants/nav-items.constant.ts index 5c247a216..e04dcc589 100644 --- a/src/app/core/constants/nav-items.constant.ts +++ b/src/app/core/constants/nav-items.constant.ts @@ -32,7 +32,7 @@ export const MENU_ITEMS: MenuItem[] = [ routerLinkActiveOptions: { exact: true }, }, { - routerLink: '/my-registrations', + routerLink: '/registries/my-registrations', label: 'navigation.registriesSubRoutes.myRegistrations', routerLinkActiveOptions: { exact: true }, }, diff --git a/src/app/features/project/registrations/components/registration-card/registration-card.component.html b/src/app/features/project/registrations/components/registration-card/registration-card.component.html deleted file mode 100644 index 6ac0530eb..000000000 --- a/src/app/features/project/registrations/components/registration-card/registration-card.component.html +++ /dev/null @@ -1,108 +0,0 @@ -
- -
-
- @if (registrationData().status === RegistrationStatus.WITHDRAWN) { - - } - - @if (registrationData().status === RegistrationStatus.IN_PROGRESS) { - - } - -

{{ registrationData().title }}

- - @if (registrationData().status === RegistrationStatus.IN_PROGRESS) { - - } - - @if (registrationData().status === RegistrationStatus.WITHDRAWN) { - - } -
- -
-
-
- {{ 'project.registrations.card.registrationTemplate' | translate }} - {{ registrationData().registrationSupplement }} -
- -
- {{ 'project.registrations.card.registry' | translate }} - {{ registrationData().registry }} -
- -
- {{ 'project.registrations.card.registered' | translate }} - {{ registrationData().dateRegistered }} -
- -
- {{ 'project.registrations.card.lastUpdated' | translate }} - {{ registrationData().dateModified }} -
- -
- {{ 'project.overview.metadata.contributors' | translate }}: - - @for (contributor of registrationData().contributors; track contributor.name) { - {{ contributor.name }} - @if (!$last) { - , - } - } - -
- -
- {{ 'project.registrations.card.description' | translate }} -

{{ registrationData().description }}

-
- -
- @if (registrationData().status === RegistrationStatus.DRAFT) { - - - - } @else { - - @if (registrationData().status === RegistrationStatus.IN_PROGRESS) { - - } - } -
-
- - @if (registrationData().status !== RegistrationStatus.DRAFT) { - - } -
-
-
-
diff --git a/src/app/features/project/registrations/components/registration-card/registration-card.component.ts b/src/app/features/project/registrations/components/registration-card/registration-card.component.ts deleted file mode 100644 index 86cc3d598..000000000 --- a/src/app/features/project/registrations/components/registration-card/registration-card.component.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { TranslatePipe } from '@ngx-translate/core'; - -import { Button } from 'primeng/button'; -import { Card } from 'primeng/card'; -import { Tag } from 'primeng/tag'; - -import { ChangeDetectionStrategy, Component, input } from '@angular/core'; - -import { RegistrationModel, RegistrationStatus } from '../../models'; - -@Component({ - selector: 'osf-registration-card', - imports: [Card, Button, Tag, TranslatePipe], - templateUrl: './registration-card.component.html', - styleUrl: './registration-card.component.scss', - changeDetection: ChangeDetectionStrategy.OnPush, -}) -export class RegistrationCardComponent { - readonly RegistrationStatus = RegistrationStatus; - readonly registrationData = input.required(); -} diff --git a/src/app/features/project/registrations/mappers/index.ts b/src/app/features/project/registrations/mappers/index.ts deleted file mode 100644 index 5afc367f2..000000000 --- a/src/app/features/project/registrations/mappers/index.ts +++ /dev/null @@ -1 +0,0 @@ -export { RegistrationsMapper } from './registrations.mapper'; diff --git a/src/app/features/project/registrations/mappers/registrations.mapper.ts b/src/app/features/project/registrations/mappers/registrations.mapper.ts deleted file mode 100644 index 932ca5774..000000000 --- a/src/app/features/project/registrations/mappers/registrations.mapper.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { RegistrationModel, RegistrationsGetResponse, RegistrationStatus } from '../models'; - -export class RegistrationsMapper { - static fromResponse(response: RegistrationsGetResponse): RegistrationModel { - return { - id: response.id, - type: response.type, - title: response.attributes?.title, - dateRegistered: response.attributes?.date_registered, - dateModified: response.attributes?.date_modified, - registrationSupplement: response.attributes?.registration_supplement, - registry: '', - description: response.attributes?.description, - withdrawn: response.attributes?.withdrawn, - lastFetched: Date.now(), - status: response.attributes?.withdrawn - ? RegistrationStatus.WITHDRAWN - : response.attributes?.date_modified - ? RegistrationStatus.IN_PROGRESS - : RegistrationStatus.DRAFT, - }; - } -} diff --git a/src/app/features/project/registrations/models/index.ts b/src/app/features/project/registrations/models/index.ts deleted file mode 100644 index af11e9261..000000000 --- a/src/app/features/project/registrations/models/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './registrations.model'; diff --git a/src/app/features/project/registrations/models/registrations.model.ts b/src/app/features/project/registrations/models/registrations.model.ts deleted file mode 100644 index a05067cb2..000000000 --- a/src/app/features/project/registrations/models/registrations.model.ts +++ /dev/null @@ -1,40 +0,0 @@ -export interface RegistrationsGetResponse { - id: string; - type: string; - attributes: RegistrationsAttributes; -} - -interface RegistrationsAttributes { - title: string; - date_registered: string; - date_modified: string; - registration_supplement: string; - description: string; - withdrawn: boolean; -} - -export interface RegistrationModel { - id: string; - type: string; - lastFetched?: number; - title: string; - dateRegistered: string; - dateModified: string; - registrationSupplement: string; - description: string; - withdrawn: boolean; - registry: string; - status: RegistrationStatus; - contributors?: Contributor[]; -} - -export interface Contributor { - name: string; - link?: string; -} -export enum RegistrationStatus { - DRAFT = 'draft', - IN_PROGRESS = 'in_progress', - WITHDRAWN = 'withdrawn', - SUBMITTED = 'submitted', -} diff --git a/src/app/features/project/registrations/registrations.component.ts b/src/app/features/project/registrations/registrations.component.ts index 07ddaffd4..208142a8d 100644 --- a/src/app/features/project/registrations/registrations.component.ts +++ b/src/app/features/project/registrations/registrations.component.ts @@ -12,8 +12,8 @@ import { FormsModule } from '@angular/forms'; import { ActivatedRoute } from '@angular/router'; import { LoadingSpinnerComponent, SubHeaderComponent } from '@osf/shared/components'; +import { RegistrationCardComponent } from '@osf/shared/components/registration-card/registration-card.component'; -import { RegistrationCardComponent } from './components'; import { GetRegistrations, RegistrationsSelectors } from './store'; @Component({ diff --git a/src/app/features/project/registrations/services/registrations.service.ts b/src/app/features/project/registrations/services/registrations.service.ts index 8ce21ecfa..706a54f01 100644 --- a/src/app/features/project/registrations/services/registrations.service.ts +++ b/src/app/features/project/registrations/services/registrations.service.ts @@ -2,13 +2,10 @@ import { map, Observable } from 'rxjs'; import { inject, Injectable } from '@angular/core'; -import { JsonApiResponse } from '@osf/core/models'; +import { JsonApiResponseWithPaging } from '@osf/core/models'; import { JsonApiService } from '@osf/core/services'; - -import { submittedRegistrations } from '../mock-data'; -import { RegistrationModel, RegistrationsGetResponse } from '../models'; - -import { RegistrationsMapper } from './../mappers'; +import { RegistrationMapper } from '@osf/shared/mappers/registration'; +import { RegistrationCard, RegistrationDataJsonApi } from '@osf/shared/models'; import { environment } from 'src/environments/environment'; @@ -16,19 +13,23 @@ import { environment } from 'src/environments/environment'; providedIn: 'root', }) export class RegistrationsService { - #jsonApiService = inject(JsonApiService); + private readonly jsonApiService = inject(JsonApiService); - getRegistrations(projectId: string): Observable { + getRegistrations(projectId: string): Observable<{ data: RegistrationCard[]; totalCount: number }> { const params: Record = { embed: 'contributors', }; const url = `${environment.apiUrl}/nodes/${projectId}/linked_by_registrations/`; - return this.#jsonApiService.get>(url, params).pipe( + return this.jsonApiService.get>(url, params).pipe( map((response) => { - return response.data.length - ? response.data.map((registration) => RegistrationsMapper.fromResponse(registration)) - : submittedRegistrations; + const data = response.data.map((registration: RegistrationDataJsonApi) => + RegistrationMapper.fromRegistrationToRegistrationCard(registration) + ); + return { + data, + totalCount: response.links.meta?.total, + }; }) ); } diff --git a/src/app/features/project/registrations/store/registrations.model.ts b/src/app/features/project/registrations/store/registrations.model.ts index 545e87bb1..63fb02293 100644 --- a/src/app/features/project/registrations/store/registrations.model.ts +++ b/src/app/features/project/registrations/store/registrations.model.ts @@ -1,7 +1,6 @@ -import { AsyncStateModel } from '@osf/shared/models/store'; - -import { RegistrationModel } from '../models'; +import { RegistrationCard } from '@osf/shared/models'; +import { AsyncStateWithTotalCount } from '@osf/shared/models/store'; export interface RegistrationsStateModel { - registrations: AsyncStateModel; + registrations: AsyncStateWithTotalCount; } diff --git a/src/app/features/project/registrations/store/registrations.state.ts b/src/app/features/project/registrations/store/registrations.state.ts index c9ff3db45..b51e1e032 100644 --- a/src/app/features/project/registrations/store/registrations.state.ts +++ b/src/app/features/project/registrations/store/registrations.state.ts @@ -15,13 +15,14 @@ import { RegistrationsStateModel } from './registrations.model'; registrations: { data: [], isLoading: false, - error: '', + error: null, + totalCount: 0, }, }, }) @Injectable() export class RegistrationsState { - #registrationsService = inject(RegistrationsService); + private readonly registrationsService = inject(RegistrationsService); @Action(GetRegistrations) getRegistrations(ctx: StateContext, action: GetRegistrations) { @@ -31,15 +32,16 @@ export class RegistrationsState { registrations: { ...state.registrations, isLoading: true, error: null }, }); - return this.#registrationsService.getRegistrations(action.projectId).pipe( + return this.registrationsService.getRegistrations(action.projectId).pipe( tap((registrations) => { const state = ctx.getState(); ctx.setState({ ...state, registrations: { - data: registrations, + data: registrations.data, isLoading: false, - error: '', + error: null, + totalCount: registrations.totalCount, }, }); }), diff --git a/src/app/features/registries/components/drafts/drafts.component.ts b/src/app/features/registries/components/drafts/drafts.component.ts index 6375e873c..b5056408b 100644 --- a/src/app/features/registries/components/drafts/drafts.component.ts +++ b/src/app/features/registries/components/drafts/drafts.component.ts @@ -13,7 +13,7 @@ import { StepOption } from '@osf/shared/models'; import { LoaderService } from '@osf/shared/services'; import { ContributorsSelectors, SubjectsSelectors } from '@osf/shared/stores'; -import { defaultSteps } from '../../constants'; +import { DEFAULT_STEPS } from '../../constants'; import { FetchDraft, FetchSchemaBlocks, RegistriesSelectors, UpdateStepValidation } from '../../store'; @Component({ @@ -62,7 +62,7 @@ export class DraftsComponent { isLoaded = false; steps: Signal = computed(() => { - this.defaultSteps = defaultSteps.map((step) => ({ + this.defaultSteps = DEFAULT_STEPS.map((step) => ({ ...step, label: this.translateService.instant(step.label), invalid: this.stepsValidation()?.[step.index]?.invalid || false, diff --git a/src/app/features/registries/components/metadata/metadata.component.ts b/src/app/features/registries/components/metadata/metadata.component.ts index d8c95bb7a..f7310dff1 100644 --- a/src/app/features/registries/components/metadata/metadata.component.ts +++ b/src/app/features/registries/components/metadata/metadata.component.ts @@ -15,8 +15,7 @@ import { ActivatedRoute, Router } from '@angular/router'; import { TextInputComponent } from '@osf/shared/components'; import { INPUT_VALIDATION_MESSAGES, InputLimits } from '@osf/shared/constants'; -import { ContributorModel, SubjectModel } from '@osf/shared/models'; -import { DraftRegistrationModel } from '@osf/shared/models/registration'; +import { ContributorModel, DraftRegistrationModel, SubjectModel } from '@osf/shared/models'; import { CustomConfirmationService } from '@osf/shared/services'; import { ContributorsSelectors, SubjectsSelectors } from '@osf/shared/stores'; import { CustomValidators, findChangedFields } from '@osf/shared/utils'; diff --git a/src/app/features/registries/constants/defaultSteps.ts b/src/app/features/registries/constants/default-steps.ts similarity index 85% rename from src/app/features/registries/constants/defaultSteps.ts rename to src/app/features/registries/constants/default-steps.ts index 417ed494b..bafca0734 100644 --- a/src/app/features/registries/constants/defaultSteps.ts +++ b/src/app/features/registries/constants/default-steps.ts @@ -1,6 +1,6 @@ import { StepOption } from '@osf/shared/models'; -export const defaultSteps: StepOption[] = [ +export const DEFAULT_STEPS: StepOption[] = [ { index: 0, label: 'navigation.project.metadata', diff --git a/src/app/features/registries/constants/index.ts b/src/app/features/registries/constants/index.ts index 92e572c61..778cd4ea5 100644 --- a/src/app/features/registries/constants/index.ts +++ b/src/app/features/registries/constants/index.ts @@ -1 +1,2 @@ -export * from './defaultSteps'; +export * from './default-steps'; +export * from './registrations-tabs'; diff --git a/src/app/features/registries/constants/registrations-tabs.ts b/src/app/features/registries/constants/registrations-tabs.ts new file mode 100644 index 000000000..f1866fb61 --- /dev/null +++ b/src/app/features/registries/constants/registrations-tabs.ts @@ -0,0 +1,14 @@ +import { TabOption } from '@osf/shared/models'; + +import { RegistrationTab } from '../enums'; + +export const REGISTRATIONS_TABS: TabOption[] = [ + { + label: 'common.labels.drafts', + value: RegistrationTab.Drafts, + }, + { + label: 'common.labels.submitted', + value: RegistrationTab.Submitted, + }, +]; diff --git a/src/app/features/registries/enums/index.ts b/src/app/features/registries/enums/index.ts index f7bf73467..70c02a5da 100644 --- a/src/app/features/registries/enums/index.ts +++ b/src/app/features/registries/enums/index.ts @@ -1,3 +1,4 @@ export * from './block-type.enum'; export * from './field-type.enum'; +export * from './registration-tab.enum'; export * from './submit-type.enum'; diff --git a/src/app/features/registries/enums/registration-tab.enum.ts b/src/app/features/registries/enums/registration-tab.enum.ts new file mode 100644 index 000000000..67eeac498 --- /dev/null +++ b/src/app/features/registries/enums/registration-tab.enum.ts @@ -0,0 +1,4 @@ +export enum RegistrationTab { + Drafts, + Submitted, +} diff --git a/src/app/features/registries/mappers/registration.mapper.ts b/src/app/features/registries/mappers/registration.mapper.ts deleted file mode 100644 index 5b6e51326..000000000 --- a/src/app/features/registries/mappers/registration.mapper.ts +++ /dev/null @@ -1,63 +0,0 @@ -import { DraftRegistrationModel, RegistrationModel } from '@osf/shared/models/registration'; - -import { RegistrationDataJsonApi } from '../models'; - -export class RegistrationMapper { - static fromDraftRegistrationResponse(response: RegistrationDataJsonApi): DraftRegistrationModel { - return { - id: response.id, - title: response.attributes.title, - description: response.attributes.description, - registrationSchemaId: response.relationships.registration_schema?.data?.id || '', - license: { - id: response.relationships.license?.data?.id || '', - options: response.attributes.node_license - ? { - year: response.attributes.node_license.year, - copyrightHolders: response.attributes.node_license.copyright_holders.join(','), - } - : null, - }, - tags: response.attributes.tags || [], - stepsData: response.attributes.registration_responses || {}, - branchedFrom: response.relationships.branched_from?.data?.id, - providerId: response.relationships.provider?.data?.id || '', - }; - } - - static fromRegistrationResponse(response: RegistrationDataJsonApi): RegistrationModel { - return { - id: response.id, - type: 'registration', - } as RegistrationModel; - } - - static toRegistrationPayload(draftId: string, embargoDate: string, providerId: string, projectId?: string) { - return { - data: { - type: 'registrations', - attributes: { - embargo_end_date: embargoDate, - draft_registration: draftId, - }, - relationships: { - registered_from: projectId - ? { - data: { - type: 'nodes', - id: projectId, - }, - } - : undefined, - - provider: { - data: { - type: 'registration-providers', - id: providerId, - }, - }, - }, - }, - }; - } -} diff --git a/src/app/features/registries/models/index.ts b/src/app/features/registries/models/index.ts index 74e5dbaf7..0b5107abd 100644 --- a/src/app/features/registries/models/index.ts +++ b/src/app/features/registries/models/index.ts @@ -3,5 +3,4 @@ export * from './project'; export * from './projects-json-api.model'; export * from './provider-schema.model'; export * from './providers-json-api.model'; -export * from './registration-json-api.model'; export * from './schema-blocks-json-api.model'; diff --git a/src/app/features/registries/models/registration-json-api.model.ts b/src/app/features/registries/models/registration-json-api.model.ts deleted file mode 100644 index 255122fe4..000000000 --- a/src/app/features/registries/models/registration-json-api.model.ts +++ /dev/null @@ -1,65 +0,0 @@ -import { ApiData, MetaJsonApi, PaginationLinksJsonApi } from '@osf/core/models'; -import { LicenseRecordJsonApi } from '@osf/shared/models'; - -export interface RegistrationResponseJsonApi { - data: RegistrationDataJsonApi; - meta: MetaJsonApi; - links: PaginationLinksJsonApi; -} - -export type RegistrationDataJsonApi = ApiData< - RegistrationAttributesJsonApi, - null, - RegistrationRelationshipsJsonApi, - null ->; - -export interface RegistrationAttributesJsonApi { - category: string; - current_user_permissions: string[]; - date_created: string; - datetime_updated: string; - description: string; - has_project: boolean; - node_license: LicenseRecordJsonApi; - registration_metadata: Record; - registration_responses: Record; - tags: string[]; - title: string; -} - -export interface RegistrationRelationshipsJsonApi { - registration_schema?: { - data: { - id: string; - type: 'registration-schemas'; - }; - }; - license?: { - data: { - id: string; - type: 'licenses'; - }; - }; - provider?: { - data: { - id: string; - type: 'registration-providers'; - }; - }; - branched_from?: { - data: { - id: string; - type: 'nodes'; - }; - }; -} - -export interface RegistrationPayloadJsonApi { - data: { - type: 'draft_registrations'; - id: string; - relationships?: RegistrationRelationshipsJsonApi; - attributes?: Partial; - }; -} diff --git a/src/app/features/registries/pages/index.ts b/src/app/features/registries/pages/index.ts index d5bc067cd..a5e6a80e0 100644 --- a/src/app/features/registries/pages/index.ts +++ b/src/app/features/registries/pages/index.ts @@ -1 +1,2 @@ +export * from './my-registrations/my-registrations.component'; export * from './registries-landing/registries-landing.component'; diff --git a/src/app/features/registries/pages/my-registrations/my-registrations.component.html b/src/app/features/registries/pages/my-registrations/my-registrations.component.html new file mode 100644 index 000000000..3ca18a7ef --- /dev/null +++ b/src/app/features/registries/pages/my-registrations/my-registrations.component.html @@ -0,0 +1,91 @@ +
+
+ +
+
+ + @if (!isMobile()) { + + @for (tab of tabOptions; track tab.value) { + {{ tab.label | translate }} + } + + } + + + @if (isMobile()) { + + } + + + @if (isDraftRegistrationsLoading()) { + @for (item of skeletons; track $index) { + + } + } @else { + @if (draftRegistrationsTotalCount() === 0) { + + } + @for (registration of draftRegistrations(); track registration.id) { + + } + @if (draftRegistrationsTotalCount() > itemsPerPage) { + + } + } + + + + + @if (isSubmittedRegistrationsLoading()) { + @for (item of skeletons; track $index) { + + } + } @else { + @if (submittedRegistrationsTotalCount() === 0) { + + } + @for (registration of submittedRegistrations(); track registration.registry) { + + } + @if (submittedRegistrationsTotalCount() > itemsPerPage) { + + } + } + + + +
+
+ + +
+ {{ 'registries.noRegistrations' | translate }} + + +
+
diff --git a/src/app/features/registries/pages/my-registrations/my-registrations.component.scss b/src/app/features/registries/pages/my-registrations/my-registrations.component.scss new file mode 100644 index 000000000..e69de29bb diff --git a/src/app/features/registries/pages/my-registrations/my-registrations.component.spec.ts b/src/app/features/registries/pages/my-registrations/my-registrations.component.spec.ts new file mode 100644 index 000000000..cdf878f0e --- /dev/null +++ b/src/app/features/registries/pages/my-registrations/my-registrations.component.spec.ts @@ -0,0 +1,22 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { MyRegistrationsComponent } from './my-registrations.component'; + +describe('MyRegistrationsComponent', () => { + let component: MyRegistrationsComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [MyRegistrationsComponent], + }).compileComponents(); + + fixture = TestBed.createComponent(MyRegistrationsComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/features/registries/pages/my-registrations/my-registrations.component.ts b/src/app/features/registries/pages/my-registrations/my-registrations.component.ts new file mode 100644 index 000000000..ebf9690fb --- /dev/null +++ b/src/app/features/registries/pages/my-registrations/my-registrations.component.ts @@ -0,0 +1,129 @@ +import { createDispatchMap, select } from '@ngxs/store'; + +import { TranslatePipe } from '@ngx-translate/core'; + +import { Button } from 'primeng/button'; +import { PaginatorState } from 'primeng/paginator'; +import { Skeleton } from 'primeng/skeleton'; +import { TabsModule } from 'primeng/tabs'; + +import { NgTemplateOutlet } from '@angular/common'; +import { ChangeDetectionStrategy, Component, effect, inject, signal } from '@angular/core'; +import { toSignal } from '@angular/core/rxjs-interop'; +import { FormsModule } from '@angular/forms'; +import { ActivatedRoute, Router, RouterLink } from '@angular/router'; + +import { CustomPaginatorComponent, SelectComponent, SubHeaderComponent } from '@osf/shared/components'; +import { RegistrationCardComponent } from '@osf/shared/components/registration-card/registration-card.component'; +import { CustomConfirmationService, ToastService } from '@osf/shared/services'; +import { IS_XSMALL } from '@osf/shared/utils'; + +import { REGISTRATIONS_TABS } from '../../constants/registrations-tabs'; +import { RegistrationTab } from '../../enums'; +import { DeleteDraft, FetchDraftRegistrations, FetchSubmittedRegistrations, RegistriesSelectors } from '../../store'; + +@Component({ + selector: 'osf-my-registrations', + imports: [ + SubHeaderComponent, + TranslatePipe, + TabsModule, + FormsModule, + SelectComponent, + RegistrationCardComponent, + CustomPaginatorComponent, + Skeleton, + Button, + RouterLink, + NgTemplateOutlet, + ], + templateUrl: './my-registrations.component.html', + styleUrl: './my-registrations.component.scss', + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class MyRegistrationsComponent { + private router = inject(Router); + private readonly route = inject(ActivatedRoute); + + private readonly customConfirmationService = inject(CustomConfirmationService); + private readonly toastService = inject(ToastService); + + protected readonly isMobile = toSignal(inject(IS_XSMALL)); + protected readonly tabOptions = REGISTRATIONS_TABS; + + protected draftRegistrations = select(RegistriesSelectors.getDraftRegistrations); + protected draftRegistrationsTotalCount = select(RegistriesSelectors.getDraftRegistrationsTotalCount); + protected isDraftRegistrationsLoading = select(RegistriesSelectors.isDraftRegistrationsLoading); + protected submittedRegistrations = select(RegistriesSelectors.getSubmittedRegistrations); + protected submittedRegistrationsTotalCount = select(RegistriesSelectors.getSubmittedRegistrationsTotalCount); + protected isSubmittedRegistrationsLoading = select(RegistriesSelectors.isSubmittedRegistrationsLoading); + + protected actions = createDispatchMap({ + getDraftRegistrations: FetchDraftRegistrations, + getSubmittedRegistrations: FetchSubmittedRegistrations, + deleteDraft: DeleteDraft, + }); + + protected readonly RegistrationTab = RegistrationTab; + + readonly provider = 'osf'; + + selectedTab = signal(RegistrationTab.Submitted); + itemsPerPage = 10; + skeletons = [...Array(8)]; + draftFirst = 0; + submittedFirst = 0; + + constructor() { + const initialTab = this.route.snapshot.queryParams['tab']; + if (initialTab == 'drafts') { + this.selectedTab.set(RegistrationTab.Drafts); + } else { + this.selectedTab.set(RegistrationTab.Submitted); + } + + effect(() => { + const tab = this.selectedTab(); + + if (tab === 0) { + this.actions.getDraftRegistrations(); + } else { + this.actions.getSubmittedRegistrations(); + } + this.router.navigate([], { + relativeTo: this.route, + queryParams: { tab: tab === RegistrationTab.Drafts ? 'drafts' : 'submitted' }, + queryParamsHandling: 'merge', + }); + }); + } + + goToCreateRegistration(): void { + this.router.navigate([`/registries/${this.provider}/new`]); + } + + onDeleteDraft(id: string): void { + this.customConfirmationService.confirmDelete({ + headerKey: 'registries.deleteDraft', + messageKey: 'registries.confirmDeleteDraft', + onConfirm: () => { + this.actions.deleteDraft(id).subscribe({ + next: () => { + this.toastService.showSuccess('registries.successDeleteDraft'); + this.actions.getDraftRegistrations(); + }, + }); + }, + }); + } + + onDraftsPageChange(event: PaginatorState): void { + this.actions.getDraftRegistrations(event.page! + 1); + this.draftFirst = event.first!; + } + + onSubmittedPageChange(event: PaginatorState): void { + this.actions.getSubmittedRegistrations(event.page! + 1); + this.submittedFirst = event.first!; + } +} diff --git a/src/app/features/registries/registries.routes.ts b/src/app/features/registries/registries.routes.ts index 5c815ae2f..fad5575b9 100644 --- a/src/app/features/registries/registries.routes.ts +++ b/src/app/features/registries/registries.routes.ts @@ -32,6 +32,10 @@ export const registriesRoutes: Routes = [ path: 'overview', loadComponent: () => import('@osf/features/registries/pages').then((c) => c.RegistriesLandingComponent), }, + { + path: 'my-registrations', + loadComponent: () => import('@osf/features/registries/pages').then((c) => c.MyRegistrationsComponent), + }, { path: ':id/moderation', loadComponent: () => diff --git a/src/app/features/registries/services/licenses.service.ts b/src/app/features/registries/services/licenses.service.ts index cb8023884..6fbbdef10 100644 --- a/src/app/features/registries/services/licenses.service.ts +++ b/src/app/features/registries/services/licenses.service.ts @@ -3,12 +3,17 @@ import { map, Observable } from 'rxjs'; import { inject, Injectable } from '@angular/core'; import { JsonApiService } from '@osf/core/services'; -import { License, LicenseOptions, LicensesResponseJsonApi } from '@osf/shared/models'; -import { DraftRegistrationModel } from '@osf/shared/models/registration'; +import { RegistrationMapper } from '@osf/shared/mappers/registration'; +import { + CreateRegistrationPayloadJsonApi, + DraftRegistrationDataJsonApi, + DraftRegistrationModel, + License, + LicenseOptions, + LicensesResponseJsonApi, +} from '@osf/shared/models'; import { LicensesMapper } from '../mappers'; -import { RegistrationMapper } from '../mappers/registration.mapper'; -import { RegistrationDataJsonApi, RegistrationPayloadJsonApi } from '../models'; import { environment } from 'src/environments/environment'; @@ -56,7 +61,7 @@ export class LicensesService { licenseId: string, licenseOptions?: LicenseOptions ): Observable { - const payload: RegistrationPayloadJsonApi = { + const payload: CreateRegistrationPayloadJsonApi = { data: { type: 'draft_registrations', id: registrationId, @@ -80,7 +85,7 @@ export class LicensesService { }; return this.jsonApiService - .patch(`${this.apiUrl}/draft_registrations/${registrationId}/`, payload) + .patch(`${this.apiUrl}/draft_registrations/${registrationId}/`, payload) .pipe(map((response) => RegistrationMapper.fromDraftRegistrationResponse(response))); } } diff --git a/src/app/features/registries/services/registries.service.ts b/src/app/features/registries/services/registries.service.ts index 9efabfa65..e3d1f3aa7 100644 --- a/src/app/features/registries/services/registries.service.ts +++ b/src/app/features/registries/services/registries.service.ts @@ -2,19 +2,23 @@ import { map, Observable } from 'rxjs'; import { inject, Injectable } from '@angular/core'; +import { JsonApiResponseWithPaging } from '@osf/core/models'; import { JsonApiService } from '@osf/core/services'; -import { DraftRegistrationModel, RegistrationModel } from '@osf/shared/models/registration'; - -import { PageSchemaMapper } from '../mappers'; -import { RegistrationMapper } from '../mappers/registration.mapper'; +import { RegistrationMapper } from '@osf/shared/mappers/registration'; import { - PageSchema, + DraftRegistrationDataJsonApi, + DraftRegistrationModel, + DraftRegistrationRelationshipsJsonApi, + DraftRegistrationResponseJsonApi, RegistrationAttributesJsonApi, + RegistrationCard, RegistrationDataJsonApi, - RegistrationRelationshipsJsonApi, + RegistrationModel, RegistrationResponseJsonApi, - SchemaBlocksResponseJsonApi, -} from '../models'; +} from '@osf/shared/models'; + +import { PageSchemaMapper } from '../mappers'; +import { PageSchema, SchemaBlocksResponseJsonApi } from '../models'; import { environment } from 'src/environments/environment'; @@ -48,20 +52,20 @@ export class RegistriesService { }, }; return this.jsonApiService - .post(`${this.apiUrl}/draft_registrations/`, payload) + .post(`${this.apiUrl}/draft_registrations/`, payload) .pipe(map((response) => RegistrationMapper.fromDraftRegistrationResponse(response.data))); } getDraft(draftId: string): Observable { return this.jsonApiService - .get(`${this.apiUrl}/draft_registrations/${draftId}/`) + .get(`${this.apiUrl}/draft_registrations/${draftId}/`) .pipe(map((response) => RegistrationMapper.fromDraftRegistrationResponse(response.data))); } updateDraft( id: string, attributes: Partial, - relationships?: Partial + relationships?: Partial ): Observable { const payload = { data: { @@ -73,7 +77,7 @@ export class RegistriesService { }; return this.jsonApiService - .patch(`${this.apiUrl}/draft_registrations/${id}/`, payload) + .patch(`${this.apiUrl}/draft_registrations/${id}/`, payload) .pipe(map((response) => RegistrationMapper.fromDraftRegistrationResponse(response))); } @@ -98,4 +102,51 @@ export class RegistriesService { .get(`${this.apiUrl}/schemas/registrations/${registrationSchemaId}/schema_blocks/`) .pipe(map((response) => PageSchemaMapper.fromSchemaBlocksResponse(response))); } + + getDraftRegistrations(page: number, pageSize: number): Observable<{ data: RegistrationCard[]; totalCount: number }> { + const params = { + page, + 'page[size]': pageSize, + embed: ['bibliographic_contributors', 'registration_schema', 'provider'], + }; + return this.jsonApiService + .get< + JsonApiResponseWithPaging + >(`${this.apiUrl}/draft_registrations/`, params) + .pipe( + map((response) => { + const data = response.data.map((registration: DraftRegistrationDataJsonApi) => + RegistrationMapper.fromDraftToRegistrationCard(registration) + ); + return { + data, + totalCount: response.links.meta?.total, + }; + }) + ); + } + + getSubmittedRegistrations( + page: number, + pageSize: number + ): Observable<{ data: RegistrationCard[]; totalCount: number }> { + const params = { + page, + 'page[size]': pageSize, + embed: ['bibliographic_contributors', 'registration_schema', 'provider'], + }; + return this.jsonApiService + .get>(`${this.apiUrl}/registrations/`, params) + .pipe( + map((response) => { + const data = response.data.map((registration: RegistrationDataJsonApi) => + RegistrationMapper.fromRegistrationToRegistrationCard(registration) + ); + return { + data, + totalCount: response.links.meta?.total, + }; + }) + ); + } } diff --git a/src/app/features/registries/store/default.state.ts b/src/app/features/registries/store/default.state.ts index 3ae396e3a..c5e68f17a 100644 --- a/src/app/features/registries/store/default.state.ts +++ b/src/app/features/registries/store/default.state.ts @@ -39,4 +39,16 @@ export const DefaultState: RegistriesStateModel = { isSubmitting: false, error: null, }, + draftRegistrations: { + data: [], + isLoading: false, + error: null, + totalCount: 0, + }, + submittedRegistrations: { + data: [], + isLoading: false, + error: null, + totalCount: 0, + }, }; diff --git a/src/app/features/registries/store/registries.actions.ts b/src/app/features/registries/store/registries.actions.ts index 7932f3830..cc17adbc9 100644 --- a/src/app/features/registries/store/registries.actions.ts +++ b/src/app/features/registries/store/registries.actions.ts @@ -1,6 +1,8 @@ -import { LicenseOptions } from '@osf/shared/models'; - -import { RegistrationAttributesJsonApi, RegistrationRelationshipsJsonApi } from '../models'; +import { + DraftRegistrationAttributesJsonApi, + DraftRegistrationRelationshipsJsonApi, + LicenseOptions, +} from '@osf/shared/models'; export class GetRegistries { static readonly type = '[Registries] Get Registries'; @@ -29,8 +31,8 @@ export class UpdateDraft { static readonly type = '[Registries] Update Registration Tags'; constructor( public draftId: string, - public attributes: Partial, - public relationships?: Partial + public attributes: Partial, + public relationships?: Partial ) {} } @@ -75,3 +77,19 @@ export class UpdateStepValidation { public invalid: boolean ) {} } + +export class FetchDraftRegistrations { + static readonly type = '[Registries] Fetch Draft Registrations'; + constructor( + public page = 1, + public pageSize = 10 + ) {} +} + +export class FetchSubmittedRegistrations { + static readonly type = '[Registries] Fetch Submitted Registrations'; + constructor( + public page = 1, + public pageSize = 10 + ) {} +} diff --git a/src/app/features/registries/store/registries.model.ts b/src/app/features/registries/store/registries.model.ts index 112b9abfd..ec41ab1b2 100644 --- a/src/app/features/registries/store/registries.model.ts +++ b/src/app/features/registries/store/registries.model.ts @@ -1,5 +1,12 @@ -import { DraftRegistrationModel, RegistrationModel } from '@osf/shared/models/registration'; -import { AsyncStateModel, License, Resource } from '@shared/models'; +import { + AsyncStateModel, + AsyncStateWithTotalCount, + DraftRegistrationModel, + License, + RegistrationCard, + RegistrationModel, + Resource, +} from '@shared/models'; import { PageSchema, Project, ProviderSchema } from '../models'; @@ -12,4 +19,6 @@ export interface RegistriesStateModel { licenses: AsyncStateModel; pagesSchema: AsyncStateModel; stepsValidation: Record; + draftRegistrations: AsyncStateWithTotalCount; + submittedRegistrations: AsyncStateWithTotalCount; } diff --git a/src/app/features/registries/store/registries.selectors.ts b/src/app/features/registries/store/registries.selectors.ts index c73e45cf4..90d08517c 100644 --- a/src/app/features/registries/store/registries.selectors.ts +++ b/src/app/features/registries/store/registries.selectors.ts @@ -1,7 +1,6 @@ import { Selector } from '@ngxs/store'; -import { DraftRegistrationModel } from '@osf/shared/models/registration'; -import { License, Resource } from '@shared/models'; +import { DraftRegistrationModel, License, RegistrationCard, Resource } from '@shared/models'; import { PageSchema, Project, ProviderSchema } from '../models'; @@ -83,4 +82,34 @@ export class RegistriesSelectors { static isRegistrationSubmitting(state: RegistriesStateModel): boolean { return state.registration.isSubmitting || false; } + + @Selector([RegistriesState]) + static getDraftRegistrations(state: RegistriesStateModel): RegistrationCard[] { + return state.draftRegistrations.data; + } + + @Selector([RegistriesState]) + static isDraftRegistrationsLoading(state: RegistriesStateModel): boolean { + return state.draftRegistrations.isLoading; + } + + @Selector([RegistriesState]) + static getDraftRegistrationsTotalCount(state: RegistriesStateModel): number { + return state.draftRegistrations.totalCount; + } + + @Selector([RegistriesState]) + static getSubmittedRegistrations(state: RegistriesStateModel): RegistrationCard[] { + return state.submittedRegistrations.data; + } + + @Selector([RegistriesState]) + static isSubmittedRegistrationsLoading(state: RegistriesStateModel): boolean { + return state.submittedRegistrations.isLoading; + } + + @Selector([RegistriesState]) + static getSubmittedRegistrationsTotalCount(state: RegistriesStateModel): number { + return state.submittedRegistrations.totalCount; + } } diff --git a/src/app/features/registries/store/registries.state.ts b/src/app/features/registries/store/registries.state.ts index a078bd6b9..990bf2a84 100644 --- a/src/app/features/registries/store/registries.state.ts +++ b/src/app/features/registries/store/registries.state.ts @@ -19,8 +19,10 @@ import { CreateDraft, DeleteDraft, FetchDraft, + FetchDraftRegistrations, FetchLicenses, FetchSchemaBlocks, + FetchSubmittedRegistrations, GetProjects, GetProviderSchemas, GetRegistries, @@ -253,4 +255,55 @@ export class RegistriesState { saveLicense(ctx: StateContext, { registrationId, licenseId, licenseOptions }: SaveLicense) { return this.licensesHandler.saveLicense(ctx, { registrationId, licenseId, licenseOptions }); } + + @Action(FetchDraftRegistrations) + fetchDraftRegistrations(ctx: StateContext, { page, pageSize }: FetchDraftRegistrations) { + const state = ctx.getState(); + ctx.patchState({ + draftRegistrations: { + ...state.draftRegistrations, + isLoading: true, + error: null, + }, + }); + + return this.registriesService.getDraftRegistrations(page, pageSize).pipe( + tap((draftRegistrations) => { + ctx.patchState({ + draftRegistrations: { + data: draftRegistrations.data, + totalCount: draftRegistrations.totalCount, + isLoading: false, + error: null, + }, + }); + }), + catchError((error) => handleSectionError(ctx, 'draftRegistrations', error)) + ); + } + + @Action(FetchSubmittedRegistrations) + fetchSubmittedRegistrations( + ctx: StateContext, + { page, pageSize }: FetchSubmittedRegistrations + ) { + const state = ctx.getState(); + ctx.patchState({ + submittedRegistrations: { ...state.submittedRegistrations, isLoading: true, error: null }, + }); + + return this.registriesService.getSubmittedRegistrations(page, pageSize).pipe( + tap((submittedRegistrations) => { + ctx.patchState({ + submittedRegistrations: { + data: submittedRegistrations.data, + totalCount: submittedRegistrations.totalCount, + isLoading: false, + error: null, + }, + }); + }), + catchError((error) => handleSectionError(ctx, 'submittedRegistrations', error)) + ); + } } diff --git a/src/app/shared/components/registration-card/registration-card.component.html b/src/app/shared/components/registration-card/registration-card.component.html new file mode 100644 index 000000000..098685f27 --- /dev/null +++ b/src/app/shared/components/registration-card/registration-card.component.html @@ -0,0 +1,125 @@ +
+ + @if (registrationData()) { +
+
+ +

+ {{ registrationData().title || 'project.registrations.card.noTitle' | translate }} +

+ +
+
+
+
+ {{ 'project.registrations.card.registrationTemplate' | translate }} + {{ registrationData().registrationTemplate }} +
+ +
+ {{ 'project.registrations.card.registry' | translate }} + {{ registrationData().registry }} +
+ +
+ {{ 'project.registrations.card.registered' | translate }} + {{ registrationData().dateCreated | date: 'medium' }} +
+ +
+ {{ 'project.registrations.card.lastUpdated' | translate }} + {{ registrationData().dateModified | date: 'medium' }} +
+ +
+ {{ 'project.overview.metadata.contributors' | translate }}: + + @for (contributor of registrationData().contributors; track contributor) { + {{ contributor.fullName }} + @if (!$last) { + , + } + } + +
+ +
+ {{ 'project.registrations.card.description' | translate }} +

{{ registrationData().description || 'project.registrations.card.noDescription' | translate }}

+
+
+ @if (registrationData().status === RegistrationStatus.None) { + + + + } @else { + + @if (registrationData().status === RegistrationStatus.InProgress) { + + } + } +
+
+ + +
+
+ } +
+
diff --git a/src/app/features/project/registrations/components/registration-card/registration-card.component.scss b/src/app/shared/components/registration-card/registration-card.component.scss similarity index 100% rename from src/app/features/project/registrations/components/registration-card/registration-card.component.scss rename to src/app/shared/components/registration-card/registration-card.component.scss diff --git a/src/app/features/project/registrations/components/registration-card/registration-card.component.spec.ts b/src/app/shared/components/registration-card/registration-card.component.spec.ts similarity index 100% rename from src/app/features/project/registrations/components/registration-card/registration-card.component.spec.ts rename to src/app/shared/components/registration-card/registration-card.component.spec.ts diff --git a/src/app/shared/components/registration-card/registration-card.component.ts b/src/app/shared/components/registration-card/registration-card.component.ts new file mode 100644 index 000000000..cef9a114c --- /dev/null +++ b/src/app/shared/components/registration-card/registration-card.component.ts @@ -0,0 +1,25 @@ +import { TranslatePipe } from '@ngx-translate/core'; + +import { Button } from 'primeng/button'; +import { Card } from 'primeng/card'; +import { Tag } from 'primeng/tag'; + +import { DatePipe } from '@angular/common'; +import { ChangeDetectionStrategy, Component, input, output } from '@angular/core'; +import { RouterLink } from '@angular/router'; + +import { RegistryStatus } from '@osf/shared/enums'; +import { RegistrationCard } from '@osf/shared/models'; + +@Component({ + selector: 'osf-registration-card', + imports: [Card, Button, Tag, TranslatePipe, DatePipe, RouterLink], + templateUrl: './registration-card.component.html', + styleUrl: './registration-card.component.scss', + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class RegistrationCardComponent { + RegistrationStatus = RegistryStatus; + readonly registrationData = input.required(); + readonly deleteDraft = output(); +} diff --git a/src/app/shared/mappers/registration/index.ts b/src/app/shared/mappers/registration/index.ts new file mode 100644 index 000000000..8f0e07dd0 --- /dev/null +++ b/src/app/shared/mappers/registration/index.ts @@ -0,0 +1 @@ +export * from './registration.mapper'; diff --git a/src/app/shared/mappers/registration/registration.mapper.ts b/src/app/shared/mappers/registration/registration.mapper.ts new file mode 100644 index 000000000..ed50c01e3 --- /dev/null +++ b/src/app/shared/mappers/registration/registration.mapper.ts @@ -0,0 +1,104 @@ +import { RegistryStatus } from '@osf/shared/enums'; +import { + DraftRegistrationDataJsonApi, + DraftRegistrationModel, + RegistrationCard, + RegistrationDataJsonApi, + RegistrationModel, +} from '@osf/shared/models'; + +export class RegistrationMapper { + static fromDraftRegistrationResponse(response: DraftRegistrationDataJsonApi): DraftRegistrationModel { + return { + id: response.id, + title: response.attributes.title, + description: response.attributes.description, + registrationSchemaId: response.relationships.registration_schema?.data?.id || '', + license: { + id: response.relationships.license?.data?.id || '', + options: response.attributes.node_license + ? { + year: response.attributes.node_license.year, + copyrightHolders: response.attributes.node_license.copyright_holders.join(','), + } + : null, + }, + tags: response.attributes.tags || [], + stepsData: response.attributes.registration_responses || {}, + branchedFrom: response.relationships.branched_from?.data?.id, + providerId: response.relationships.registration_schema?.data?.id || '', + }; + } + + static fromRegistrationResponse(response: RegistrationDataJsonApi): RegistrationModel { + return { + id: response.id, + type: 'registration', + } as RegistrationModel; + } + + static fromDraftToRegistrationCard(registration: DraftRegistrationDataJsonApi): RegistrationCard { + return { + id: registration.id, + title: registration.attributes.title, + description: registration.attributes.description || '', + status: RegistryStatus.None, + dateCreated: registration.attributes.datetime_initiated, + dateModified: registration.attributes.datetime_updated, + registrationTemplate: registration.embeds?.registration_schema?.data?.attributes?.name || '', + registry: registration.embeds?.provider?.data?.attributes?.name || '', + contributors: + registration.embeds?.bibliographic_contributors?.data.map((contributor) => ({ + id: contributor.id, + fullName: contributor.embeds?.users?.data.attributes.full_name, + })) || [], + }; + } + + static fromRegistrationToRegistrationCard(registration: RegistrationDataJsonApi): RegistrationCard { + return { + id: registration.id, + title: registration.attributes.title, + description: registration.attributes.description || '', + status: RegistryStatus.InProgress, // [NM] TODO: map status accordingly + dateCreated: registration.attributes.datetime_initiated, + dateModified: registration.attributes.date_modified, + registrationTemplate: registration.embeds?.registration_schema?.data?.attributes?.name || '', + registry: registration.embeds?.provider?.data?.attributes?.name || '', + contributors: + registration.embeds?.bibliographic_contributors?.data.map((contributor) => ({ + id: contributor.id, + fullName: contributor.embeds?.users?.data.attributes.full_name, + })) || [], + }; + } + + static toRegistrationPayload(draftId: string, embargoDate: string, providerId: string, projectId?: string) { + return { + data: { + type: 'registrations', + attributes: { + embargo_end_date: embargoDate, + draft_registration: draftId, + }, + relationships: { + registered_from: projectId + ? { + data: { + type: 'nodes', + id: projectId, + }, + } + : undefined, + + provider: { + data: { + type: 'registration-providers', + id: providerId, + }, + }, + }, + }, + }; + } +} diff --git a/src/app/shared/models/index.ts b/src/app/shared/models/index.ts index 809165bd6..a2b9274ee 100644 --- a/src/app/shared/models/index.ts +++ b/src/app/shared/models/index.ts @@ -22,6 +22,7 @@ export * from './nodes/create-project-form.model'; export * from './nodes/nodes-json-api.model'; export * from './paginated-data.model'; export * from './query-params.model'; +export * from './registration'; export * from './resource-card'; export * from './resource-overview.model'; export * from './search'; diff --git a/src/app/shared/models/registration/index.ts b/src/app/shared/models/registration/index.ts index 58f03a095..510ca14e1 100644 --- a/src/app/shared/models/registration/index.ts +++ b/src/app/shared/models/registration/index.ts @@ -1,2 +1,4 @@ export * from './draft-registration.model'; export * from './registration.model'; +export * from './registration-card.model'; +export * from './registration-json-api.model'; diff --git a/src/app/shared/models/registration/registration-card.model.ts b/src/app/shared/models/registration/registration-card.model.ts new file mode 100644 index 000000000..2e3c76f72 --- /dev/null +++ b/src/app/shared/models/registration/registration-card.model.ts @@ -0,0 +1,16 @@ +import { RegistryStatus } from '@osf/shared/enums'; + +import { ContributorModel } from '../contributors'; + +export interface RegistrationCard { + id: string; + title: string; + description: string; + status: RegistryStatus; + dateCreated: string; + dateModified: string; + contributors: Partial[]; + registrationTemplate: string; + registry: string; + resources?: Record; +} diff --git a/src/app/shared/models/registration/registration-json-api.model.ts b/src/app/shared/models/registration/registration-json-api.model.ts new file mode 100644 index 000000000..e79d94840 --- /dev/null +++ b/src/app/shared/models/registration/registration-json-api.model.ts @@ -0,0 +1,133 @@ +import { ApiData, MetaJsonApi, PaginationLinksJsonApi } from '@osf/core/models'; +import { LicenseRecordJsonApi } from '@osf/shared/models'; + +export interface DraftRegistrationResponseJsonApi { + data: DraftRegistrationDataJsonApi; + meta: MetaJsonApi; + links: PaginationLinksJsonApi; +} + +export interface RegistrationResponseJsonApi { + data: RegistrationDataJsonApi; + meta: MetaJsonApi; + links: PaginationLinksJsonApi; +} + +export type DraftRegistrationDataJsonApi = ApiData< + DraftRegistrationAttributesJsonApi, + DraftRegistrationEmbedsJsonApi, + DraftRegistrationRelationshipsJsonApi, + null +>; + +export type RegistrationDataJsonApi = ApiData< + RegistrationAttributesJsonApi, + RegistrationEmbedsJsonApi, + RegistrationRelationshipsJsonApi, + null +>; + +export interface DraftRegistrationAttributesJsonApi { + category: string; + date_created: string; + datetime_initiated: string; + datetime_updated: string; + description: string; + has_project: boolean; + node_license: LicenseRecordJsonApi; + registration_metadata: Record; + registration_responses: Record; + tags: string[]; + title: string; +} + +export interface RegistrationAttributesJsonApi { + access_requests_enabled: boolean; + datetime_initiated: string; + date_modified: string; + description: string; + embargoed: boolean; + archiving: boolean; + title: string; +} + +export interface DraftRegistrationRelationshipsJsonApi { + registration_schema?: { + data: { + id: string; + type: 'registration-schemas'; + }; + }; + license?: { + data: { + id: string; + type: 'licenses'; + }; + }; + branched_from?: { + data: { + id: string; + type: 'nodes'; + }; + }; +} + +export interface RegistrationRelationshipsJsonApi { + registration_schema?: { + data: { + id: string; + type: 'registration-schemas'; + }; + }; + license?: { + data: { + id: string; + type: 'licenses'; + }; + }; +} + +export interface RegistrationEmbedsJsonApi { + registration_schema?: { + data: { + attributes: { + name: string; + }; + }; + }; + bibliographic_contributors?: { + data: { + id: string; + type: 'users'; + embeds: { + users: { + data: { + attributes: { + full_name: string; + }; + id: string; + }; + }; + }; + }[]; + }; + provider?: { + data: { + attributes: { + name: string; + }; + }; + }; +} + +// eslint-disable-next-line @typescript-eslint/no-empty-object-type +export interface DraftRegistrationEmbedsJsonApi extends RegistrationEmbedsJsonApi {} + +export interface CreateRegistrationPayloadJsonApi { + data: { + type: 'draft_registrations'; + id: string; + relationships?: DraftRegistrationRelationshipsJsonApi; + attributes?: Partial; + }; +} diff --git a/src/assets/i18n/en.json b/src/assets/i18n/en.json index 2d445514f..cc7311812 100644 --- a/src/assets/i18n/en.json +++ b/src/assets/i18n/en.json @@ -31,7 +31,9 @@ "update": "Update", "continue update": "Continue update", "withdraw": "Withdraw", - "submit": "Submit" + "submit": "Submit", + "view": "View", + "review": "Review" }, "search": { "title": "Search", @@ -73,6 +75,8 @@ "makePublic": "Make Public", "noData": "No data", "noFiles": "No files selected", + "drafts": "Drafts", + "submitted": "Submitted", "yes": "Yes", "no": "No", "available": "Available", @@ -717,6 +721,8 @@ "addRegistration": "Add a Registration", "emptyState": "There have been no completed registrations of this project.", "card": { + "noTitle": "(Untitled)", + "noDescription": "No description", "registrationTemplate": "Registration template:", "registry": "Registry:", "registered": "Registered:", @@ -1803,6 +1809,7 @@ "registries": { "title": "Registries", "addRegistration": "Add a Registration", + "noRegistrations": "You don't have any registrations. Create one with the button below.", "description": "The open registries network", "searchPlaceholder": "Search Registrations", "services": { @@ -1829,6 +1836,7 @@ }, "deleteDraft": "Delete Draft", "confirmDeleteDraft": "Are you sure you want to delete this draft registration?", + "successDeleteDraft": "Draft registration successfully deleted.", "metadata": { "title": "Registration Metadata", "description": "This metadata applies only to the registration you are creating, and will not be applied to your project.",