+
diff --git a/src/app/features/registries/registries.component.scss b/src/app/features/registries/registries.component.scss
index e69de29b..da0c027b 100644
--- a/src/app/features/registries/registries.component.scss
+++ b/src/app/features/registries/registries.component.scss
@@ -0,0 +1,5 @@
+:host {
+ display: flex;
+ flex-direction: column;
+ flex: 1;
+}
diff --git a/src/app/features/registries/registries.routes.ts b/src/app/features/registries/registries.routes.ts
index 60b2d2d2..5c815ae2 100644
--- a/src/app/features/registries/registries.routes.ts
+++ b/src/app/features/registries/registries.routes.ts
@@ -41,7 +41,7 @@ export const registriesRoutes: Routes = [
providers: [provideStates([ModeratorsState])],
},
{
- path: 'new',
+ path: ':providerId/new',
loadComponent: () =>
import('./components/new-registration/new-registration.component').then(
(mod) => mod.NewRegistrationComponent
diff --git a/src/app/features/registries/services/licenses.service.ts b/src/app/features/registries/services/licenses.service.ts
index 03c0db32..cb802388 100644
--- a/src/app/features/registries/services/licenses.service.ts
+++ b/src/app/features/registries/services/licenses.service.ts
@@ -4,8 +4,10 @@ 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 { LicensesMapper } from '../mappers';
+import { RegistrationMapper } from '../mappers/registration.mapper';
import { RegistrationDataJsonApi, RegistrationPayloadJsonApi } from '../models';
import { environment } from 'src/environments/environment';
@@ -34,9 +36,9 @@ export class LicensesService {
private apiUrl = environment.apiUrl;
private readonly jsonApiService = inject(JsonApiService);
- getLicenses(): Observable
{
+ getLicenses(providerId: string): Observable {
return this.jsonApiService
- .get(`${this.apiUrl}/providers/registrations/osf/licenses/`, {
+ .get(`${this.apiUrl}/providers/registrations/${providerId}/licenses/`, {
params: {
'page[size]': 100,
},
@@ -49,7 +51,11 @@ export class LicensesService {
);
}
- updateLicense(registrationId: string, licenseId: string, licenseOptions?: LicenseOptions) {
+ updateLicense(
+ registrationId: string,
+ licenseId: string,
+ licenseOptions?: LicenseOptions
+ ): Observable {
const payload: RegistrationPayloadJsonApi = {
data: {
type: 'draft_registrations',
@@ -73,9 +79,8 @@ export class LicensesService {
},
};
- return this.jsonApiService.patch(
- `${this.apiUrl}/draft_registrations/${registrationId}/`,
- payload
- );
+ return this.jsonApiService
+ .patch(`${this.apiUrl}/draft_registrations/${registrationId}/`, payload)
+ .pipe(map((response) => RegistrationMapper.fromDraftRegistrationResponse(response)));
}
}
diff --git a/src/app/features/registries/services/providers.service.ts b/src/app/features/registries/services/providers.service.ts
index 721234bd..5a08992e 100644
--- a/src/app/features/registries/services/providers.service.ts
+++ b/src/app/features/registries/services/providers.service.ts
@@ -5,7 +5,7 @@ import { inject, Injectable } from '@angular/core';
import { JsonApiService } from '@osf/core/services';
import { ProvidersMapper } from '../mappers/providers.mapper';
-import { Provider } from '../models';
+import { ProviderSchema } from '../models';
import { ProvidersResponseJsonApi } from '../models/providers-json-api.model';
import { environment } from 'src/environments/environment';
@@ -17,9 +17,9 @@ export class ProvidersService {
private apiUrl = environment.apiUrl;
private readonly jsonApiService = inject(JsonApiService);
- getProviders(): Observable {
+ getProviderSchemas(providerId: string): Observable {
return this.jsonApiService
- .get(`${this.apiUrl}/providers/registrations/osf/schemas/`)
+ .get(`${this.apiUrl}/providers/registrations/${providerId}/schemas/`)
.pipe(map((response) => ProvidersMapper.fromProvidersResponse(response)));
}
}
diff --git a/src/app/features/registries/services/registries.service.ts b/src/app/features/registries/services/registries.service.ts
index 1fc0c1c8..9efabfa6 100644
--- a/src/app/features/registries/services/registries.service.ts
+++ b/src/app/features/registries/services/registries.service.ts
@@ -3,12 +3,12 @@ import { map, Observable } from 'rxjs';
import { inject, Injectable } from '@angular/core';
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 {
PageSchema,
- Registration,
RegistrationAttributesJsonApi,
RegistrationDataJsonApi,
RegistrationRelationshipsJsonApi,
@@ -25,7 +25,7 @@ export class RegistriesService {
private apiUrl = environment.apiUrl;
private readonly jsonApiService = inject(JsonApiService);
- createDraft(registrationSchemaId: string, projectId?: string | undefined): Observable {
+ createDraft(registrationSchemaId: string, projectId?: string | undefined): Observable {
const payload = {
data: {
type: 'draft_registrations',
@@ -49,20 +49,20 @@ export class RegistriesService {
};
return this.jsonApiService
.post(`${this.apiUrl}/draft_registrations/`, payload)
- .pipe(map((response) => RegistrationMapper.fromRegistrationResponse(response.data)));
+ .pipe(map((response) => RegistrationMapper.fromDraftRegistrationResponse(response.data)));
}
- getDraft(draftId: string): Observable {
+ getDraft(draftId: string): Observable {
return this.jsonApiService
.get(`${this.apiUrl}/draft_registrations/${draftId}/`)
- .pipe(map((response) => RegistrationMapper.fromRegistrationResponse(response.data)));
+ .pipe(map((response) => RegistrationMapper.fromDraftRegistrationResponse(response.data)));
}
updateDraft(
id: string,
attributes: Partial,
relationships?: Partial
- ): Observable {
+ ): Observable {
const payload = {
data: {
id,
@@ -74,13 +74,25 @@ export class RegistriesService {
return this.jsonApiService
.patch(`${this.apiUrl}/draft_registrations/${id}/`, payload)
- .pipe(map((response) => RegistrationMapper.fromRegistrationResponse(response)));
+ .pipe(map((response) => RegistrationMapper.fromDraftRegistrationResponse(response)));
}
deleteDraft(draftId: string): Observable {
return this.jsonApiService.delete(`${this.apiUrl}/draft_registrations/${draftId}/`);
}
+ registerDraft(
+ draftId: string,
+ embargoDate: string,
+ providerId: string,
+ projectId?: string
+ ): Observable {
+ const payload = RegistrationMapper.toRegistrationPayload(draftId, embargoDate, providerId, projectId);
+ return this.jsonApiService
+ .post(`${this.apiUrl}/registrations/`, payload)
+ .pipe(map((response) => RegistrationMapper.fromRegistrationResponse(response.data)));
+ }
+
getSchemaBlocks(registrationSchemaId: string): Observable {
return this.jsonApiService
.get(`${this.apiUrl}/schemas/registrations/${registrationSchemaId}/schema_blocks/`)
diff --git a/src/app/features/registries/store/default.state.ts b/src/app/features/registries/store/default.state.ts
index 68ba15ee..3ae396e3 100644
--- a/src/app/features/registries/store/default.state.ts
+++ b/src/app/features/registries/store/default.state.ts
@@ -1,7 +1,7 @@
import { RegistriesStateModel } from './registries.model';
export const DefaultState: RegistriesStateModel = {
- providers: {
+ providerSchemas: {
data: [],
isLoading: false,
error: null,
@@ -33,4 +33,10 @@ export const DefaultState: RegistriesStateModel = {
error: null,
},
stepsValidation: {},
+ registration: {
+ data: null,
+ isLoading: false,
+ isSubmitting: false,
+ error: null,
+ },
};
diff --git a/src/app/features/registries/store/handlers/licenses.handlers.ts b/src/app/features/registries/store/handlers/licenses.handlers.ts
index 28ee8b15..6c4079aa 100644
--- a/src/app/features/registries/store/handlers/licenses.handlers.ts
+++ b/src/app/features/registries/store/handlers/licenses.handlers.ts
@@ -14,7 +14,7 @@ import { RegistriesStateModel } from '../registries.model';
export class LicensesHandlers {
licensesService = inject(LicensesService);
- fetchLicenses(ctx: StateContext) {
+ fetchLicenses(ctx: StateContext, providerId: string) {
ctx.patchState({
licenses: {
...ctx.getState().licenses,
@@ -22,7 +22,7 @@ export class LicensesHandlers {
},
});
- return this.licensesService.getLicenses().pipe(
+ return this.licensesService.getLicenses(providerId).pipe(
tap((licenses) => {
ctx.patchState({
licenses: {
@@ -39,25 +39,23 @@ export class LicensesHandlers {
saveLicense(ctx: StateContext, { registrationId, licenseId, licenseOptions }: SaveLicense) {
const state = ctx.getState();
ctx.patchState({
- licenses: {
- ...state.licenses,
+ draftRegistration: {
+ ...state.draftRegistration,
isLoading: true,
},
});
- return this.licensesService
- .updateLicense(registrationId, licenseId, licenseOptions)
- .pipe
- // tap((response) => {
- // ctx.patchState({
- // licenses: {
- // data: response,
- // isLoading: false,
- // error: null,
- // },
- // });
- // }),
- // catchError((error) => handleSectionError(ctx, 'licenses', error))
- ();
+ return this.licensesService.updateLicense(registrationId, licenseId, licenseOptions).pipe(
+ tap((response) => {
+ ctx.patchState({
+ draftRegistration: {
+ data: response,
+ isLoading: false,
+ error: null,
+ },
+ });
+ }),
+ catchError((error) => handleSectionError(ctx, 'licenses', error))
+ );
}
}
diff --git a/src/app/features/registries/store/handlers/providers.handlers.ts b/src/app/features/registries/store/handlers/providers.handlers.ts
index c1c30406..8b68e2a0 100644
--- a/src/app/features/registries/store/handlers/providers.handlers.ts
+++ b/src/app/features/registries/store/handlers/providers.handlers.ts
@@ -10,17 +10,17 @@ import { RegistriesStateModel } from '../registries.model';
export class ProvidersHandlers {
providersService = inject(ProvidersService);
- getProviders({ patchState }: StateContext) {
+ getProviderSchemas({ patchState }: StateContext, providerId: string) {
patchState({
- providers: {
- ...DefaultState.providers,
+ providerSchemas: {
+ ...DefaultState.providerSchemas,
isLoading: true,
},
});
- return this.providersService.getProviders().subscribe({
+ return this.providersService.getProviderSchemas(providerId).subscribe({
next: (providers) => {
patchState({
- providers: {
+ providerSchemas: {
data: providers,
isLoading: false,
error: null,
@@ -29,8 +29,8 @@ export class ProvidersHandlers {
},
error: (error) => {
patchState({
- providers: {
- ...DefaultState.providers,
+ providerSchemas: {
+ ...DefaultState.providerSchemas,
isLoading: false,
error,
},
diff --git a/src/app/features/registries/store/registries.actions.ts b/src/app/features/registries/store/registries.actions.ts
index b7458327..7932f383 100644
--- a/src/app/features/registries/store/registries.actions.ts
+++ b/src/app/features/registries/store/registries.actions.ts
@@ -6,8 +6,9 @@ export class GetRegistries {
static readonly type = '[Registries] Get Registries';
}
-export class GetProviders {
- static readonly type = '[Registries] Get Providers';
+export class GetProviderSchemas {
+ static readonly type = '[Registries] Get Provider Schemas';
+ constructor(public providerId: string) {}
}
export class GetProjects {
@@ -38,6 +39,16 @@ export class DeleteDraft {
constructor(public draftId: string) {}
}
+export class RegisterDraft {
+ static readonly type = '[Registries] Register Draft Registration';
+ constructor(
+ public draftId: string,
+ public embargoDate: string,
+ public providerId: string,
+ public projectId?: string
+ ) {}
+}
+
export class FetchSchemaBlocks {
static readonly type = '[Registries] Fetch Schema Blocks';
constructor(public registrationSchemaId: string) {}
@@ -45,6 +56,7 @@ export class FetchSchemaBlocks {
export class FetchLicenses {
static readonly type = '[Registries] Fetch Licenses';
+ constructor(public providerId: string) {}
}
export class SaveLicense {
diff --git a/src/app/features/registries/store/registries.model.ts b/src/app/features/registries/store/registries.model.ts
index 73ab322d..112b9abf 100644
--- a/src/app/features/registries/store/registries.model.ts
+++ b/src/app/features/registries/store/registries.model.ts
@@ -1,12 +1,13 @@
+import { DraftRegistrationModel, RegistrationModel } from '@osf/shared/models/registration';
import { AsyncStateModel, License, Resource } from '@shared/models';
-import { PageSchema, Project, Provider } from '../models';
-import { Registration } from '../models/registration.model';
+import { PageSchema, Project, ProviderSchema } from '../models';
export interface RegistriesStateModel {
- providers: AsyncStateModel;
+ providerSchemas: AsyncStateModel;
projects: AsyncStateModel;
- draftRegistration: AsyncStateModel;
+ draftRegistration: AsyncStateModel;
+ registration: AsyncStateModel;
registries: AsyncStateModel;
licenses: AsyncStateModel;
pagesSchema: AsyncStateModel;
diff --git a/src/app/features/registries/store/registries.selectors.ts b/src/app/features/registries/store/registries.selectors.ts
index 8f92c627..c73e45cf 100644
--- a/src/app/features/registries/store/registries.selectors.ts
+++ b/src/app/features/registries/store/registries.selectors.ts
@@ -1,21 +1,22 @@
import { Selector } from '@ngxs/store';
+import { DraftRegistrationModel } from '@osf/shared/models/registration';
import { License, Resource } from '@shared/models';
-import { PageSchema, Project, Provider, Registration } from '../models';
+import { PageSchema, Project, ProviderSchema } from '../models';
import { RegistriesStateModel } from './registries.model';
import { RegistriesState } from './registries.state';
export class RegistriesSelectors {
@Selector([RegistriesState])
- static getProviders(state: RegistriesStateModel): Provider[] {
- return state.providers.data;
+ static getProviderSchemas(state: RegistriesStateModel): ProviderSchema[] {
+ return state.providerSchemas.data;
}
@Selector([RegistriesState])
static isProvidersLoading(state: RegistriesStateModel): boolean {
- return state.providers.isLoading;
+ return state.providerSchemas.isLoading;
}
@Selector([RegistriesState])
@@ -29,7 +30,7 @@ export class RegistriesSelectors {
}
@Selector([RegistriesState])
- static getDraftRegistration(state: RegistriesStateModel): Registration | null {
+ static getDraftRegistration(state: RegistriesStateModel): DraftRegistrationModel | null {
return state.draftRegistration.data;
}
@@ -77,4 +78,9 @@ export class RegistriesSelectors {
static getStepsData(state: RegistriesStateModel) {
return state.draftRegistration.data?.stepsData || {};
}
+
+ @Selector([RegistriesState])
+ static isRegistrationSubmitting(state: RegistriesStateModel): boolean {
+ return state.registration.isSubmitting || false;
+ }
}
diff --git a/src/app/features/registries/store/registries.state.ts b/src/app/features/registries/store/registries.state.ts
index 12968e61..a078bd6b 100644
--- a/src/app/features/registries/store/registries.state.ts
+++ b/src/app/features/registries/store/registries.state.ts
@@ -22,8 +22,9 @@ import {
FetchLicenses,
FetchSchemaBlocks,
GetProjects,
- GetProviders,
+ GetProviderSchemas,
GetRegistries,
+ RegisterDraft,
SaveLicense,
UpdateDraft,
UpdateStepValidation,
@@ -74,9 +75,9 @@ export class RegistriesState {
return this.projectsHandler.getProjects(ctx);
}
- @Action(GetProviders)
- getProviders(ctx: StateContext) {
- return this.providersHandler.getProviders(ctx);
+ @Action(GetProviderSchemas)
+ getProviders(ctx: StateContext, { providerId }: GetProviderSchemas) {
+ return this.providersHandler.getProviderSchemas(ctx, providerId);
}
@Action(CreateDraft)
@@ -185,6 +186,33 @@ export class RegistriesState {
);
}
+ @Action(RegisterDraft)
+ registerDraft(
+ ctx: StateContext,
+ { draftId, embargoDate, providerId, projectId }: RegisterDraft
+ ) {
+ ctx.patchState({
+ registration: {
+ ...ctx.getState().registration,
+ isSubmitting: true,
+ },
+ });
+
+ return this.registriesService.registerDraft(draftId, embargoDate, providerId, projectId).pipe(
+ tap((registration) => {
+ ctx.patchState({
+ registration: {
+ data: { ...registration },
+ isLoading: false,
+ isSubmitting: false,
+ error: null,
+ },
+ });
+ }),
+ catchError((error) => handleSectionError(ctx, 'draftRegistration', error))
+ );
+ }
+
@Action(FetchSchemaBlocks)
fetchSchemaBlocks(ctx: StateContext, action: FetchSchemaBlocks) {
const state = ctx.getState();
@@ -217,8 +245,8 @@ export class RegistriesState {
}
@Action(FetchLicenses)
- fetchLicenses(ctx: StateContext) {
- return this.licensesHandler.fetchLicenses(ctx);
+ fetchLicenses(ctx: StateContext, { providerId }: FetchLicenses) {
+ return this.licensesHandler.fetchLicenses(ctx, providerId);
}
@Action(SaveLicense)
diff --git a/src/app/features/registries/models/registration.model.ts b/src/app/shared/models/registration/draft-registration.model.ts
similarity index 66%
rename from src/app/features/registries/models/registration.model.ts
rename to src/app/shared/models/registration/draft-registration.model.ts
index 972d6256..19bfa8f4 100644
--- a/src/app/features/registries/models/registration.model.ts
+++ b/src/app/shared/models/registration/draft-registration.model.ts
@@ -1,6 +1,6 @@
-import { LicenseOptions } from '@osf/shared/models';
+import { LicenseOptions } from '../license.model';
-export interface Registration {
+export interface DraftRegistrationModel {
id: string;
title: string;
description: string;
@@ -12,4 +12,6 @@ export interface Registration {
tags: string[];
// eslint-disable-next-line @typescript-eslint/no-explicit-any
stepsData?: Record;
+ branchedFrom?: string;
+ providerId: string;
}
diff --git a/src/app/shared/models/registration/index.ts b/src/app/shared/models/registration/index.ts
new file mode 100644
index 00000000..58f03a09
--- /dev/null
+++ b/src/app/shared/models/registration/index.ts
@@ -0,0 +1,2 @@
+export * from './draft-registration.model';
+export * from './registration.model';
diff --git a/src/app/shared/models/registration/registration.model.ts b/src/app/shared/models/registration/registration.model.ts
new file mode 100644
index 00000000..5bddecd6
--- /dev/null
+++ b/src/app/shared/models/registration/registration.model.ts
@@ -0,0 +1,67 @@
+import { RegistryStatus, RevisionReviewStates } from '@osf/shared/enums';
+
+import { ContributorModel } from '../contributors';
+import { SubjectModel } from '../subject';
+
+export type RegistrationQuestions = Record;
+
+export interface RegistrationModel {
+ id: string;
+ type: string;
+ isPublic: boolean;
+ forksCount: number;
+ title: string;
+ description: string;
+ dateModified: string;
+ dateCreated: string;
+ dateRegistered?: string;
+ registrationType: string;
+ doi: string;
+ tags: string[];
+ contributors: ContributorModel[];
+ citation: string;
+ category: string;
+ isFork: boolean;
+ accessRequestsEnabled: boolean;
+ nodeLicense?: {
+ copyrightHolders: string[];
+ year: string;
+ };
+ license?: {
+ name: string;
+ text: string;
+ url: string;
+ };
+ identifiers?: {
+ id: string;
+ type: string;
+ category: string;
+ value: string;
+ }[];
+ analyticsKey: string;
+ currentUserCanComment: boolean;
+ currentUserPermissions: string[];
+ currentUserIsContributor: boolean;
+ currentUserIsContributorOrGroupMember: boolean;
+ wikiEnabled: boolean;
+ region?: {
+ id: string;
+ type: string;
+ };
+ subjects?: SubjectModel[];
+ hasData: boolean;
+ hasAnalyticCode: boolean;
+ hasMaterials: boolean;
+ hasPapers: boolean;
+ hasSupplements: boolean;
+ questions: RegistrationQuestions;
+ registrationSchemaLink: string;
+ associatedProjectId: string;
+ schemaResponses: {
+ id: string;
+ revisionResponses: RegistrationQuestions;
+ updatedResponseKeys: string[];
+ }[];
+ status: RegistryStatus;
+ revisionStatus: RevisionReviewStates;
+}
diff --git a/src/assets/i18n/en.json b/src/assets/i18n/en.json
index 029cfefc..2d445514 100644
--- a/src/assets/i18n/en.json
+++ b/src/assets/i18n/en.json
@@ -71,6 +71,8 @@
"year": "Year",
"optional": "Optional",
"makePublic": "Make Public",
+ "noData": "No data",
+ "noFiles": "No files selected",
"yes": "Yes",
"no": "No",
"available": "Available",
@@ -1834,7 +1836,22 @@
},
"review": {
"step": "Review",
- "title": "Review Registration"
+ "title": "Review Registration",
+ "register": "Register",
+ "confirmation": {
+ "title": "Almost done...",
+ "remember": "Remember:",
+ "text1": "Do not edit any files until the registration has completely archived.",
+ "text2": "This will be permanent and cannot be deleted once submitted.",
+ "text3": "This registration will be copied to Internet Archive as a backup.",
+ "text4": "Title and contributors cannot be updated once submitted.",
+ "options": {
+ "public": "Make registration public immediately",
+ "embargo": "Enter registration into embargo"
+ },
+ "embargoPlaceholder": "Choose embargo end date",
+ "successMessage": "Registration successfully created."
+ }
}
},
"registry": {
@@ -1957,6 +1974,9 @@
"noSelected": "No subjects selected",
"searchSubjects": "Search subjects",
"noSubject": "No subjects found"
+ },
+ "tags": {
+ "title": "Tags"
}
},
"resourceCard": {