diff --git a/src/app/features/preprints/components/submit-steps/metadata/metadata.component.html b/src/app/features/preprints/components/submit-steps/metadata/metadata.component.html
deleted file mode 100644
index 682a46bb2..000000000
--- a/src/app/features/preprints/components/submit-steps/metadata/metadata.component.html
+++ /dev/null
@@ -1,86 +0,0 @@
-
Metadata
-
-
-
-
-
-
-
-
-
-
-
-
Publication DOI
-
-
- @let doiControl = metadataForm.controls['doi'];
- @if (doiControl.errors?.['required'] && (doiControl.touched || doiControl.dirty)) {
-
- {{ INPUT_VALIDATION_MESSAGES.required | translate }}
-
- }
- @if (doiControl.errors?.['pattern'] && (doiControl.touched || doiControl.dirty)) {
-
Please use a valid DOI format (10.xxxx/xxxxx)
-
- }
-
-
-
-
-
-
Tags (optional)
-
-
-
-
-
-
-
-
- Publication Date (optional)
-
-
-
-
-
-
-
-
-
- Publication Citation (optional)
-
-
-
-
-
diff --git a/src/app/features/preprints/components/submit-steps/metadata/metadata.component.ts b/src/app/features/preprints/components/submit-steps/metadata/metadata.component.ts
deleted file mode 100644
index c82bb1e9a..000000000
--- a/src/app/features/preprints/components/submit-steps/metadata/metadata.component.ts
+++ /dev/null
@@ -1,132 +0,0 @@
-import { createDispatchMap, select } from '@ngxs/store';
-
-import { TranslatePipe } from '@ngx-translate/core';
-
-import { Button } from 'primeng/button';
-import { Card } from 'primeng/card';
-import { DatePicker } from 'primeng/datepicker';
-import { InputText } from 'primeng/inputtext';
-import { Message } from 'primeng/message';
-import { Tooltip } from 'primeng/tooltip';
-
-import { ChangeDetectionStrategy, Component, HostListener, OnInit, output } from '@angular/core';
-import { FormControl, FormGroup, ReactiveFormsModule, Validators } from '@angular/forms';
-
-import { formInputLimits } from '@osf/features/preprints/constants';
-import { MetadataForm, Preprint } from '@osf/features/preprints/models';
-import {
- CreatePreprint,
- FetchLicenses,
- SaveLicense,
- SubmitPreprintSelectors,
- UpdatePreprint,
-} from '@osf/features/preprints/store/submit-preprint';
-import { IconComponent, LicenseComponent, TagsInputComponent, TextInputComponent } from '@shared/components';
-import { INPUT_VALIDATION_MESSAGES } from '@shared/constants';
-import { License, LicenseOptions } from '@shared/models';
-import { CustomValidators, findChangedFields } from '@shared/utils';
-
-import { ContributorsComponent } from './contributors/contributors.component';
-
-@Component({
- selector: 'osf-preprint-metadata',
- imports: [
- ContributorsComponent,
- Button,
- Card,
- ReactiveFormsModule,
- Message,
- TranslatePipe,
- DatePicker,
- IconComponent,
- InputText,
- TextInputComponent,
- Tooltip,
- LicenseComponent,
- TagsInputComponent,
- ],
- templateUrl: './metadata.component.html',
- styleUrl: './metadata.component.scss',
- changeDetection: ChangeDetectionStrategy.OnPush,
-})
-export class MetadataComponent implements OnInit {
- private actions = createDispatchMap({
- createPreprint: CreatePreprint,
- updatePreprint: UpdatePreprint,
- fetchLicenses: FetchLicenses,
- saveLicense: SaveLicense,
- });
-
- protected metadataForm!: FormGroup;
- protected inputLimits = formInputLimits;
- protected readonly INPUT_VALIDATION_MESSAGES = INPUT_VALIDATION_MESSAGES;
-
- licences = select(SubmitPreprintSelectors.getLicenses);
- createdPreprint = select(SubmitPreprintSelectors.getCreatedPreprint);
- isUpdatingPreprint = select(SubmitPreprintSelectors.isPreprintSubmitting);
-
- nextClicked = output();
-
- ngOnInit() {
- this.actions.fetchLicenses();
- this.initForm();
- }
-
- initForm() {
- const publicationDate = this.createdPreprint()?.originalPublicationDate;
- this.metadataForm = new FormGroup({
- doi: new FormControl(this.createdPreprint()?.doi || '', {
- nonNullable: true,
- validators: [CustomValidators.requiredTrimmed(), Validators.pattern(this.inputLimits.doi.pattern)],
- }),
- originalPublicationDate: new FormControl(publicationDate ? new Date(publicationDate) : null, {
- nonNullable: false,
- validators: [],
- }),
- customPublicationCitation: new FormControl(this.createdPreprint()?.customPublicationCitation || null, {
- nonNullable: false,
- validators: [Validators.maxLength(this.inputLimits.citation.maxLength)],
- }),
- tags: new FormControl(this.createdPreprint()?.tags || [], {
- nonNullable: true,
- validators: [],
- }),
- });
- }
-
- nextButtonClicked() {
- if (this.metadataForm.invalid) {
- return;
- }
-
- const model = this.metadataForm.value;
-
- const changedFields = findChangedFields(model, this.createdPreprint()!);
-
- this.actions.updatePreprint(this.createdPreprint()!.id, changedFields).subscribe({
- complete: () => {
- this.nextClicked.emit();
- },
- });
- }
-
- @HostListener('window:beforeunload', ['$event'])
- public onBeforeUnload($event: BeforeUnloadEvent): boolean {
- $event.preventDefault();
- return false;
- }
-
- createLicense(licenseDetails: { id: string; licenseOptions: LicenseOptions }) {
- this.actions.saveLicense(licenseDetails.id, licenseDetails.licenseOptions);
- }
-
- selectLicense(license: License) {
- this.actions.saveLicense(license.id);
- }
-
- updateTags(updatedTags: string[]) {
- this.metadataForm.patchValue({
- tags: updatedTags,
- });
- }
-}
diff --git a/src/app/features/registries/components/metadata/registries-license/registries-license.component.html b/src/app/features/registries/components/metadata/registries-license/registries-license.component.html
index cc5f9c25a..3d5788724 100644
--- a/src/app/features/registries/components/metadata/registries-license/registries-license.component.html
+++ b/src/app/features/registries/components/metadata/registries-license/registries-license.component.html
@@ -1,47 +1,7 @@
-
- {{ 'shared.license.title' | translate }}
-
- {{ 'shared.license.description' | translate }}
-
-
- {{ 'shared.license.helpText' | translate }}
- {{ 'common.links.helpGuide' | translate }}.
-
-
- @if (selectedLicense) {
-
- @if (selectedLicense.requiredFields.length) {
-
- }
-
-
-
-
- }
-
+
diff --git a/src/app/features/registries/components/metadata/registries-license/registries-license.component.ts b/src/app/features/registries/components/metadata/registries-license/registries-license.component.ts
index d81e74502..90b22d921 100644
--- a/src/app/features/registries/components/metadata/registries-license/registries-license.component.ts
+++ b/src/app/features/registries/components/metadata/registries-license/registries-license.component.ts
@@ -1,37 +1,18 @@
import { createDispatchMap, select } from '@ngxs/store';
-import { TranslatePipe } from '@ngx-translate/core';
-
-import { Card } from 'primeng/card';
-import { DatePicker } from 'primeng/datepicker';
-import { Divider } from 'primeng/divider';
-import { Select } from 'primeng/select';
-
import { ChangeDetectionStrategy, Component, inject } from '@angular/core';
import { FormBuilder, FormsModule, ReactiveFormsModule } from '@angular/forms';
import { ActivatedRoute } from '@angular/router';
-import { License } from '@osf/features/registries/models';
-import { FetchLicenses, RegistriesSelectors } from '@osf/features/registries/store';
-import { TextInputComponent, TruncatedTextComponent } from '@osf/shared/components';
+import { FetchLicenses, RegistriesSelectors, SaveLicense } from '@osf/features/registries/store';
+import { LicenseComponent } from '@osf/shared/components';
import { InputLimits } from '@osf/shared/constants';
-import { InterpolatePipe } from '@osf/shared/pipes';
+import { License, LicenseOptions } from '@osf/shared/models';
import { CustomValidators } from '@osf/shared/utils';
@Component({
selector: 'osf-registries-license',
- imports: [
- Card,
- TranslatePipe,
- Select,
- FormsModule,
- Divider,
- TruncatedTextComponent,
- DatePicker,
- TextInputComponent,
- InterpolatePipe,
- ReactiveFormsModule,
- ],
+ imports: [FormsModule, ReactiveFormsModule, LicenseComponent],
templateUrl: './registries-license.component.html',
styleUrl: './registries-license.component.scss',
changeDetection: ChangeDetectionStrategy.OnPush,
@@ -41,11 +22,11 @@ export class RegistriesLicenseComponent {
private readonly draftId = this.route.snapshot.params['id'];
private readonly fb = inject(FormBuilder);
- protected actions = createDispatchMap({ fetchLicenses: FetchLicenses });
+ protected actions = createDispatchMap({ fetchLicenses: FetchLicenses, saveLicense: SaveLicense });
protected licenses = select(RegistriesSelectors.getLicenses);
protected inputLimits = InputLimits;
- selectedLicense: License | null = null;
+ selectedLicense = select(RegistriesSelectors.getSelectedLicense);
currentYear = new Date();
licenseYear = this.currentYear;
licenseForm = this.fb.group({
@@ -57,7 +38,11 @@ export class RegistriesLicenseComponent {
this.actions.fetchLicenses();
}
- onSelectLicense(license: License): void {
- console.log('Selected License:', license);
+ createLicense(licenseDetails: { id: string; licenseOptions: LicenseOptions }) {
+ this.actions.saveLicense(this.draftId, licenseDetails.id, licenseDetails.licenseOptions);
+ }
+
+ selectLicense(license: License) {
+ this.actions.saveLicense(this.draftId, license.id);
}
}
diff --git a/src/app/features/registries/mappers/licenses.mapper.ts b/src/app/features/registries/mappers/licenses.mapper.ts
index 9ad0a8290..27e12fd9a 100644
--- a/src/app/features/registries/mappers/licenses.mapper.ts
+++ b/src/app/features/registries/mappers/licenses.mapper.ts
@@ -1,4 +1,4 @@
-import { License, LicensesResponseJsonApi } from '../models';
+import { License, LicensesResponseJsonApi } from '@osf/shared/models';
export class LicensesMapper {
static fromLicensesResponse(response: LicensesResponseJsonApi): License[] {
diff --git a/src/app/features/registries/mappers/registration.mapper.ts b/src/app/features/registries/mappers/registration.mapper.ts
index 9ae2e1bd2..2620bc2c9 100644
--- a/src/app/features/registries/mappers/registration.mapper.ts
+++ b/src/app/features/registries/mappers/registration.mapper.ts
@@ -8,6 +8,10 @@ export class RegistrationMapper {
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,
+ },
};
}
}
diff --git a/src/app/features/registries/models/index.ts b/src/app/features/registries/models/index.ts
index 69c4c8b7c..937fb5772 100644
--- a/src/app/features/registries/models/index.ts
+++ b/src/app/features/registries/models/index.ts
@@ -1,4 +1,3 @@
-export * from './license.model';
export * from './licenses-json-api.model';
export * from './page-schema.model';
export * from './project';
diff --git a/src/app/features/registries/models/license.model.ts b/src/app/features/registries/models/license.model.ts
index c4a243a7f..e69de29bb 100644
--- a/src/app/features/registries/models/license.model.ts
+++ b/src/app/features/registries/models/license.model.ts
@@ -1,7 +0,0 @@
-export interface License {
- id: string;
- name: string;
- requiredFields: string[];
- url: string;
- text: string;
-}
diff --git a/src/app/features/registries/models/licenses-json-api.model.ts b/src/app/features/registries/models/licenses-json-api.model.ts
index 32e8fc049..dde5dfe58 100644
--- a/src/app/features/registries/models/licenses-json-api.model.ts
+++ b/src/app/features/registries/models/licenses-json-api.model.ts
@@ -1,16 +1,21 @@
-import { ApiData, MetaJsonApi, PaginationLinksJsonApi } from '@osf/core/models';
+import { LicenseRecordJsonApi } from '@shared/models';
-export interface LicensesResponseJsonApi {
- data: LicenseDataJsonApi[];
- meta: MetaJsonApi;
- links: PaginationLinksJsonApi;
+export interface LicenseRelationshipJsonApi {
+ license: {
+ data: {
+ id: string;
+ type: 'licenses';
+ };
+ };
}
-export type LicenseDataJsonApi = ApiData;
-
-interface LicenseAttributesJsonApi {
- name: string;
- required_fields: string[];
- url: string;
- text: string;
+export interface LicensePayloadJsonApi {
+ data: {
+ type: 'draft_registrations';
+ id: string;
+ relationships: LicenseRelationshipJsonApi;
+ attributes: {
+ node_license?: LicenseRecordJsonApi;
+ };
+ };
}
diff --git a/src/app/features/registries/models/registration-json-api.model.ts b/src/app/features/registries/models/registration-json-api.model.ts
index f581282e6..4e4502248 100644
--- a/src/app/features/registries/models/registration-json-api.model.ts
+++ b/src/app/features/registries/models/registration-json-api.model.ts
@@ -1,4 +1,5 @@
import { ApiData, MetaJsonApi, PaginationLinksJsonApi } from '@osf/core/models';
+import { LicenseOptions } from '@osf/shared/models';
export interface RegistrationResponseJsonApi {
data: RegistrationDataJsonApi;
@@ -20,7 +21,7 @@ interface RegistrationAttributesJsonApi {
datetime_updated: string;
description: string;
has_project: boolean;
- node_license: string | null;
+ node_license: LicenseOptions;
registration_metadata: Record;
registration_responses: Record;
tags: string[];
@@ -30,8 +31,14 @@ interface RegistrationAttributesJsonApi {
interface RegistrationRelationshipsJsonApi {
registration_schema: {
data: {
- id: '58fd62fcda3e2400012ca5c1';
+ id: string;
type: 'registration-schemas';
};
};
+ license: {
+ data: {
+ id: string;
+ type: 'licenses';
+ };
+ };
}
diff --git a/src/app/features/registries/models/registration.model.ts b/src/app/features/registries/models/registration.model.ts
index 6a717fc94..7415a2156 100644
--- a/src/app/features/registries/models/registration.model.ts
+++ b/src/app/features/registries/models/registration.model.ts
@@ -1,6 +1,12 @@
+import { LicenseOptions } from '@osf/shared/models';
+
export interface Registration {
id: string;
title: string;
description: string;
registrationSchemaId: string;
+ license: {
+ id: string;
+ options: LicenseOptions;
+ };
}
diff --git a/src/app/features/registries/registries.routes.ts b/src/app/features/registries/registries.routes.ts
index c21f128e8..4119f0c54 100644
--- a/src/app/features/registries/registries.routes.ts
+++ b/src/app/features/registries/registries.routes.ts
@@ -10,7 +10,14 @@ import { SUBJECTS_SERVICE } from '@osf/shared/tokens/subjects.token';
import { ModerationState } from '../moderation/store';
-import { RegistrationSubjectsService } from './services';
+import {
+ LicensesHandlers,
+ ProjectsHandlers,
+ ProvidersHandlers,
+ RegistrationContributorsHandlers,
+ SubjectsHandlers,
+} from './store/handlers';
+import { LicensesService, RegistrationContributorsService, RegistrationSubjectsService } from './services';
export const registriesRoutes: Routes = [
{
@@ -18,6 +25,14 @@ export const registriesRoutes: Routes = [
component: RegistriesComponent,
providers: [
provideStates([RegistriesState, ContributorsState, SubjectsState]),
+ ProvidersHandlers,
+ ProjectsHandlers,
+ LicensesHandlers,
+ RegistrationContributorsHandlers,
+ SubjectsHandlers,
+ RegistrationSubjectsService,
+ RegistrationContributorsService,
+ LicensesService,
{
provide: SUBJECTS_SERVICE,
useClass: RegistrationSubjectsService,
diff --git a/src/app/features/registries/services/index.ts b/src/app/features/registries/services/index.ts
index 99fc8de16..a81908564 100644
--- a/src/app/features/registries/services/index.ts
+++ b/src/app/features/registries/services/index.ts
@@ -1,5 +1,6 @@
export * from './licenses.service';
export * from './projects.service';
export * from './providers.service';
+export * from './registration-contributors.service';
export * from './registration-subjects.service';
export * from './registries.service';
diff --git a/src/app/features/registries/services/licenses.service.ts b/src/app/features/registries/services/licenses.service.ts
index 8879ed1c7..f6b7d0f10 100644
--- a/src/app/features/registries/services/licenses.service.ts
+++ b/src/app/features/registries/services/licenses.service.ts
@@ -3,9 +3,10 @@ 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 { LicensesMapper } from '../mappers';
-import { License, LicensesResponseJsonApi } from '../models';
+import { LicensePayloadJsonApi, RegistrationDataJsonApi } from '../models';
import { environment } from 'src/environments/environment';
@@ -28,9 +29,7 @@ const data: any = {
],
};
-@Injectable({
- providedIn: 'root',
-})
+@Injectable()
export class LicensesService {
private apiUrl = environment.apiUrl;
private readonly jsonApiService = inject(JsonApiService);
@@ -49,4 +48,34 @@ export class LicensesService {
})
);
}
+
+ updateLicense(registrationId: string, licenseId: string, licenseOptions?: LicenseOptions) {
+ const payload: LicensePayloadJsonApi = {
+ data: {
+ type: 'draft_registrations',
+ id: registrationId,
+ relationships: {
+ license: {
+ data: {
+ id: licenseId,
+ type: 'licenses',
+ },
+ },
+ },
+ attributes: {
+ ...(licenseOptions && {
+ node_license: {
+ copyright_holders: [licenseOptions.copyrightHolders],
+ year: licenseOptions.year,
+ },
+ }),
+ },
+ },
+ };
+
+ return this.jsonApiService.patch(
+ `${this.apiUrl}/draft_registrations/${registrationId}/`,
+ payload
+ );
+ }
}
diff --git a/src/app/features/registries/services/registration-contributors.service.ts b/src/app/features/registries/services/registration-contributors.service.ts
new file mode 100644
index 000000000..b490ba880
--- /dev/null
+++ b/src/app/features/registries/services/registration-contributors.service.ts
@@ -0,0 +1,48 @@
+import { map, Observable } from 'rxjs';
+
+import { inject, Injectable } from '@angular/core';
+
+import { JsonApiResponse } from '@osf/core/models';
+import { JsonApiService } from '@osf/core/services';
+import { AddContributorType } from '@osf/shared/components/contributors/enums';
+import { ContributorsMapper } from '@osf/shared/components/contributors/mappers';
+import { ContributorAddModel, ContributorModel, ContributorResponse } from '@osf/shared/components/contributors/models';
+
+import { environment } from 'src/environments/environment';
+
+@Injectable()
+export class RegistrationContributorsService {
+ private apiUrl = environment.apiUrl;
+ private readonly jsonApiService = inject(JsonApiService);
+
+ getContributors(draftId: string): Observable {
+ return this.jsonApiService
+ .get>(`${this.apiUrl}/draft_registrations/${draftId}/contributors/`)
+ .pipe(map((contributors) => ContributorsMapper.fromResponse(contributors.data)));
+ }
+
+ addContributor(draftId: string, data: ContributorAddModel): Observable {
+ const baseUrl = `${this.apiUrl}/draft_registrations/${draftId}/contributors/`;
+ const type = data.id ? AddContributorType.Registered : AddContributorType.Unregistered;
+
+ const contributorData = { data: ContributorsMapper.toContributorAddRequest(data, type) };
+
+ return this.jsonApiService
+ .post(baseUrl, contributorData)
+ .pipe(map((contributor) => ContributorsMapper.fromContributorResponse(contributor)));
+ }
+
+ updateContributor(draftId: string, data: ContributorModel): Observable {
+ const baseUrl = `${environment.apiUrl}/draft_registrations/${draftId}/contributors/${data.userId}`;
+
+ const contributorData = { data: ContributorsMapper.toContributorAddRequest(data) };
+
+ return this.jsonApiService
+ .patch(baseUrl, contributorData)
+ .pipe(map((contributor) => ContributorsMapper.fromContributorResponse(contributor)));
+ }
+
+ deleteContributor(draftId: string, contributorId: string): Observable {
+ return this.jsonApiService.delete(`${this.apiUrl}/draft_registrations/${draftId}/contributors/${contributorId}`);
+ }
+}
diff --git a/src/app/features/registries/services/registration-subjects.service.ts b/src/app/features/registries/services/registration-subjects.service.ts
index 7f6f4f618..2b6209ccd 100644
--- a/src/app/features/registries/services/registration-subjects.service.ts
+++ b/src/app/features/registries/services/registration-subjects.service.ts
@@ -8,9 +8,7 @@ import { ISubjectsService, Subject, SubjectsResponseJsonApi } from '@osf/shared/
import { environment } from 'src/environments/environment';
-@Injectable({
- providedIn: 'root',
-})
+@Injectable()
export class RegistrationSubjectsService implements ISubjectsService {
private apiUrl = environment.apiUrl;
private readonly jsonApiService = inject(JsonApiService);
diff --git a/src/app/features/registries/services/registries.service.ts b/src/app/features/registries/services/registries.service.ts
index 5a89f9e4c..f5869b161 100644
--- a/src/app/features/registries/services/registries.service.ts
+++ b/src/app/features/registries/services/registries.service.ts
@@ -2,11 +2,7 @@ import { map, Observable } from 'rxjs';
import { inject, Injectable } from '@angular/core';
-import { JsonApiResponse } from '@osf/core/models';
import { JsonApiService } from '@osf/core/services';
-import { AddContributorType } from '@osf/shared/components/contributors/enums';
-import { ContributorsMapper } from '@osf/shared/components/contributors/mappers';
-import { ContributorAddModel, ContributorModel, ContributorResponse } from '@osf/shared/components/contributors/models';
import { PageSchemaMapper } from '../mappers';
import { RegistrationMapper } from '../mappers/registration.mapper';
@@ -81,35 +77,4 @@ export class RegistriesService {
.get(`${this.apiUrl}/schemas/registrations/${registrationSchemaId}/schema_blocks/`)
.pipe(map((response) => PageSchemaMapper.fromSchemaBlocksResponse(response)));
}
-
- getContributors(draftId: string): Observable {
- return this.jsonApiService
- .get>(`${this.apiUrl}/draft_registrations/${draftId}/contributors/`)
- .pipe(map((contributors) => ContributorsMapper.fromResponse(contributors.data)));
- }
-
- addContributor(draftId: string, data: ContributorAddModel): Observable {
- const baseUrl = `${this.apiUrl}/draft_registrations/${draftId}/contributors/`;
- const type = data.id ? AddContributorType.Registered : AddContributorType.Unregistered;
-
- const contributorData = { data: ContributorsMapper.toContributorAddRequest(data, type) };
-
- return this.jsonApiService
- .post(baseUrl, contributorData)
- .pipe(map((contributor) => ContributorsMapper.fromContributorResponse(contributor)));
- }
-
- updateContributor(draftId: string, data: ContributorModel): Observable {
- const baseUrl = `${environment.apiUrl}/draft_registrations/${draftId}/contributors/${data.userId}`;
-
- const contributorData = { data: ContributorsMapper.toContributorAddRequest(data) };
-
- return this.jsonApiService
- .patch(baseUrl, contributorData)
- .pipe(map((contributor) => ContributorsMapper.fromContributorResponse(contributor)));
- }
-
- deleteContributor(draftId: string, contributorId: string): Observable {
- return this.jsonApiService.delete(`${this.apiUrl}/draft_registrations/${draftId}/contributors/${contributorId}`);
- }
}
diff --git a/src/app/features/registries/store/default.state.ts b/src/app/features/registries/store/default.state.ts
new file mode 100644
index 000000000..a9e3c4d4c
--- /dev/null
+++ b/src/app/features/registries/store/default.state.ts
@@ -0,0 +1,45 @@
+import { RegistriesStateModel } from './registries.model';
+
+export const DefaultState: RegistriesStateModel = {
+ providers: {
+ data: [],
+ isLoading: false,
+ error: null,
+ },
+ projects: {
+ data: [],
+ isLoading: false,
+ error: null,
+ },
+ draftRegistration: {
+ isLoading: false,
+ data: null,
+ isSubmitting: false,
+ error: null,
+ },
+ contributorsList: {
+ data: [],
+ isLoading: false,
+ error: null,
+ },
+ registries: {
+ data: [],
+ isLoading: false,
+ error: null,
+ },
+ licenses: {
+ data: [],
+ isLoading: false,
+ error: null,
+ },
+ registrationSubjects: {
+ data: [],
+ isLoading: false,
+ error: null,
+ },
+ pagesSchema: {
+ data: [],
+ isLoading: false,
+ error: null,
+ },
+};
diff --git a/src/app/features/registries/store/handlers/contributors.handlers.ts b/src/app/features/registries/store/handlers/contributors.handlers.ts
new file mode 100644
index 000000000..f228381c4
--- /dev/null
+++ b/src/app/features/registries/store/handlers/contributors.handlers.ts
@@ -0,0 +1,106 @@
+import { StateContext } from '@ngxs/store';
+
+import { catchError, tap } from 'rxjs';
+
+import { inject, Injectable } from '@angular/core';
+
+import { handleSectionError } from '@osf/core/handlers/state-error.handler';
+
+import { RegistrationContributorsService } from '../../services/registration-contributors.service';
+import { AddContributor, DeleteContributor, FetchContributors, UpdateContributor } from '../registries.actions';
+import { RegistriesStateModel } from '../registries.model';
+
+@Injectable()
+export class RegistrationContributorsHandlers {
+ contributorsService = inject(RegistrationContributorsService);
+
+ fetchContributors(ctx: StateContext, action: FetchContributors) {
+ const state = ctx.getState();
+
+ ctx.patchState({
+ contributorsList: { ...state.contributorsList, isLoading: true, error: null },
+ });
+
+ return this.contributorsService.getContributors(action.draftId).pipe(
+ tap((contributors) => {
+ ctx.patchState({
+ contributorsList: {
+ ...state.contributorsList,
+ data: contributors,
+ isLoading: false,
+ },
+ });
+ }),
+ catchError((error) => handleSectionError(ctx, 'contributorsList', error))
+ );
+ }
+
+ addContributor(ctx: StateContext, action: AddContributor) {
+ const state = ctx.getState();
+
+ ctx.patchState({
+ contributorsList: { ...state.contributorsList, isLoading: true, error: null },
+ });
+
+ return this.contributorsService.addContributor(action.draftId, action.contributor).pipe(
+ tap((contributor) => {
+ const currentState = ctx.getState();
+
+ ctx.patchState({
+ contributorsList: {
+ ...currentState.contributorsList,
+ data: [...currentState.contributorsList.data, contributor],
+ isLoading: false,
+ },
+ });
+ }),
+ catchError((error) => handleSectionError(ctx, 'contributorsList', error))
+ );
+ }
+
+ updateContributor(ctx: StateContext, action: UpdateContributor) {
+ const state = ctx.getState();
+
+ ctx.patchState({
+ contributorsList: { ...state.contributorsList, isLoading: true, error: null },
+ });
+
+ return this.contributorsService.updateContributor(action.draftId, action.contributor).pipe(
+ tap((updatedContributor) => {
+ const currentState = ctx.getState();
+
+ ctx.patchState({
+ contributorsList: {
+ ...currentState.contributorsList,
+ data: currentState.contributorsList.data.map((contributor) =>
+ contributor.id === updatedContributor.id ? updatedContributor : contributor
+ ),
+ isLoading: false,
+ },
+ });
+ }),
+ catchError((error) => handleSectionError(ctx, 'contributorsList', error))
+ );
+ }
+
+ deleteContributor(ctx: StateContext, action: DeleteContributor) {
+ const state = ctx.getState();
+
+ ctx.patchState({
+ contributorsList: { ...state.contributorsList, isLoading: true, error: null },
+ });
+
+ return this.contributorsService.deleteContributor(action.draftId, action.contributorId).pipe(
+ tap(() => {
+ ctx.patchState({
+ contributorsList: {
+ ...state.contributorsList,
+ data: state.contributorsList.data.filter((contributor) => contributor.userId !== action.contributorId),
+ isLoading: false,
+ },
+ });
+ }),
+ catchError((error) => handleSectionError(ctx, 'contributorsList', error))
+ );
+ }
+}
diff --git a/src/app/features/registries/store/handlers/index.ts b/src/app/features/registries/store/handlers/index.ts
new file mode 100644
index 000000000..383d0b6bc
--- /dev/null
+++ b/src/app/features/registries/store/handlers/index.ts
@@ -0,0 +1,5 @@
+export * from './contributors.handlers';
+export * from './licenses.handlers';
+export * from './projects.handlers';
+export * from './providers.handlers';
+export * from './subjects.handlers';
diff --git a/src/app/features/registries/store/handlers/licenses.handlers.ts b/src/app/features/registries/store/handlers/licenses.handlers.ts
new file mode 100644
index 000000000..28ee8b15d
--- /dev/null
+++ b/src/app/features/registries/store/handlers/licenses.handlers.ts
@@ -0,0 +1,63 @@
+import { StateContext } from '@ngxs/store';
+
+import { catchError, tap } from 'rxjs';
+
+import { inject, Injectable } from '@angular/core';
+
+import { handleSectionError } from '@osf/core/handlers';
+
+import { LicensesService } from '../../services';
+import { SaveLicense } from '../registries.actions';
+import { RegistriesStateModel } from '../registries.model';
+
+@Injectable()
+export class LicensesHandlers {
+ licensesService = inject(LicensesService);
+
+ fetchLicenses(ctx: StateContext) {
+ ctx.patchState({
+ licenses: {
+ ...ctx.getState().licenses,
+ isLoading: true,
+ },
+ });
+
+ return this.licensesService.getLicenses().pipe(
+ tap((licenses) => {
+ ctx.patchState({
+ licenses: {
+ data: licenses,
+ isLoading: false,
+ error: null,
+ },
+ });
+ }),
+ catchError((error) => handleSectionError(ctx, 'licenses', error))
+ );
+ }
+
+ saveLicense(ctx: StateContext, { registrationId, licenseId, licenseOptions }: SaveLicense) {
+ const state = ctx.getState();
+ ctx.patchState({
+ licenses: {
+ ...state.licenses,
+ 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))
+ ();
+ }
+}
diff --git a/src/app/features/registries/store/handlers/projects.handlers.ts b/src/app/features/registries/store/handlers/projects.handlers.ts
new file mode 100644
index 000000000..d0f161842
--- /dev/null
+++ b/src/app/features/registries/store/handlers/projects.handlers.ts
@@ -0,0 +1,38 @@
+import { StateContext } from '@ngxs/store';
+
+import { inject, Injectable } from '@angular/core';
+
+import { Project } from '../../models';
+import { ProjectsService } from '../../services';
+import { DefaultState } from '../default.state';
+import { RegistriesStateModel } from '../registries.model';
+
+@Injectable()
+export class ProjectsHandlers {
+ projectsService = inject(ProjectsService);
+
+ getProjects({ patchState }: StateContext) {
+ patchState({
+ projects: {
+ ...DefaultState.projects,
+ isLoading: true,
+ },
+ });
+ return this.projectsService.getProjects().subscribe({
+ next: (projects: Project[]) => {
+ patchState({
+ projects: {
+ data: projects,
+ isLoading: false,
+ error: null,
+ },
+ });
+ },
+ error: (error) => {
+ patchState({
+ projects: { ...DefaultState.projects, isLoading: false, error },
+ });
+ },
+ });
+ }
+}
diff --git a/src/app/features/registries/store/handlers/providers.handlers.ts b/src/app/features/registries/store/handlers/providers.handlers.ts
new file mode 100644
index 000000000..c1c304065
--- /dev/null
+++ b/src/app/features/registries/store/handlers/providers.handlers.ts
@@ -0,0 +1,41 @@
+import { StateContext } from '@ngxs/store';
+
+import { inject, Injectable } from '@angular/core';
+
+import { ProvidersService } from '../../services';
+import { DefaultState } from '../default.state';
+import { RegistriesStateModel } from '../registries.model';
+
+@Injectable()
+export class ProvidersHandlers {
+ providersService = inject(ProvidersService);
+
+ getProviders({ patchState }: StateContext) {
+ patchState({
+ providers: {
+ ...DefaultState.providers,
+ isLoading: true,
+ },
+ });
+ return this.providersService.getProviders().subscribe({
+ next: (providers) => {
+ patchState({
+ providers: {
+ data: providers,
+ isLoading: false,
+ error: null,
+ },
+ });
+ },
+ error: (error) => {
+ patchState({
+ providers: {
+ ...DefaultState.providers,
+ isLoading: false,
+ error,
+ },
+ });
+ },
+ });
+ }
+}
diff --git a/src/app/features/registries/store/handlers/subjects.handlers.ts b/src/app/features/registries/store/handlers/subjects.handlers.ts
new file mode 100644
index 000000000..7f06f3d63
--- /dev/null
+++ b/src/app/features/registries/store/handlers/subjects.handlers.ts
@@ -0,0 +1,64 @@
+import { StateContext } from '@ngxs/store';
+
+import { catchError, tap } from 'rxjs';
+
+import { inject, Injectable } from '@angular/core';
+
+import { handleSectionError } from '@osf/core/handlers';
+
+import { RegistrationSubjectsService } from '../../services';
+import { FetchRegistrationSubjects, UpdateRegistrationSubjects } from '../registries.actions';
+import { RegistriesStateModel } from '../registries.model';
+
+@Injectable()
+export class SubjectsHandlers {
+ subjectsService = inject(RegistrationSubjectsService);
+
+ fetchRegistrationSubjects(ctx: StateContext, { registrationId }: FetchRegistrationSubjects) {
+ ctx.patchState({
+ registrationSubjects: {
+ ...ctx.getState().registrationSubjects,
+ isLoading: true,
+ error: null,
+ },
+ });
+
+ return this.subjectsService.getRegistrationSubjects(registrationId).pipe(
+ tap((subjects) => {
+ ctx.patchState({
+ registrationSubjects: {
+ data: subjects,
+ isLoading: false,
+ error: null,
+ },
+ });
+ }),
+ catchError((error) => handleSectionError(ctx, 'registrationSubjects', error))
+ );
+ }
+
+ updateRegistrationSubjects(
+ ctx: StateContext,
+ { registrationId, subjects }: UpdateRegistrationSubjects
+ ) {
+ ctx.patchState({
+ registrationSubjects: {
+ ...ctx.getState().registrationSubjects,
+ isLoading: true,
+ error: null,
+ },
+ });
+ return this.subjectsService.updateRegistrationSubjects(registrationId, subjects).pipe(
+ tap(() => {
+ ctx.patchState({
+ registrationSubjects: {
+ data: subjects,
+ isLoading: false,
+ error: null,
+ },
+ });
+ }),
+ catchError((error) => handleSectionError(ctx, 'registrationSubjects', error))
+ );
+ }
+}
diff --git a/src/app/features/registries/store/registries.actions.ts b/src/app/features/registries/store/registries.actions.ts
index bc0e1579e..6de2aaa41 100644
--- a/src/app/features/registries/store/registries.actions.ts
+++ b/src/app/features/registries/store/registries.actions.ts
@@ -1,5 +1,5 @@
import { ContributorAddModel, ContributorModel } from '@osf/shared/components/contributors/models';
-import { Subject } from '@osf/shared/models';
+import { LicenseOptions, Subject } from '@osf/shared/models';
export class GetRegistries {
static readonly type = '[Registries] Get Registries';
@@ -70,6 +70,15 @@ export class FetchLicenses {
static readonly type = '[Registries] Fetch Licenses';
}
+export class SaveLicense {
+ static readonly type = '[Registries] Save License';
+ constructor(
+ public registrationId: string,
+ public licenseId: string,
+ public licenseOptions?: LicenseOptions
+ ) {}
+}
+
export class FetchRegistrationSubjects {
static readonly type = '[Registries] Fetch Registration Subjects';
constructor(public registrationId: string) {}
diff --git a/src/app/features/registries/store/registries.model.ts b/src/app/features/registries/store/registries.model.ts
index 2ce1f6202..b36e022de 100644
--- a/src/app/features/registries/store/registries.model.ts
+++ b/src/app/features/registries/store/registries.model.ts
@@ -1,7 +1,7 @@
import { ContributorModel } from '@osf/shared/components/contributors/models';
-import { AsyncStateModel, Resource, Subject } from '@shared/models';
+import { AsyncStateModel, License, Resource, Subject } from '@shared/models';
-import { License, PageSchema, Project, Provider } from '../models';
+import { PageSchema, Project, Provider } from '../models';
import { Registration } from '../models/registration.model';
export interface RegistriesStateModel {
diff --git a/src/app/features/registries/store/registries.selectors.ts b/src/app/features/registries/store/registries.selectors.ts
index 4ea91666a..c3fb6dcf2 100644
--- a/src/app/features/registries/store/registries.selectors.ts
+++ b/src/app/features/registries/store/registries.selectors.ts
@@ -1,8 +1,8 @@
import { Selector } from '@ngxs/store';
-import { Resource, Subject } from '@shared/models';
+import { License, Resource, Subject } from '@shared/models';
-import { License, PageSchema, Project, Provider, Registration } from '../models';
+import { PageSchema, Project, Provider, Registration } from '../models';
import { RegistriesStateModel } from './registries.model';
import { RegistriesState } from './registries.state';
@@ -58,6 +58,11 @@ export class RegistriesSelectors {
return state.licenses.data;
}
+ @Selector([RegistriesState])
+ static getSelectedLicense(state: RegistriesStateModel) {
+ return state.draftRegistration.data?.license || null;
+ }
+
@Selector([RegistriesState])
static getPagesSchema(state: RegistriesStateModel): PageSchema[] {
return state.pagesSchema.data;
diff --git a/src/app/features/registries/store/registries.state.ts b/src/app/features/registries/store/registries.state.ts
index 6f01d6cf9..4b04264e6 100644
--- a/src/app/features/registries/store/registries.state.ts
+++ b/src/app/features/registries/store/registries.state.ts
@@ -9,15 +9,14 @@ import { ResourceTab } from '@osf/shared/enums';
import { SearchService } from '@osf/shared/services';
import { getResourceTypes } from '@osf/shared/utils';
-import { Project } from '../models';
-import {
- LicensesService,
- ProjectsService,
- ProvidersService,
- RegistrationSubjectsService,
- RegistriesService,
-} from '../services';
-
+import { RegistriesService } from '../services';
+
+import { RegistrationContributorsHandlers } from './handlers/contributors.handlers';
+import { LicensesHandlers } from './handlers/licenses.handlers';
+import { ProjectsHandlers } from './handlers/projects.handlers';
+import { ProvidersHandlers } from './handlers/providers.handlers';
+import { SubjectsHandlers } from './handlers/subjects.handlers';
+import { DefaultState } from './default.state';
import {
AddContributor,
CreateDraft,
@@ -31,55 +30,12 @@ import {
GetProjects,
GetProviders,
GetRegistries,
+ SaveLicense,
UpdateContributor,
UpdateRegistrationSubjects,
} from './registries.actions';
import { RegistriesStateModel } from './registries.model';
-const DefaultState: RegistriesStateModel = {
- providers: {
- data: [],
- isLoading: false,
- error: null,
- },
- projects: {
- data: [],
- isLoading: false,
- error: null,
- },
- draftRegistration: {
- isLoading: false,
- data: null,
- isSubmitting: false,
- error: null,
- },
- contributorsList: {
- data: [],
- isLoading: false,
- error: null,
- },
- registries: {
- data: [],
- isLoading: false,
- error: null,
- },
- licenses: {
- data: [],
- isLoading: false,
- error: null,
- },
- registrationSubjects: {
- data: [],
- isLoading: false,
- error: null,
- },
- pagesSchema: {
- data: [],
- isLoading: false,
- error: null,
- },
-};
-
@State({
name: 'registries',
defaults: { ...DefaultState },
@@ -87,11 +43,13 @@ const DefaultState: RegistriesStateModel = {
@Injectable()
export class RegistriesState {
searchService = inject(SearchService);
- providersService = inject(ProvidersService);
- projectsService = inject(ProjectsService);
registriesService = inject(RegistriesService);
- licensesService = inject(LicensesService);
- subjectsService = inject(RegistrationSubjectsService);
+
+ providersHandler = inject(ProvidersHandlers);
+ projectsHandler = inject(ProjectsHandlers);
+ licensesHandler = inject(LicensesHandlers);
+ subjectsHandler = inject(SubjectsHandlers);
+ contributorsHandler = inject(RegistrationContributorsHandlers);
@Action(GetRegistries)
getRegistries(ctx: StateContext) {
@@ -120,59 +78,13 @@ export class RegistriesState {
}
@Action(GetProjects)
- getProjects({ patchState }: StateContext) {
- patchState({
- projects: {
- ...DefaultState.projects,
- isLoading: true,
- },
- });
- return this.projectsService.getProjects().subscribe({
- next: (projects: Project[]) => {
- patchState({
- projects: {
- data: projects,
- isLoading: false,
- error: null,
- },
- });
- },
- error: (error) => {
- patchState({
- projects: { ...DefaultState.projects, isLoading: false, error },
- });
- },
- });
+ getProjects(ctx: StateContext) {
+ return this.projectsHandler.getProjects(ctx);
}
@Action(GetProviders)
- getProviders({ patchState }: StateContext) {
- patchState({
- providers: {
- ...DefaultState.providers,
- isLoading: true,
- },
- });
- return this.providersService.getProviders().subscribe({
- next: (providers) => {
- patchState({
- providers: {
- data: providers,
- isLoading: false,
- error: null,
- },
- });
- },
- error: (error) => {
- patchState({
- providers: {
- ...DefaultState.providers,
- isLoading: false,
- error,
- },
- });
- },
- });
+ getProviders(ctx: StateContext) {
+ return this.providersHandler.getProviders(ctx);
}
@Action(CreateDraft)
@@ -279,143 +191,37 @@ export class RegistriesState {
@Action(FetchContributors)
fetchContributors(ctx: StateContext, action: FetchContributors) {
- const state = ctx.getState();
-
- ctx.patchState({
- contributorsList: { ...state.contributorsList, isLoading: true, error: null },
- });
-
- return this.registriesService.getContributors(action.draftId).pipe(
- tap((contributors) => {
- ctx.patchState({
- contributorsList: {
- ...state.contributorsList,
- data: contributors,
- isLoading: false,
- },
- });
- }),
- catchError((error) => handleSectionError(ctx, 'contributorsList', error))
- );
+ return this.contributorsHandler.fetchContributors(ctx, action);
}
@Action(AddContributor)
addContributor(ctx: StateContext, action: AddContributor) {
- const state = ctx.getState();
-
- ctx.patchState({
- contributorsList: { ...state.contributorsList, isLoading: true, error: null },
- });
-
- return this.registriesService.addContributor(action.draftId, action.contributor).pipe(
- tap((contributor) => {
- const currentState = ctx.getState();
-
- ctx.patchState({
- contributorsList: {
- ...currentState.contributorsList,
- data: [...currentState.contributorsList.data, contributor],
- isLoading: false,
- },
- });
- }),
- catchError((error) => handleSectionError(ctx, 'contributorsList', error))
- );
+ return this.contributorsHandler.addContributor(ctx, action);
}
@Action(UpdateContributor)
updateContributor(ctx: StateContext, action: UpdateContributor) {
- const state = ctx.getState();
-
- ctx.patchState({
- contributorsList: { ...state.contributorsList, isLoading: true, error: null },
- });
-
- return this.registriesService.updateContributor(action.draftId, action.contributor).pipe(
- tap((updatedContributor) => {
- const currentState = ctx.getState();
-
- ctx.patchState({
- contributorsList: {
- ...currentState.contributorsList,
- data: currentState.contributorsList.data.map((contributor) =>
- contributor.id === updatedContributor.id ? updatedContributor : contributor
- ),
- isLoading: false,
- },
- });
- }),
- catchError((error) => handleSectionError(ctx, 'contributorsList', error))
- );
+ return this.contributorsHandler.updateContributor(ctx, action);
}
@Action(DeleteContributor)
deleteContributor(ctx: StateContext, action: DeleteContributor) {
- const state = ctx.getState();
-
- ctx.patchState({
- contributorsList: { ...state.contributorsList, isLoading: true, error: null },
- });
-
- return this.registriesService.deleteContributor(action.draftId, action.contributorId).pipe(
- tap(() => {
- ctx.patchState({
- contributorsList: {
- ...state.contributorsList,
- data: state.contributorsList.data.filter((contributor) => contributor.userId !== action.contributorId),
- isLoading: false,
- },
- });
- }),
- catchError((error) => handleSectionError(ctx, 'contributorsList', error))
- );
+ return this.contributorsHandler.deleteContributor(ctx, action);
}
@Action(FetchLicenses)
fetchLicenses(ctx: StateContext) {
- ctx.patchState({
- licenses: {
- ...ctx.getState().licenses,
- isLoading: true,
- },
- });
+ return this.licensesHandler.fetchLicenses(ctx);
+ }
- return this.licensesService.getLicenses().pipe(
- tap((licenses) => {
- ctx.patchState({
- licenses: {
- data: licenses,
- isLoading: false,
- error: null,
- },
- });
- }),
- catchError((error) => handleSectionError(ctx, 'licenses', error))
- );
+ @Action(SaveLicense)
+ saveLicense(ctx: StateContext, { registrationId, licenseId, licenseOptions }: SaveLicense) {
+ return this.licensesHandler.saveLicense(ctx, { registrationId, licenseId, licenseOptions });
}
@Action(FetchRegistrationSubjects)
fetchRegistrationSubjects(ctx: StateContext, { registrationId }: FetchRegistrationSubjects) {
- ctx.patchState({
- registrationSubjects: {
- ...ctx.getState().registrationSubjects,
- isLoading: true,
- error: null,
- },
- });
-
- return this.subjectsService.getRegistrationSubjects(registrationId).pipe(
- tap((subjects) => {
- ctx.patchState({
- registrationSubjects: {
- data: subjects,
- isLoading: false,
- error: null,
- },
- });
- }),
- catchError((error) => handleSectionError(ctx, 'registrationSubjects', error))
- );
+ return this.subjectsHandler.fetchRegistrationSubjects(ctx, { registrationId });
}
@Action(UpdateRegistrationSubjects)
@@ -423,24 +229,6 @@ export class RegistriesState {
ctx: StateContext,
{ registrationId, subjects }: UpdateRegistrationSubjects
) {
- ctx.patchState({
- registrationSubjects: {
- ...ctx.getState().registrationSubjects,
- isLoading: true,
- error: null,
- },
- });
- return this.subjectsService.updateRegistrationSubjects(registrationId, subjects).pipe(
- tap(() => {
- ctx.patchState({
- registrationSubjects: {
- data: subjects,
- isLoading: false,
- error: null,
- },
- });
- }),
- catchError((error) => handleSectionError(ctx, 'registrationSubjects', error))
- );
+ return this.subjectsHandler.updateRegistrationSubjects(ctx, { registrationId, subjects });
}
}