From 81348c5da366ab7b52f118f8ef1296fc2e26aaa0 Mon Sep 17 00:00:00 2001 From: NazarMykhalkevych Date: Wed, 10 Sep 2025 15:50:45 +0300 Subject: [PATCH] fix(children): refactor get children request --- .../create-view-link-dialog.component.ts | 4 +-- .../contributors/contributors.component.ts | 1 + .../models/resource-info.model.ts | 1 + .../files-widget/files-widget.component.ts | 2 +- .../mappers/project-overview.mapper.ts | 1 + .../models/project-overview.models.ts | 7 +++++ .../overview/project-overview.component.ts | 9 ++++++- .../shared/mappers/nodes/base-node.mapper.ts | 16 ++++++++++-- .../shared/models/nodes/base-node.model.ts | 1 + src/app/shared/services/resource.service.ts | 10 ++++--- .../current-resource.actions.ts | 1 + .../current-resource.state.ts | 26 ++++++++++--------- 12 files changed, 58 insertions(+), 21 deletions(-) diff --git a/src/app/features/project/contributors/components/create-view-link-dialog/create-view-link-dialog.component.ts b/src/app/features/project/contributors/components/create-view-link-dialog/create-view-link-dialog.component.ts index da23abcb4..fac977b87 100644 --- a/src/app/features/project/contributors/components/create-view-link-dialog/create-view-link-dialog.component.ts +++ b/src/app/features/project/contributors/components/create-view-link-dialog/create-view-link-dialog.component.ts @@ -72,10 +72,10 @@ export class CreateViewLinkDialogComponent implements OnInit { ngOnInit(): void { const currentResource = this.config.data as ResourceInfoModel; - const { id, type } = currentResource; + const { id, type, rootParentId } = currentResource; if (id) { - this.actions.getComponents(id, type); + this.actions.getComponents(rootParentId ?? '', id, type); } } diff --git a/src/app/features/project/contributors/contributors.component.ts b/src/app/features/project/contributors/contributors.component.ts index 7a2d962fd..5c61531c4 100644 --- a/src/app/features/project/contributors/contributors.component.ts +++ b/src/app/features/project/contributors/contributors.component.ts @@ -264,6 +264,7 @@ export class ContributorsComponent implements OnInit { id: this.resourceDetails().id, title: this.resourceDetails().title, type: this.resourceType(), + rootParentId: this.resourceDetails().rootParentId, }; this.dialogService diff --git a/src/app/features/project/contributors/models/resource-info.model.ts b/src/app/features/project/contributors/models/resource-info.model.ts index 1925a8dab..ddd417ac2 100644 --- a/src/app/features/project/contributors/models/resource-info.model.ts +++ b/src/app/features/project/contributors/models/resource-info.model.ts @@ -4,4 +4,5 @@ export interface ResourceInfoModel { id: string; title: string; type: ResourceType; + rootParentId?: string; } diff --git a/src/app/features/project/overview/components/files-widget/files-widget.component.ts b/src/app/features/project/overview/components/files-widget/files-widget.component.ts index e54bcfec3..5007345db 100644 --- a/src/app/features/project/overview/components/files-widget/files-widget.component.ts +++ b/src/app/features/project/overview/components/files-widget/files-widget.component.ts @@ -76,7 +76,7 @@ export class FilesWidgetComponent { readonly options = computed(() => { const components = this.components().filter((component) => this.rootOption().value !== component.id); - return [this.rootOption(), ...this.buildOptions(components).reverse()]; + return [this.rootOption(), ...this.buildOptions(components)]; }); readonly storageAddons = computed(() => { diff --git a/src/app/features/project/overview/mappers/project-overview.mapper.ts b/src/app/features/project/overview/mappers/project-overview.mapper.ts index aee91a847..2fe859310 100644 --- a/src/app/features/project/overview/mappers/project-overview.mapper.ts +++ b/src/app/features/project/overview/mappers/project-overview.mapper.ts @@ -78,6 +78,7 @@ export class ProjectOverviewMapper { rootFolder: response.relationships?.files?.links?.related?.href, iri: response.links?.iri, }, + rootParentId: response.relationships?.root?.data?.id, } as ProjectOverview; } } diff --git a/src/app/features/project/overview/models/project-overview.models.ts b/src/app/features/project/overview/models/project-overview.models.ts index be943dcc3..3a4bda79b 100644 --- a/src/app/features/project/overview/models/project-overview.models.ts +++ b/src/app/features/project/overview/models/project-overview.models.ts @@ -63,6 +63,7 @@ export interface ProjectOverview { rootFolder: string; iri: string; }; + rootParentId?: string; } export interface ProjectOverviewSubject { @@ -206,6 +207,12 @@ export interface ProjectOverviewGetResponseJsonApi { }; }; }; + root?: { + data: { + id: string; + type: string; + }; + }; }; links: { iri: string; diff --git a/src/app/features/project/overview/project-overview.component.ts b/src/app/features/project/overview/project-overview.component.ts index 804aa7f4d..9da3c1b86 100644 --- a/src/app/features/project/overview/project-overview.component.ts +++ b/src/app/features/project/overview/project-overview.component.ts @@ -224,6 +224,14 @@ export class ProjectOverviewComponent extends DataciteTrackerComponent implement super(); this.setupCollectionsEffects(); this.setupCleanup(); + + effect(() => { + const currentProject = this.currentProject(); + if (currentProject) { + const rootParentId = currentProject.rootParentId ?? currentProject.id; + this.actions.getComponentsTree(rootParentId, currentProject.id, ResourceType.Project); + } + }); } getDoi(): Observable { @@ -244,7 +252,6 @@ export class ProjectOverviewComponent extends DataciteTrackerComponent implement this.actions.getBookmarksId(); this.actions.getHomeWiki(ResourceType.Project, projectId); this.actions.getComponents(projectId); - this.actions.getComponentsTree(projectId, ResourceType.Project); this.actions.getLinkedProjects(projectId); this.actions.getActivityLogs(projectId, this.activityDefaultPage.toString(), this.activityPageSize.toString()); this.setupDataciteViewTrackerEffect().subscribe(); diff --git a/src/app/shared/mappers/nodes/base-node.mapper.ts b/src/app/shared/mappers/nodes/base-node.mapper.ts index 25f4f3f67..bcfc0697f 100644 --- a/src/app/shared/mappers/nodes/base-node.mapper.ts +++ b/src/app/shared/mappers/nodes/base-node.mapper.ts @@ -5,8 +5,8 @@ export class BaseNodeMapper { return data.map((item) => this.getNodeData(item)); } - static getNodesWithChildren(data: BaseNodeDataJsonApi[]): NodeShortInfoModel[] { - return data.map((item) => ({ + static getNodesWithChildren(data: BaseNodeDataJsonApi[], parentId: string): NodeShortInfoModel[] { + return this.getAllDescendants(data, parentId).map((item) => ({ id: item.id, title: item.attributes.title, parentId: item.relationships.parent?.data?.id, @@ -36,6 +36,18 @@ export class BaseNodeMapper { currentUserIsContributor: data.attributes.current_user_is_contributor, wikiEnabled: data.attributes.wiki_enabled, customCitation: data.attributes.custom_citation || undefined, + rootParentId: data.relationships.root?.data?.id, }; } + + static getAllDescendants(allNodes: BaseNodeDataJsonApi[], parentId: string): BaseNodeDataJsonApi[] { + const parent = allNodes.find((n) => n.id === parentId); + if (!parent) return []; + + const directChildren = allNodes.filter((node) => node.relationships.parent?.data?.id === parentId); + + const descendants = directChildren.flatMap((child) => this.getAllDescendants(allNodes, child.id)); + + return [parent, ...descendants]; + } } diff --git a/src/app/shared/models/nodes/base-node.model.ts b/src/app/shared/models/nodes/base-node.model.ts index 46c16be78..1fbb4464a 100644 --- a/src/app/shared/models/nodes/base-node.model.ts +++ b/src/app/shared/models/nodes/base-node.model.ts @@ -19,4 +19,5 @@ export interface BaseNodeModel { currentUserPermissions: string[]; currentUserIsContributor: boolean; wikiEnabled: boolean; + rootParentId?: string; } diff --git a/src/app/shared/services/resource.service.ts b/src/app/shared/services/resource.service.ts index ea775f0f6..6df94f72d 100644 --- a/src/app/shared/services/resource.service.ts +++ b/src/app/shared/services/resource.service.ts @@ -66,11 +66,15 @@ export class ResourceGuidService { .pipe(map((response) => BaseNodeMapper.getNodeData(response.data))); } - getResourceWithChildren(resourceId: string, resourceType: ResourceType): Observable { + getResourceWithChildren( + rootParentId: string, + resourceId: string, + resourceType: ResourceType + ): Observable { const resourcePath = this.urlMap.get(resourceType); return this.jsonApiService - .get>(`${this.apiUrl}/${resourcePath}/?filter[root]=${resourceId}`) - .pipe(map((response) => BaseNodeMapper.getNodesWithChildren(response.data.reverse()))); + .get>(`${this.apiUrl}/${resourcePath}/?filter[root]=${rootParentId}`) + .pipe(map((response) => BaseNodeMapper.getNodesWithChildren(response.data, resourceId))); } } diff --git a/src/app/shared/stores/current-resource/current-resource.actions.ts b/src/app/shared/stores/current-resource/current-resource.actions.ts index 3e1c6300b..4f850fa9c 100644 --- a/src/app/shared/stores/current-resource/current-resource.actions.ts +++ b/src/app/shared/stores/current-resource/current-resource.actions.ts @@ -16,6 +16,7 @@ export class GetResourceDetails { export class GetResourceWithChildren { static readonly type = '[Current Resource] Get Resource With Children'; constructor( + public rootParentId: string, public resourceId: string, public resourceType: ResourceType ) {} diff --git a/src/app/shared/stores/current-resource/current-resource.state.ts b/src/app/shared/stores/current-resource/current-resource.state.ts index b635d9b86..29a5f41a9 100644 --- a/src/app/shared/stores/current-resource/current-resource.state.ts +++ b/src/app/shared/stores/current-resource/current-resource.state.ts @@ -90,17 +90,19 @@ export class CurrentResourceState { }, }); - return this.resourceService.getResourceWithChildren(action.resourceId, action.resourceType).pipe( - tap((children) => { - ctx.patchState({ - resourceChildren: { - data: children, - isLoading: false, - error: null, - }, - }); - }), - catchError((error) => handleSectionError(ctx, 'resourceChildren', error)) - ); + return this.resourceService + .getResourceWithChildren(action.rootParentId, action.resourceId, action.resourceType) + .pipe( + tap((children) => { + ctx.patchState({ + resourceChildren: { + data: children, + isLoading: false, + error: null, + }, + }); + }), + catchError((error) => handleSectionError(ctx, 'resourceChildren', error)) + ); } }