Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 23 additions & 5 deletions src/app/features/contributors/contributors.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ import {
AcceptRequestAccess,
AddContributor,
BulkAddContributors,
BulkAddContributorsFromParentProject,
BulkUpdateContributors,
ContributorsSelectors,
CreateViewOnlyLink,
Expand Down Expand Up @@ -159,6 +160,7 @@ export class ContributorsComponent implements OnInit {
deleteContributor: DeleteContributor,
bulkUpdateContributors: BulkUpdateContributors,
bulkAddContributors: BulkAddContributors,
bulkAddContributorsFromParentProject: BulkAddContributorsFromParentProject,
addContributor: AddContributor,
createViewOnlyLink: CreateViewOnlyLink,
deleteViewOnlyLink: DeleteViewOnlyLink,
Expand Down Expand Up @@ -246,17 +248,19 @@ export class ContributorsComponent implements OnInit {

openAddContributorDialog() {
const addedContributorIds = this.initialContributors().map((x) => x.userId);
const rootParentId = this.resourceDetails().rootParentId ?? this.resourceId();
const resourceDetails = this.resourceDetails();
const resourceId = this.resourceId();
const rootParentId = resourceDetails.rootParentId ?? resourceId;

this.loaderService.show();

this.actions
.getResourceWithChildren(rootParentId, this.resourceId(), this.resourceType())
.getResourceWithChildren(rootParentId, resourceId, this.resourceType())
.pipe(takeUntilDestroyed(this.destroyRef))
.subscribe(() => {
this.loaderService.hide();

const components = this.mapNodesToComponentCheckboxItems(this.resourceChildren(), this.resourceId());
const components = this.mapNodesToComponentCheckboxItems(this.resourceChildren(), resourceId);

this.customDialogService
.open(AddContributorDialogComponent, {
Expand All @@ -265,15 +269,22 @@ export class ContributorsComponent implements OnInit {
data: {
addedContributorIds,
components,
resourceName: this.resourceDetails().title,
resourceName: resourceDetails.title,
parentResourceName: resourceDetails.parent?.title,
allowAddingContributorsFromParentProject:
this.resourceType() === ResourceType.Project &&
resourceDetails.rootParentId &&
resourceDetails.rootParentId !== resourceId,
},
})
.onClose.pipe(
filter((res: ContributorDialogAddModel) => !!res),
takeUntilDestroyed(this.destroyRef)
)
.subscribe((res: ContributorDialogAddModel) => {
if (res.type === AddContributorType.Unregistered) {
if (res.type === AddContributorType.ParentProject) {
this.addContributorsFromParentProjectToComponents();
} else if (res.type === AddContributorType.Unregistered) {
this.openAddUnregisteredContributorDialog();
} else {
this.addContributorsToComponents(res);
Expand Down Expand Up @@ -303,6 +314,13 @@ export class ContributorsComponent implements OnInit {
.subscribe(() => this.toastService.showSuccess('project.contributors.toastMessages.multipleAddSuccessMessage'));
}

private addContributorsFromParentProjectToComponents(): void {
this.actions
.bulkAddContributorsFromParentProject(this.resourceId(), this.resourceType())
.pipe(takeUntilDestroyed(this.destroyRef))
.subscribe(() => this.toastService.showSuccess('project.contributors.toastMessages.multipleAddSuccessMessage'));
}

openAddUnregisteredContributorDialog() {
this.customDialogService
.open(AddUnregisteredContributorDialogComponent, {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,22 @@
</div>
}

@if (allowAddingContributorsFromParentProject() && this.isSearchState()) {
<div class="flex justify-content-center mt-2">
<p-button
class="w-full"
styleClass="w-full"
severity="secondary"
(onClick)="addSourceProjectContributors()"
[label]="
'project.contributors.addDialog.addingContributorsFromParentProject'
| translate: { projectName: parentResourceName() }
"
>
</p-button>
</div>
}

<div class="flex gap-2 mt-6">
<p-button
class="w-full"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,8 @@ export class AddContributorDialogComponent implements OnInit, OnDestroy {
readonly selectedUsers = signal<ContributorAddModel[]>([]);
readonly components = signal<ComponentCheckboxItemModel[]>([]);
readonly resourceName = signal<string>('');
readonly parentResourceName = signal<string>('');
readonly allowAddingContributorsFromParentProject = signal<boolean>(false);

readonly contributorNames = computed(() =>
this.selectedUsers()
Expand Down Expand Up @@ -113,6 +115,10 @@ export class AddContributorDialogComponent implements OnInit, OnDestroy {
}
}

addSourceProjectContributors(): void {
this.closeDialogWithData(AddContributorType.ParentProject);
}

addUnregistered(): void {
this.dialogRef.close({
data: [],
Expand All @@ -129,7 +135,8 @@ export class AddContributorDialogComponent implements OnInit, OnDestroy {
private initializeDialogData(): void {
this.selectedUsers.set([]);

const { components, resourceName } = this.config.data || {};
const { components, resourceName, parentResourceName, allowAddingContributorsFromParentProject } =
this.config.data || {};

if (components) {
this.components.set(components);
Expand All @@ -138,16 +145,24 @@ export class AddContributorDialogComponent implements OnInit, OnDestroy {
if (resourceName) {
this.resourceName.set(resourceName);
}

if (allowAddingContributorsFromParentProject) {
this.allowAddingContributorsFromParentProject.set(allowAddingContributorsFromParentProject);
}

if (parentResourceName) {
this.parentResourceName.set(parentResourceName);
}
}

private closeDialogWithData(): void {
private closeDialogWithData(AddContributorTypeValue = AddContributorType.Registered): void {
const childNodeIds = this.components()
.filter((c) => c.checked && !c.isCurrent)
.map((c) => c.id);

this.dialogRef.close({
data: this.selectedUsers(),
type: AddContributorType.Registered,
type: AddContributorTypeValue,
childNodeIds: childNodeIds.length > 0 ? childNodeIds : undefined,
} as ContributorDialogAddModel);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
export enum AddContributorType {
Registered = 1,
Unregistered,
ParentProject,
}
1 change: 1 addition & 0 deletions src/app/shared/mappers/nodes/base-node.mapper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ export class BaseNodeMapper {
wikiEnabled: data.attributes.wiki_enabled,
customCitation: data.attributes.custom_citation || undefined,
rootParentId: data.relationships.root?.data?.id,
parent: data.embeds?.parent?.data ? this.getNodeData(data.embeds?.parent.data) : undefined,
};
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import {
BaseNodeDataJsonApi,
ContributorDataJsonApi,
IdentifierAttributes,
IdentifiersJsonApiData,
Expand All @@ -23,6 +24,9 @@ export interface BaseNodeEmbedsJsonApi {
region?: {
data: RegionDataJsonApi;
};
parent?: {
data: BaseNodeDataJsonApi;
};
}

export interface JsonApiResource<T extends string, A> {
Expand Down
1 change: 1 addition & 0 deletions src/app/shared/models/nodes/base-node.model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ export interface BaseNodeModel {
wikiEnabled: boolean;
rootParentId?: string;
type: string;
parent?: BaseNodeModel;
}

export interface NodeModel extends BaseNodeModel {
Expand Down
6 changes: 6 additions & 0 deletions src/app/shared/services/contributors.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,12 @@ export class ContributorsService {
.pipe(map((contributor) => ContributorsMapper.getContributor(contributor.data)));
}

addContributorsFromProject(resourceType: ResourceType, resourceId: string): Observable<void> {
const baseUrl = `${this.getBaseUrl(resourceType, resourceId)}/?copy_contributors_from_parent_project=true`;
const contributorData = { data: { type: AddContributorType.ParentProject } };
return this.jsonApiService.patch(baseUrl, contributorData);
}

deleteContributor(resourceType: ResourceType, resourceId: string, userId: string): Observable<void> {
const baseUrl = `${this.getBaseUrl(resourceType, resourceId)}/${userId}/`;

Expand Down
6 changes: 4 additions & 2 deletions src/app/shared/services/resource.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,9 +66,11 @@ export class ResourceGuidService {

getResourceDetails(resourceId: string, resourceType: ResourceType): Observable<BaseNodeModel> {
const resourcePath = this.urlMap.get(resourceType);

const params: Record<string, unknown> = {
embed: 'parent',
};
return this.jsonApiService
.get<ResponseDataJsonApi<BaseNodeDataJsonApi>>(`${this.apiUrl}/${resourcePath}/${resourceId}/`)
.get<ResponseDataJsonApi<BaseNodeDataJsonApi>>(`${this.apiUrl}/${resourcePath}/${resourceId}/`, params)
.pipe(map((response) => BaseNodeMapper.getNodeData(response.data)));
}

Expand Down
9 changes: 9 additions & 0 deletions src/app/shared/stores/contributors/contributors.actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,15 @@ export class BulkAddContributors {
) {}
}

export class BulkAddContributorsFromParentProject {
static readonly type = '[Contributors] Bulk Add Contributors From Parent Project';

constructor(
public resourceId: string | undefined | null,
public resourceType: ResourceType | undefined
) {}
}

export class DeleteContributor {
static readonly type = '[Contributors] Delete Contributor';

Expand Down
24 changes: 24 additions & 0 deletions src/app/shared/stores/contributors/contributors.state.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {
AddContributor,
BulkAddContributors,
BulkUpdateContributors,
BulkAddContributorsFromParentProject,
ClearUsers,
DeleteContributor,
GetAllContributors,
Expand Down Expand Up @@ -206,6 +207,29 @@ export class ContributorsState {
);
}

@Action(BulkAddContributorsFromParentProject)
bulkAddContributorsFromParentProject(
ctx: StateContext<ContributorsStateModel>,
action: BulkAddContributorsFromParentProject
) {
const state = ctx.getState();

if (!action.resourceId || !action.resourceType) {
return;
}

ctx.patchState({
contributorsList: { ...state.contributorsList, isLoading: true, error: null },
});

return this.contributorsService.addContributorsFromProject(action.resourceType, action.resourceId).pipe(
tap(() => {
ctx.dispatch(new GetAllContributors(action.resourceId, action.resourceType));
}),
catchError((error) => handleSectionError(ctx, 'contributorsList', error))
);
}

@Action(DeleteContributor)
deleteContributor(ctx: StateContext<ContributorsStateModel>, action: DeleteContributor) {
const state = ctx.getState();
Expand Down
3 changes: 2 additions & 1 deletion src/assets/i18n/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -606,7 +606,8 @@
"bibliographicContributor": "Bibliographic Contributor",
"addUnregisteredContributor": "Add Unregistered Contributor",
"addRegisteredContributor": "Add Registered Contributor",
"unregisteredContributorNotification": "We will notify the user that they have been added to your project"
"unregisteredContributorNotification": "We will notify the user that they have been added to your project",
"addingContributorsFromParentProject": "Import contributors from {{projectName}}"
},
"removeDialog": {
"title": "Remove contributor",
Expand Down
Loading