From 516a9d1909cfa1ed26bb57cd081577af6738377a Mon Sep 17 00:00:00 2001 From: Andrey Nenashev Date: Tue, 20 Feb 2024 14:22:40 +0100 Subject: [PATCH] Introduce list of relathionships between Data Entities (#1630) Co-authored-by: vburlachenko --- .../oddplatform/dto/RelationshipDto.java | 5 + .../mapper/RelationshipDetailsMapper.java | 8 +- .../mapper/RelationshipMapper.java | 11 +- ...eDataEntityRelationshipRepositoryImpl.java | 28 ++++- .../ReactiveRelationshipsRepositoryImpl.java | 40 ++++++- odd-platform-specification/components.yaml | 10 +- odd-platform-ui/package.json | 6 +- odd-platform-ui/pnpm-lock.yaml | 104 ++++++++++++------ .../Alerts/AlertsTabs/AlertsTabs.tsx | 5 +- .../DataEntityDetailsTabs.tsx | 10 +- .../TestReportDetails/TestReportDetails.tsx | 5 +- .../DataModelling/DataModellingRoutes.tsx | 2 + .../DataModelling/DataModellingTabs.tsx | 23 ++-- ...QuertExampleDetailsLinkedEntitiesItem.tsx} | 0 .../QueryExampleDetailsLinkedEntities.tsx | 2 +- .../DataModelling/Relationships.tsx | 84 ++++++++++++++ .../Relationships/RelationshipsListItem.tsx | 98 +++++++++++++++++ .../RelationshipsSearchInput.tsx | 24 ++++ .../Relationships/RelationshipsSkeleton.tsx | 48 ++++++++ .../Relationships/RelationshipsTabs.tsx | 56 ++++++++++ .../Relationships/RelationshipsTitle.tsx | 26 +++++ .../IntegrationTabs/IntegrationTabs.tsx | 11 +- .../ManagementTabs/ManagementTabs.tsx | 6 +- .../OwnerAssociationsTabs.tsx | 3 +- .../TermDetailsTabs/TermDetailsTabs.tsx | 4 +- .../shared/elements/AppTabs/AppTabs.tsx | 4 +- .../elements/AppTabs/useSetSelectedTab.ts | 11 +- .../AppToolbar/ToolbarTabs/ToolbarTabs.tsx | 2 - .../shared/styled-components/container.ts | 13 +++ .../shared/styled-components/index.ts | 5 + .../styled-components/scrollable-container.ts | 10 ++ .../shared/styled-components/section.ts | 15 +++ odd-platform-ui/src/lib/api.ts | 2 + odd-platform-ui/src/lib/constants.ts | 12 ++ .../hooks/api/dataModelling/relatioships.ts | 27 +++++ .../src/routes/dataModelling/dataModelling.ts | 7 ++ .../src/routes/dataModelling/index.ts | 3 + .../queryExamplesRoutes.ts} | 22 ++-- .../dataModelling/relationshipsRoutes.ts | 6 + odd-platform-ui/src/routes/index.ts | 2 +- 40 files changed, 641 insertions(+), 119 deletions(-) rename odd-platform-ui/src/components/DataModelling/QueryExampleDetails/{QuertExampleDettailsLinkedEntitiesItem.tsx => QuertExampleDetailsLinkedEntitiesItem.tsx} (100%) create mode 100644 odd-platform-ui/src/components/DataModelling/Relationships.tsx create mode 100644 odd-platform-ui/src/components/DataModelling/Relationships/RelationshipsListItem.tsx create mode 100644 odd-platform-ui/src/components/DataModelling/Relationships/RelationshipsSearchInput.tsx create mode 100644 odd-platform-ui/src/components/DataModelling/Relationships/RelationshipsSkeleton.tsx create mode 100644 odd-platform-ui/src/components/DataModelling/Relationships/RelationshipsTabs.tsx create mode 100644 odd-platform-ui/src/components/DataModelling/Relationships/RelationshipsTitle.tsx create mode 100644 odd-platform-ui/src/components/shared/styled-components/container.ts create mode 100644 odd-platform-ui/src/components/shared/styled-components/index.ts create mode 100644 odd-platform-ui/src/components/shared/styled-components/scrollable-container.ts create mode 100644 odd-platform-ui/src/components/shared/styled-components/section.ts create mode 100644 odd-platform-ui/src/lib/hooks/api/dataModelling/relatioships.ts create mode 100644 odd-platform-ui/src/routes/dataModelling/dataModelling.ts create mode 100644 odd-platform-ui/src/routes/dataModelling/index.ts rename odd-platform-ui/src/routes/{dataModellingRoutes.ts => dataModelling/queryExamplesRoutes.ts} (61%) create mode 100644 odd-platform-ui/src/routes/dataModelling/relationshipsRoutes.ts diff --git a/odd-platform-api/src/main/java/org/opendatadiscovery/oddplatform/dto/RelationshipDto.java b/odd-platform-api/src/main/java/org/opendatadiscovery/oddplatform/dto/RelationshipDto.java index a170cb515..35c61a277 100644 --- a/odd-platform-api/src/main/java/org/opendatadiscovery/oddplatform/dto/RelationshipDto.java +++ b/odd-platform-api/src/main/java/org/opendatadiscovery/oddplatform/dto/RelationshipDto.java @@ -2,11 +2,16 @@ import lombok.Builder; import org.opendatadiscovery.oddplatform.model.tables.pojos.DataEntityPojo; +import org.opendatadiscovery.oddplatform.model.tables.pojos.DataSourcePojo; +import org.opendatadiscovery.oddplatform.model.tables.pojos.NamespacePojo; import org.opendatadiscovery.oddplatform.model.tables.pojos.RelationshipsPojo; @Builder public record RelationshipDto(DataEntityPojo dataEntityRelationship, RelationshipsPojo relationshipPojo, + DataSourcePojo dataSourcePojo, + NamespacePojo relationshipNamespacePojo, + NamespacePojo dataSourceNamespacePojo, DataEntityPojo sourceDataEntity, DataEntityPojo targetDataEntity) { } diff --git a/odd-platform-api/src/main/java/org/opendatadiscovery/oddplatform/mapper/RelationshipDetailsMapper.java b/odd-platform-api/src/main/java/org/opendatadiscovery/oddplatform/mapper/RelationshipDetailsMapper.java index 6057b3507..0ac7daf27 100644 --- a/odd-platform-api/src/main/java/org/opendatadiscovery/oddplatform/mapper/RelationshipDetailsMapper.java +++ b/odd-platform-api/src/main/java/org/opendatadiscovery/oddplatform/mapper/RelationshipDetailsMapper.java @@ -29,8 +29,8 @@ public DataEntityERDRelationshipDetails mapToDataEntityERDRelationshipDetails( relationshipDetailsDto.targetDataEntity() != null ? relationshipDetailsDto.targetDataEntity().getId() : null) .type(RelationshipTypeDto.ERD.name().equals(relationshipDetailsDto.relationshipPojo().getRelationshipType()) - ? DataEntityRelationshipType.ERD - : DataEntityRelationshipType.GRAPH) + ? DataEntityRelationshipType.ENTITY_RELATIONSHIP + : DataEntityRelationshipType.GRAPH_RELATIONSHIP) .erdRelationship( erdRelationshipMapper.mapPojoToDetails(relationshipDetailsDto.erdRelationshipDetailsPojo())); } @@ -50,8 +50,8 @@ public DataEntityGraphRelationshipDetails mapToDataEntityGraphRelationshipDetail relationshipDetailsDto.targetDataEntity() != null ? relationshipDetailsDto.targetDataEntity().getId() : null) .type(RelationshipTypeDto.ERD.name().equals(relationshipDetailsDto.relationshipPojo().getRelationshipType()) - ? DataEntityRelationshipType.ERD - : DataEntityRelationshipType.GRAPH) + ? DataEntityRelationshipType.ENTITY_RELATIONSHIP + : DataEntityRelationshipType.GRAPH_RELATIONSHIP) .graphRelationship( graphRelationshipMapper.mapPojoToDetails(relationshipDetailsDto.graphRelationshipPojo())); } diff --git a/odd-platform-api/src/main/java/org/opendatadiscovery/oddplatform/mapper/RelationshipMapper.java b/odd-platform-api/src/main/java/org/opendatadiscovery/oddplatform/mapper/RelationshipMapper.java index 6a3d14bf5..4d7c31ee7 100644 --- a/odd-platform-api/src/main/java/org/opendatadiscovery/oddplatform/mapper/RelationshipMapper.java +++ b/odd-platform-api/src/main/java/org/opendatadiscovery/oddplatform/mapper/RelationshipMapper.java @@ -7,6 +7,7 @@ import org.opendatadiscovery.oddplatform.api.contract.model.DataEntityRelationshipList; import org.opendatadiscovery.oddplatform.api.contract.model.DataEntityRelationshipType; import org.opendatadiscovery.oddplatform.api.contract.model.PageInfo; +import org.opendatadiscovery.oddplatform.dto.DataSourceDto; import org.opendatadiscovery.oddplatform.dto.RelationshipDto; import org.opendatadiscovery.oddplatform.dto.RelationshipTypeDto; import org.opendatadiscovery.oddplatform.utils.Page; @@ -15,6 +16,9 @@ @RequiredArgsConstructor @Component public class RelationshipMapper { + private final DataSourceSafeMapper dataSourceSafeMapper; + private final NamespaceMapper namespaceMapper; + public DataEntityRelationshipList mapListToRelationshipList(final List relationshipDtos) { return new DataEntityRelationshipList() .items(mapToRelationshipList(relationshipDtos)) @@ -36,9 +40,12 @@ public DataEntityRelationship mapToDatasetRelationship(final RelationshipDto ite .targetDatasetOddrn(item.relationshipPojo().getTargetDatasetOddrn()) .sourceDataEntityId(item.sourceDataEntity() != null ? item.sourceDataEntity().getId() : null) .targetDataEntityId(item.targetDataEntity() != null ? item.targetDataEntity().getId() : null) + .dataSource(dataSourceSafeMapper.mapDto(new DataSourceDto(item.dataSourcePojo(), + item.dataSourceNamespacePojo(), null))) + .namespace(namespaceMapper.mapPojo(item.relationshipNamespacePojo())) .type(RelationshipTypeDto.ERD.name().equals(item.relationshipPojo().getRelationshipType()) - ? DataEntityRelationshipType.ERD - : DataEntityRelationshipType.GRAPH); + ? DataEntityRelationshipType.ENTITY_RELATIONSHIP + : DataEntityRelationshipType.GRAPH_RELATIONSHIP); } public DataEntityRelationshipList mapListToRelationshipPage(final Page relationshipDtoPage) { diff --git a/odd-platform-api/src/main/java/org/opendatadiscovery/oddplatform/repository/reactive/ReactiveDataEntityRelationshipRepositoryImpl.java b/odd-platform-api/src/main/java/org/opendatadiscovery/oddplatform/repository/reactive/ReactiveDataEntityRelationshipRepositoryImpl.java index 54c68fe3d..2e07300d5 100644 --- a/odd-platform-api/src/main/java/org/opendatadiscovery/oddplatform/repository/reactive/ReactiveDataEntityRelationshipRepositoryImpl.java +++ b/odd-platform-api/src/main/java/org/opendatadiscovery/oddplatform/repository/reactive/ReactiveDataEntityRelationshipRepositoryImpl.java @@ -18,8 +18,12 @@ import org.opendatadiscovery.oddplatform.api.contract.model.RelationshipsType; import org.opendatadiscovery.oddplatform.dto.RelationshipDto; import org.opendatadiscovery.oddplatform.model.tables.pojos.DataEntityPojo; +import org.opendatadiscovery.oddplatform.model.tables.pojos.DataSourcePojo; +import org.opendatadiscovery.oddplatform.model.tables.pojos.NamespacePojo; import org.opendatadiscovery.oddplatform.model.tables.pojos.RelationshipsPojo; import org.opendatadiscovery.oddplatform.model.tables.records.DataEntityRecord; +import org.opendatadiscovery.oddplatform.model.tables.records.DataSourceRecord; +import org.opendatadiscovery.oddplatform.model.tables.records.NamespaceRecord; import org.opendatadiscovery.oddplatform.model.tables.records.RelationshipsRecord; import org.opendatadiscovery.oddplatform.repository.util.JooqQueryHelper; import org.opendatadiscovery.oddplatform.repository.util.JooqReactiveOperations; @@ -30,6 +34,8 @@ import static org.opendatadiscovery.oddplatform.dto.DataEntityClassDto.DATA_RELATIONSHIP; import static org.opendatadiscovery.oddplatform.model.Tables.DATA_ENTITY; +import static org.opendatadiscovery.oddplatform.model.Tables.DATA_SOURCE; +import static org.opendatadiscovery.oddplatform.model.Tables.NAMESPACE; import static org.opendatadiscovery.oddplatform.model.Tables.RELATIONSHIPS; @Slf4j @@ -40,6 +46,9 @@ public class ReactiveDataEntityRelationshipRepositoryImpl private static final String RELATIONSHIPS_CTE = "relationships_cte"; private static final String SOURCE_DATA_ENTITY = "source_data_entity"; private static final String TARGET_DATA_ENTITY = "target_data_entity"; + private static final String RELATIONSHIP_NAMESPACE = "relationship_namespace"; + private static final String DATA_SOURCE_NAMESPACE = "data_source_namespace"; + private static final String DATA_SOURCE_CTE = "data_source_cte"; private final JooqReactiveOperations jooqReactiveOperations; private final JooqQueryHelper jooqQueryHelper; @@ -50,6 +59,9 @@ public Mono> getRelationships(final Integer page, final In final Table relationships = RELATIONSHIPS.asTable(RELATIONSHIPS_CTE); final Table srcDataEntity = DATA_ENTITY.asTable(SOURCE_DATA_ENTITY); final Table trgtDataEntity = DATA_ENTITY.asTable(TARGET_DATA_ENTITY); + final Table dataSource = DATA_SOURCE.asTable(DATA_SOURCE_CTE); + final Table relationshipNamespace = NAMESPACE.asTable(RELATIONSHIP_NAMESPACE); + final Table dataSourceNamespace = NAMESPACE.asTable(DATA_SOURCE_NAMESPACE); final List conditionList = new ArrayList<>(); @@ -71,14 +83,16 @@ public Mono> getRelationships(final Integer page, final In final List> groupByFields = Stream.of(relationshipsDataEntityCTE.fields(), srcDataEntity.fields(), - trgtDataEntity.fields(), relationships.fields()) + trgtDataEntity.fields(), relationships.fields(), dataSource.fields(), + relationshipNamespace.fields(), dataSourceNamespace.fields()) .flatMap(Arrays::stream) .toList(); final ResultQuery resultQuery = DSL.with(relationshipsDataEntityCTE.getName()) .as(relationshipSelect) .select(relationshipsDataEntityCTE.fields()) - .select(relationships.asterisk(), srcDataEntity.asterisk(), trgtDataEntity.asterisk()) + .select(relationships.asterisk(), srcDataEntity.asterisk(), trgtDataEntity.asterisk(), + dataSource.asterisk(), relationshipNamespace.asterisk(), dataSourceNamespace.asterisk()) .from(relationshipsDataEntityCTE.getName()) .join(relationships) .on(relationshipsDataEntityCTE.field(DATA_ENTITY.ID).eq(relationships.field(RELATIONSHIPS.DATA_ENTITY_ID)) @@ -89,6 +103,13 @@ public Mono> getRelationships(final Integer page, final In .on(relationships.field(RELATIONSHIPS.SOURCE_DATASET_ODDRN).eq(srcDataEntity.field(DATA_ENTITY.ODDRN))) .leftJoin(trgtDataEntity) .on(relationships.field(RELATIONSHIPS.TARGET_DATASET_ODDRN).eq(trgtDataEntity.field(DATA_ENTITY.ODDRN))) + .leftJoin(dataSource) + .on(relationshipsDataEntityCTE.field(DATA_ENTITY.DATA_SOURCE_ID).eq(dataSource.field(DATA_SOURCE.ID))) + .leftJoin(dataSourceNamespace) + .on(dataSource.field(DATA_SOURCE.NAMESPACE_ID).eq(dataSourceNamespace.field(NAMESPACE.ID))) + .leftJoin(relationshipNamespace) + .on(relationshipsDataEntityCTE.field(DATA_ENTITY.NAMESPACE_ID) + .eq(relationshipNamespace.field(NAMESPACE.ID))) .groupBy(groupByFields); return jooqReactiveOperations.flux(resultQuery) @@ -100,6 +121,9 @@ public Mono> getRelationships(final Integer page, final In .dataEntityRelationship(r.into(relationshipsDataEntityCTE).into(DataEntityPojo.class)) .sourceDataEntity(r.into(srcDataEntity).into(DataEntityPojo.class)) .targetDataEntity(r.into(trgtDataEntity).into(DataEntityPojo.class)) + .dataSourcePojo(r.into(dataSource).into(DataSourcePojo.class)) + .dataSourceNamespacePojo(r.into(dataSourceNamespace).into(NamespacePojo.class)) + .relationshipNamespacePojo(r.into(relationshipNamespace).into(NamespacePojo.class)) .build(), jooqReactiveOperations .mono(DSL.selectCount().from(DATA_ENTITY).where(conditionList)) diff --git a/odd-platform-api/src/main/java/org/opendatadiscovery/oddplatform/repository/reactive/ReactiveRelationshipsRepositoryImpl.java b/odd-platform-api/src/main/java/org/opendatadiscovery/oddplatform/repository/reactive/ReactiveRelationshipsRepositoryImpl.java index bcf5e2651..1a27ae27f 100644 --- a/odd-platform-api/src/main/java/org/opendatadiscovery/oddplatform/repository/reactive/ReactiveRelationshipsRepositoryImpl.java +++ b/odd-platform-api/src/main/java/org/opendatadiscovery/oddplatform/repository/reactive/ReactiveRelationshipsRepositoryImpl.java @@ -12,8 +12,12 @@ import org.opendatadiscovery.oddplatform.api.contract.model.RelationshipsType; import org.opendatadiscovery.oddplatform.dto.RelationshipDto; import org.opendatadiscovery.oddplatform.model.tables.pojos.DataEntityPojo; +import org.opendatadiscovery.oddplatform.model.tables.pojos.DataSourcePojo; +import org.opendatadiscovery.oddplatform.model.tables.pojos.NamespacePojo; import org.opendatadiscovery.oddplatform.model.tables.pojos.RelationshipsPojo; import org.opendatadiscovery.oddplatform.model.tables.records.DataEntityRecord; +import org.opendatadiscovery.oddplatform.model.tables.records.DataSourceRecord; +import org.opendatadiscovery.oddplatform.model.tables.records.NamespaceRecord; import org.opendatadiscovery.oddplatform.model.tables.records.RelationshipsRecord; import org.opendatadiscovery.oddplatform.repository.util.JooqQueryHelper; import org.opendatadiscovery.oddplatform.repository.util.JooqReactiveOperations; @@ -23,6 +27,8 @@ import reactor.core.publisher.Mono; import static org.opendatadiscovery.oddplatform.model.Tables.DATA_ENTITY; +import static org.opendatadiscovery.oddplatform.model.Tables.DATA_SOURCE; +import static org.opendatadiscovery.oddplatform.model.Tables.NAMESPACE; import static org.opendatadiscovery.oddplatform.model.Tables.RELATIONSHIPS; @Slf4j @@ -34,6 +40,9 @@ public class ReactiveRelationshipsRepositoryImpl private static final String RELATIONSHIPS_DATA_ENTITY = "relationships_data_entity"; private static final String SOURCE_DATA_ENTITY = "source_data_entity"; private static final String TARGET_DATA_ENTITY = "target_data_entity"; + private static final String RELATIONSHIP_NAMESPACE = "relationship_namespace"; + private static final String DATA_SOURCE_NAMESPACE = "data_source_namespace"; + private static final String DATA_SOURCE_CTE = "data_source_cte"; public ReactiveRelationshipsRepositoryImpl(final JooqReactiveOperations jooqReactiveOperations, final JooqQueryHelper jooqQueryHelper, @@ -61,10 +70,17 @@ public Flux getRelationsByDatasetIdAndType(final Long dataEntit final Table relationshipsDataEntity = DATA_ENTITY.asTable(RELATIONSHIPS_DATA_ENTITY); final Table srcDataEntity = DATA_ENTITY.asTable(SOURCE_DATA_ENTITY); final Table trgtDataEntity = DATA_ENTITY.asTable(TARGET_DATA_ENTITY); + final Table dataSource = DATA_SOURCE.asTable(DATA_SOURCE_CTE); + final Table relationshipNamespace = NAMESPACE.asTable(RELATIONSHIP_NAMESPACE); + final Table dataSourceNamespace = NAMESPACE.asTable(DATA_SOURCE_NAMESPACE); + final SelectOnConditionStep query = DSL.select(RELATIONSHIPS.asterisk(), relationshipsDataEntity.asterisk(), srcDataEntity.asterisk(), - trgtDataEntity.asterisk()) + trgtDataEntity.asterisk(), + dataSource.asterisk(), + relationshipNamespace.asterisk(), + dataSourceNamespace.asterisk()) .from(RELATIONSHIPS) .leftJoin(srcDataEntity) .on(RELATIONSHIPS.SOURCE_DATASET_ODDRN.eq(srcDataEntity.field(DATA_ENTITY.ODDRN))) @@ -75,7 +91,13 @@ public Flux getRelationsByDatasetIdAndType(final Long dataEntit .or(dataEntityCTE.field(DATA_ENTITY.ODDRN).eq(RELATIONSHIPS.TARGET_DATASET_ODDRN))) ) .join(relationshipsDataEntity) - .on(RELATIONSHIPS.DATA_ENTITY_ID.eq(relationshipsDataEntity.field(DATA_ENTITY.ID))); + .on(RELATIONSHIPS.DATA_ENTITY_ID.eq(relationshipsDataEntity.field(DATA_ENTITY.ID))) + .leftJoin(dataSource) + .on(relationshipsDataEntity.field(DATA_ENTITY.DATA_SOURCE_ID).eq(dataSource.field(DATA_SOURCE.ID))) + .leftJoin(relationshipNamespace) + .on(relationshipsDataEntity.field(DATA_ENTITY.NAMESPACE_ID).eq(relationshipNamespace.field(NAMESPACE.ID))) + .leftJoin(dataSourceNamespace) + .on(dataSource.field(DATA_SOURCE.NAMESPACE_ID).eq(dataSourceNamespace.field(NAMESPACE.ID))); ResultQuery finalQuery = query; @@ -87,19 +109,29 @@ public Flux getRelationsByDatasetIdAndType(final Long dataEntit .map(r -> mapToDto(r.into(RELATIONSHIPS).into(RelationshipsPojo.class), r.into(relationshipsDataEntity).into(DataEntityPojo.class), r.into(srcDataEntity).into(DataEntityPojo.class), - r.into(trgtDataEntity).into(DataEntityPojo.class)) + r.into(trgtDataEntity).into(DataEntityPojo.class), + r.into(dataSource).into(DataSourcePojo.class), + r.into(dataSourceNamespace).into(NamespacePojo.class), + r.into(relationshipNamespace).into(NamespacePojo.class) + ) ); } private RelationshipDto mapToDto(final RelationshipsPojo pojo, final DataEntityPojo relationshipsDataEntity, final DataEntityPojo srcDataEntity, - final DataEntityPojo trgtDataEntity) { + final DataEntityPojo trgtDataEntity, + final DataSourcePojo dataSourcePojo, + final NamespacePojo dataSourceNamespace, + final NamespacePojo relationshipNamespace) { return RelationshipDto.builder() .relationshipPojo(pojo) .dataEntityRelationship(relationshipsDataEntity) .sourceDataEntity(srcDataEntity) .targetDataEntity(trgtDataEntity) + .dataSourcePojo(dataSourcePojo) + .dataSourceNamespacePojo(dataSourceNamespace) + .relationshipNamespacePojo(relationshipNamespace) .build(); } } diff --git a/odd-platform-specification/components.yaml b/odd-platform-specification/components.yaml index ce9d10d35..1afac10c0 100644 --- a/odd-platform-specification/components.yaml +++ b/odd-platform-specification/components.yaml @@ -3972,13 +3972,19 @@ components: type: string target_dataset_oddrn: type: string + data_source: + $ref: '#/components/schemas/DataSourceSafe' + namespace: + $ref: '#/components/schemas/Namespace' type: $ref: '#/components/schemas/DataEntityRelationshipType' required: + - id - name - oddrn - source_dataset_oddrn - target_dataset_oddrn + - data_source - type DataEntityERDRelationshipDetails: @@ -4004,8 +4010,8 @@ components: DataEntityRelationshipType: type: string enum: - - ERD - - GRAPH + - ENTITY_RELATIONSHIP + - GRAPH_RELATIONSHIP ERDRelationshipDetails: type: object diff --git a/odd-platform-ui/package.json b/odd-platform-ui/package.json index 411bffac4..deef45b88 100644 --- a/odd-platform-ui/package.json +++ b/odd-platform-ui/package.json @@ -46,8 +46,8 @@ "@mui/x-date-pickers": "^5.0.20", "@reduxjs/toolkit": "^1.9.7", "@tanstack/react-query": "^5.17.19", - "@tanstack/react-virtual": "^3.0.1", "@tanstack/react-table": "^8.11.2", + "@tanstack/react-virtual": "^3.0.1", "@uiw/react-md-editor": "^3.25.6", "@visx/curve": "^3.3.0", "@visx/event": "^3.3.0", @@ -80,10 +80,10 @@ "react-infinite-scroll-component": "^6.1.0", "react-multi-date-picker": "^4.4.1", "react-redux": "^8.1.2", - "react-router-dom": "^6.21.2", + "react-router-dom": "^6.22.0", "react-truncate-markup": "^5.1.2", "recharts": "^2.12.0", - "styled-components": "^6.1.1", + "styled-components": "^6.1.8", "use-debounce": "^10.0.0", "uuid": "^9.0.1", "vanilla-jsoneditor": "^0.7.11" diff --git a/odd-platform-ui/pnpm-lock.yaml b/odd-platform-ui/pnpm-lock.yaml index d07405925..50d07862e 100644 --- a/odd-platform-ui/pnpm-lock.yaml +++ b/odd-platform-ui/pnpm-lock.yaml @@ -119,7 +119,7 @@ dependencies: version: 7.48.2(react@18.2.0) react-hot-toast: specifier: ^2.4.1 - version: 2.4.1(csstype@3.1.2)(react-dom@18.2.0)(react@18.2.0) + version: 2.4.1(csstype@3.1.3)(react-dom@18.2.0)(react@18.2.0) react-i18next: specifier: ^13.0.0 version: 13.0.0(i18next@23.2.0)(react-dom@18.2.0)(react@18.2.0) @@ -133,8 +133,8 @@ dependencies: specifier: ^8.1.2 version: 8.1.2(@types/react-dom@18.2.17)(@types/react@18.2.39)(react-dom@18.2.0)(react@18.2.0)(redux@4.2.1) react-router-dom: - specifier: ^6.21.2 - version: 6.21.2(react-dom@18.2.0)(react@18.2.0) + specifier: ^6.22.0 + version: 6.22.0(react-dom@18.2.0)(react@18.2.0) react-truncate-markup: specifier: ^5.1.2 version: 5.1.2(react@18.2.0) @@ -142,8 +142,8 @@ dependencies: specifier: ^2.12.0 version: 2.12.0(react-dom@18.2.0)(react@18.2.0) styled-components: - specifier: ^6.1.1 - version: 6.1.1(react-dom@18.2.0)(react@18.2.0) + specifier: ^6.1.8 + version: 6.1.8(react-dom@18.2.0)(react@18.2.0) use-debounce: specifier: ^10.0.0 version: 10.0.0(react@18.2.0) @@ -169,7 +169,7 @@ devDependencies: version: 14.0.0(react-dom@18.2.0)(react@18.2.0) '@testing-library/user-event': specifier: ^14.4.3 - version: 14.4.3(@testing-library/dom@9.3.1) + version: 14.4.3(@testing-library/dom@9.3.4) '@types/d3': specifier: ^7.4.3 version: 7.4.3 @@ -546,6 +546,13 @@ packages: dependencies: regenerator-runtime: 0.14.0 + /@babel/runtime@7.23.9: + resolution: {integrity: sha512-0CX6F+BI2s9dkUqr08KFrAIZgNFj75rdBU/DjCyYLIaV/quFjkk6T+EJ2LkZHyZTbEV4L5p97mNkUsHl2wLFAw==} + engines: {node: '>=6.9.0'} + dependencies: + regenerator-runtime: 0.14.1 + dev: true + /@babel/template@7.22.15: resolution: {integrity: sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w==} engines: {node: '>=6.9.0'} @@ -735,6 +742,10 @@ packages: '@types/react': 18.2.39 react: 18.2.0 + /@emotion/unitless@0.8.0: + resolution: {integrity: sha512-VINS5vEYAscRl2ZUDiT3uMPlrFQupiKgHz5AA4bCH1miKBg4qtwkim1qPmJj/4WG6TreYMY111rEFsjupcOKHw==} + dev: false + /@emotion/unitless@0.8.1: resolution: {integrity: sha512-KOEGMu6dmJZtpadb476IsZBclKvILjopjUii3V+7MnXIQCYh8W3NgNcgwo21n9LXZX6EDIKvqfjYxXebDwxKmQ==} @@ -1467,8 +1478,8 @@ packages: reselect: 4.1.8 dev: false - /@remix-run/router@1.14.2: - resolution: {integrity: sha512-ACXpdMM9hmKZww21yEqWwiLws/UPLhNKvimN8RrYSqPSvB3ov7sLvAcfvaxePeLvccTQKGdkDIhLYApZVDFuKg==} + /@remix-run/router@1.15.0: + resolution: {integrity: sha512-HOil5aFtme37dVQTB6M34G95kPM3MMuqSmIRVCC52eKV+Y/tGSqw9P3rWhlAx6A+mz+MoX+XxsGsNJbaI5qCgQ==} engines: {node: '>=14.0.0'} dev: false @@ -1639,6 +1650,20 @@ packages: pretty-format: 27.5.1 dev: true + /@testing-library/dom@9.3.4: + resolution: {integrity: sha512-FlS4ZWlp97iiNWig0Muq8p+3rVDjRiYE+YKGbAqXOu9nwJFFOdL00kFpz42M+4huzYi86vAK1sOOfyOG45muIQ==} + engines: {node: '>=14'} + dependencies: + '@babel/code-frame': 7.23.5 + '@babel/runtime': 7.23.9 + '@types/aria-query': 5.0.4 + aria-query: 5.1.3 + chalk: 4.1.2 + dom-accessibility-api: 0.5.16 + lz-string: 1.5.0 + pretty-format: 27.5.1 + dev: true + /@testing-library/jest-dom@5.17.0: resolution: {integrity: sha512-ynmNeT7asXyH3aSVv4vvX4Rb+0qjOhdNHnO/3vuZNqPmhDpV/+rCSGwQ7bLcmU2cJ4dvoheIO85LQj0IbJHEtg==} engines: {node: '>=8', npm: '>=6', yarn: '>=1'} @@ -1668,19 +1693,23 @@ packages: react-dom: 18.2.0(react@18.2.0) dev: true - /@testing-library/user-event@14.4.3(@testing-library/dom@9.3.1): + /@testing-library/user-event@14.4.3(@testing-library/dom@9.3.4): resolution: {integrity: sha512-kCUc5MEwaEMakkO5x7aoD+DLi02ehmEM2QCGWvNqAS1dV/fAvORWEjnjsEIvml59M7Y5kCkWN6fCCyPOe8OL6Q==} engines: {node: '>=12', npm: '>=6'} peerDependencies: '@testing-library/dom': '>=7.21.4' dependencies: - '@testing-library/dom': 9.3.1 + '@testing-library/dom': 9.3.4 dev: true /@types/aria-query@5.0.1: resolution: {integrity: sha512-XTIieEY+gvJ39ChLcB4If5zHtPxt3Syj5rgZR+e1ctpmK8NjPf0zFqsz4JpLJT0xla9GFDKjy8Cpu331nrmE1Q==} dev: true + /@types/aria-query@5.0.4: + resolution: {integrity: sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==} + dev: true + /@types/babel__core@7.20.5: resolution: {integrity: sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==} dependencies: @@ -2071,8 +2100,8 @@ packages: resolution: {integrity: sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw==} dev: true - /@types/stylis@4.2.2: - resolution: {integrity: sha512-Rm17MsTpQQP5Jq4BF7CdrxJsDufoiL/q5IbJZYZmOZAJALyijgF7BzLgobXUqraNcQdqFYLYGeglDp6QzaxPpg==} + /@types/stylis@4.2.0: + resolution: {integrity: sha512-n4sx2bqL0mW1tvDf/loQ+aMX7GQD3lc3fkCMC55VFNDu/vBOabO+LTIeXKM14xK0ppk5TUGcWRjiSpIlUpghKw==} dev: false /@types/testing-library__jest-dom@5.14.9: @@ -3162,6 +3191,10 @@ packages: /csstype@3.1.2: resolution: {integrity: sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ==} + /csstype@3.1.3: + resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==} + dev: false + /d3-array@3.2.1: resolution: {integrity: sha512-gUY/qeHq/yNqqoCKNq4vtpFLdoCdvyNpWoC/KNjhGbhDuQpAM9sIQQKkXSNpXa9h5KySs/gzm7R88WkUutgwWQ==} engines: {node: '>=12'} @@ -4385,12 +4418,12 @@ packages: resolution: {integrity: sha512-uHJgbwAMwNFf5mLst7IWLNg14x1CkeqglJb/K3doi4dw6q2IvAAmM/Y81kevy83wP+Sst+nutFTYOGg3d1lsxg==} dev: true - /goober@2.1.13(csstype@3.1.2): + /goober@2.1.13(csstype@3.1.3): resolution: {integrity: sha512-jFj3BQeleOoy7t93E9rZ2de+ScC4lQICLwiAQmKMg9F6roKGaLSHoCDYKkWlSafg138jejvq/mTdvmnwDQgqoQ==} peerDependencies: csstype: ^3.0.10 dependencies: - csstype: 3.1.2 + csstype: 3.1.3 dev: false /gopd@1.0.1: @@ -6340,14 +6373,14 @@ packages: react: 18.2.0 dev: false - /react-hot-toast@2.4.1(csstype@3.1.2)(react-dom@18.2.0)(react@18.2.0): + /react-hot-toast@2.4.1(csstype@3.1.3)(react-dom@18.2.0)(react@18.2.0): resolution: {integrity: sha512-j8z+cQbWIM5LY37pR6uZR6D4LfseplqnuAO4co4u8917hBUvXlEqyP1ZzqVLcqoyUesZZv/ImreoCeHVDpE5pQ==} engines: {node: '>=10'} peerDependencies: react: '>=16' react-dom: '>=16' dependencies: - goober: 2.1.13(csstype@3.1.2) + goober: 2.1.13(csstype@3.1.3) react: 18.2.0 react-dom: 18.2.0(react@18.2.0) transitivePeerDependencies: @@ -6475,26 +6508,26 @@ packages: engines: {node: '>=0.10.0'} dev: true - /react-router-dom@6.21.2(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-tE13UukgUOh2/sqYr6jPzZTzmzc70aGRP4pAjG2if0IP3aUT+sBtAKUJh0qMh0zylJHGLmzS+XWVaON4UklHeg==} + /react-router-dom@6.22.0(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-z2w+M4tH5wlcLmH3BMMOMdrtrJ9T3oJJNsAlBJbwk+8Syxd5WFJ7J5dxMEW0/GEXD1BBis4uXRrNIz3mORr0ag==} engines: {node: '>=14.0.0'} peerDependencies: react: '>=16.8' react-dom: '>=16.8' dependencies: - '@remix-run/router': 1.14.2 + '@remix-run/router': 1.15.0 react: 18.2.0 react-dom: 18.2.0(react@18.2.0) - react-router: 6.21.2(react@18.2.0) + react-router: 6.22.0(react@18.2.0) dev: false - /react-router@6.21.2(react@18.2.0): - resolution: {integrity: sha512-jJcgiwDsnaHIeC+IN7atO0XiSRCrOsQAHHbChtJxmgqG2IaYQXSnhqGb5vk2CU/wBQA12Zt+TkbuJjIn65gzbA==} + /react-router@6.22.0(react@18.2.0): + resolution: {integrity: sha512-q2yemJeg6gw/YixRlRnVx6IRJWZD6fonnfZhN1JIOhV2iJCPeRNSH3V1ISwHf+JWcESzLC3BOLD1T07tmO5dmg==} engines: {node: '>=14.0.0'} peerDependencies: react: '>=16.8' dependencies: - '@remix-run/router': 1.14.2 + '@remix-run/router': 1.15.0 react: 18.2.0 dev: false @@ -6655,6 +6688,10 @@ packages: /regenerator-runtime@0.14.0: resolution: {integrity: sha512-srw17NI0TUWHuGa5CFGGmhfNIeja30WMBfbslPNhf6JrqQlLN5gcrvig1oqPxiVaXb0oW0XRKtH6Nngs5lKCIA==} + /regenerator-runtime@0.14.1: + resolution: {integrity: sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==} + dev: true + /regexp.prototype.flags@1.5.0: resolution: {integrity: sha512-0SutC3pNudRKgquxGoRGIz946MZVHqbNfPjBdxeOhBrdgDKlRoXmYLQN9xRbrR09ZXWeGAdPuif7egofn6v5LA==} engines: {node: '>= 0.4'} @@ -7249,31 +7286,31 @@ packages: inline-style-parser: 0.1.1 dev: false - /styled-components@6.1.1(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-cpZZP5RrKRIClBW5Eby4JM1wElLVP4NQrJbJ0h10TidTyJf4SIIwa3zLXOoPb4gJi8MsJ8mjq5mu2IrEhZIAcQ==} + /styled-components@6.1.8(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-PQ6Dn+QxlWyEGCKDS71NGsXoVLKfE1c3vApkvDYS5KAK+V8fNWGhbSUEo9Gg2iaID2tjLXegEW3bZDUGpofRWw==} engines: {node: '>= 16'} peerDependencies: react: '>= 16.8.0' react-dom: '>= 16.8.0' dependencies: '@emotion/is-prop-valid': 1.2.1 - '@emotion/unitless': 0.8.1 - '@types/stylis': 4.2.2 + '@emotion/unitless': 0.8.0 + '@types/stylis': 4.2.0 css-to-react-native: 3.2.0 csstype: 3.1.2 postcss: 8.4.31 react: 18.2.0 react-dom: 18.2.0(react@18.2.0) shallowequal: 1.1.0 - stylis: 4.3.0 - tslib: 2.6.2 + stylis: 4.3.1 + tslib: 2.5.0 dev: false /stylis@4.2.0: resolution: {integrity: sha512-Orov6g6BB1sDfYgzWfTHDOxamtX1bE/zo104Dh9e6fqJ3PooipYyfJ0pUmrZO2wAvO8YbEyeFrkV91XTsGMSrw==} - /stylis@4.3.0: - resolution: {integrity: sha512-E87pIogpwUsUwXw7dNyU4QDjdgVMy52m+XEOPEKUn161cCzWjjhPSQhByfd1CcNvrOLnXQ6OnnZDwnJrz/Z4YQ==} + /stylis@4.3.1: + resolution: {integrity: sha512-EQepAV+wMsIaGVGX1RECzgrcqRRU/0sYOHkeLsZ3fzHaHXZy4DaOOX0vOlGQdlsjkh3mFHAIlVimpwAs4dslyQ==} dev: false /supports-color@5.5.0: @@ -7429,8 +7466,13 @@ packages: strip-bom: 3.0.0 dev: true + /tslib@2.5.0: + resolution: {integrity: sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==} + dev: false + /tslib@2.6.2: resolution: {integrity: sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==} + dev: true /type-check@0.4.0: resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} diff --git a/odd-platform-ui/src/components/Alerts/AlertsTabs/AlertsTabs.tsx b/odd-platform-ui/src/components/Alerts/AlertsTabs/AlertsTabs.tsx index 564bf89b7..c565283c3 100644 --- a/odd-platform-ui/src/components/Alerts/AlertsTabs/AlertsTabs.tsx +++ b/odd-platform-ui/src/components/Alerts/AlertsTabs/AlertsTabs.tsx @@ -1,4 +1,4 @@ -import React, { useCallback, useMemo, useState } from 'react'; +import React, { useCallback, useMemo } from 'react'; import { useTranslation } from 'react-i18next'; import { type AppTabItem, AppTabs } from 'components/shared/elements'; import { changeAlertsFilterAction } from 'redux/slices/alerts.slice'; @@ -15,7 +15,6 @@ interface AlertsTabsProps { const AlertsTabs: React.FC = ({ totals, showMyAndDepends }) => { const { t } = useTranslation(); const dispatch = useAppDispatch(); - const [selectedTab, setSelectedTab] = useState(-1); const tabs = useMemo( () => [ @@ -40,7 +39,7 @@ const AlertsTabs: React.FC = ({ totals, showMyAndDepends }) => [totals, showMyAndDepends, t] ); - useSetSelectedTab(tabs, setSelectedTab); + const selectedTab = useSetSelectedTab(tabs); const alertsFilterUpdateAction = useCallback(() => { dispatch(changeAlertsFilterAction()); diff --git a/odd-platform-ui/src/components/DataEntityDetails/DataEntityDetailsTabs/DataEntityDetailsTabs.tsx b/odd-platform-ui/src/components/DataEntityDetails/DataEntityDetailsTabs/DataEntityDetailsTabs.tsx index 2e1498b5e..3a0c1e409 100644 --- a/odd-platform-ui/src/components/DataEntityDetails/DataEntityDetailsTabs/DataEntityDetailsTabs.tsx +++ b/odd-platform-ui/src/components/DataEntityDetails/DataEntityDetailsTabs/DataEntityDetailsTabs.tsx @@ -121,18 +121,12 @@ const DataEntityDetailsTabs: React.FC = () => { ] ); - const [selectedTab, setSelectedTab] = React.useState(-1); - useSetSelectedTab(tabs, setSelectedTab); + const selectedTab = useSetSelectedTab(tabs); return ( <> {tabs.length && selectedTab >= 0 ? ( - {}} - /> + ) : null} ); diff --git a/odd-platform-ui/src/components/DataEntityDetails/TestReport/TestReportDetails/TestReportDetails.tsx b/odd-platform-ui/src/components/DataEntityDetails/TestReport/TestReportDetails/TestReportDetails.tsx index 9b9ac81cb..86114ffdb 100644 --- a/odd-platform-ui/src/components/DataEntityDetails/TestReport/TestReportDetails/TestReportDetails.tsx +++ b/odd-platform-ui/src/components/DataEntityDetails/TestReport/TestReportDetails/TestReportDetails.tsx @@ -50,9 +50,7 @@ const TestReportDetails: React.FC = () => { [dataEntityId, dataQATestId, t] ); - const [selectedTab, setSelectedTab] = React.useState(-1); - - useSetSelectedTab(tabs, setSelectedTab); + const selectedTab = useSetSelectedTab(tabs); return ( @@ -83,7 +81,6 @@ const TestReportDetails: React.FC = () => { type='secondary' items={tabs} selectedTab={selectedTab} - handleTabChange={() => {}} sx={{ width: '100%' }} tabSx={{ flexGrow: 1 }} /> diff --git a/odd-platform-ui/src/components/DataModelling/DataModellingRoutes.tsx b/odd-platform-ui/src/components/DataModelling/DataModellingRoutes.tsx index e54c5401e..492b75f28 100644 --- a/odd-platform-ui/src/components/DataModelling/DataModellingRoutes.tsx +++ b/odd-platform-ui/src/components/DataModelling/DataModellingRoutes.tsx @@ -8,6 +8,7 @@ const QueryExamples = lazy(() => import('./QueryExamples')); const QueryExampleDetails = lazy( () => import('./QueryExampleDetails/QueryExampleDetailsContainer') ); +const Relationships = lazy(() => import('./Relationships')); const DataModellingRoutes: React.FC = () => ( @@ -36,6 +37,7 @@ const DataModellingRoutes: React.FC = () => ( /> } /> + } /> ); diff --git a/odd-platform-ui/src/components/DataModelling/DataModellingTabs.tsx b/odd-platform-ui/src/components/DataModelling/DataModellingTabs.tsx index eaab888f9..1426dcb45 100644 --- a/odd-platform-ui/src/components/DataModelling/DataModellingTabs.tsx +++ b/odd-platform-ui/src/components/DataModelling/DataModellingTabs.tsx @@ -1,12 +1,12 @@ import type { AppTabItem } from 'components/shared/elements'; import { AppTabs } from 'components/shared/elements'; -import React, { useCallback, useMemo, useState } from 'react'; +import React, { useMemo } from 'react'; import { useTranslation } from 'react-i18next'; -import { queryExamplesPath } from 'routes'; +import { queryExamplesPath, relationshipsPath } from 'routes'; +import useSetSelectedTab from 'components/shared/elements/AppTabs/useSetSelectedTab'; const DataModellingTabs = () => { const { t } = useTranslation(); - const [selectedTab, setSelectedTab] = useState(0); const tabs = useMemo( () => [ @@ -14,21 +14,18 @@ const DataModellingTabs = () => { name: t('Query Examples'), link: queryExamplesPath(), }, + { + name: t('Relationships'), + link: relationshipsPath(), + }, ], [t] ); - const handleTabChange = useCallback(() => { - setSelectedTab(prev => (prev === 0 ? 1 : 0)); - }, []); + + const selectedTab = useSetSelectedTab(tabs); return ( - + ); }; diff --git a/odd-platform-ui/src/components/DataModelling/QueryExampleDetails/QuertExampleDettailsLinkedEntitiesItem.tsx b/odd-platform-ui/src/components/DataModelling/QueryExampleDetails/QuertExampleDetailsLinkedEntitiesItem.tsx similarity index 100% rename from odd-platform-ui/src/components/DataModelling/QueryExampleDetails/QuertExampleDettailsLinkedEntitiesItem.tsx rename to odd-platform-ui/src/components/DataModelling/QueryExampleDetails/QuertExampleDetailsLinkedEntitiesItem.tsx diff --git a/odd-platform-ui/src/components/DataModelling/QueryExampleDetails/QueryExampleDetailsLinkedEntities.tsx b/odd-platform-ui/src/components/DataModelling/QueryExampleDetails/QueryExampleDetailsLinkedEntities.tsx index 8bebf6da8..4a8dc6806 100644 --- a/odd-platform-ui/src/components/DataModelling/QueryExampleDetails/QueryExampleDetailsLinkedEntities.tsx +++ b/odd-platform-ui/src/components/DataModelling/QueryExampleDetails/QueryExampleDetailsLinkedEntities.tsx @@ -3,7 +3,7 @@ import type { DataEntity } from 'generated-sources'; import React from 'react'; import { useTranslation } from 'react-i18next'; import { EmptyContentPlaceholder } from 'components/shared/elements'; -import QueryExampleDetailsLinkedEntitiesItem from './QuertExampleDettailsLinkedEntitiesItem'; +import QueryExampleDetailsLinkedEntitiesItem from './QuertExampleDetailsLinkedEntitiesItem'; interface QueryExampleDetailsLinkedEntitiesProps { entities: DataEntity[]; diff --git a/odd-platform-ui/src/components/DataModelling/Relationships.tsx b/odd-platform-ui/src/components/DataModelling/Relationships.tsx new file mode 100644 index 000000000..3c96cc6b8 --- /dev/null +++ b/odd-platform-ui/src/components/DataModelling/Relationships.tsx @@ -0,0 +1,84 @@ +import React, { useMemo } from 'react'; +import * as S from 'components/shared/styled-components'; +import { Typography } from '@mui/material'; +import { EmptyContentPlaceholder } from 'components/shared/elements'; +import { useSearchParams } from 'react-router-dom'; +import { RelationshipsType } from 'generated-sources'; +import InfiniteScroll from 'react-infinite-scroll-component'; +import * as Table from 'components/shared/elements/StyledComponents/Table'; +import { useSearchRelationships } from '../../lib/hooks/api/dataModelling/relatioships'; +import RelationshipsSkeleton from './Relationships/RelationshipsSkeleton'; +import RelationshipsTabs from './Relationships/RelationshipsTabs'; +import RelationshipsSearchInput from './Relationships/RelationshipsSearchInput'; +import RelationshipsTitle from './Relationships/RelationshipsTitle'; +import RelationshipsListItem from './Relationships/RelationshipsListItem'; + +const Relationships = () => { + const [searchParams] = useSearchParams(); + const query = searchParams.get('q') ?? ''; + const type = (searchParams.get('type') ?? RelationshipsType.ALL) as RelationshipsType; + const { data, isLoading, hasNextPage, fetchNextPage } = useSearchRelationships({ + query, + type, + size: 30, + }); + + const relationships = useMemo( + () => data?.pages.flatMap(page => page.items) ?? [], + [data?.pages] + ); + + const isEmpty = useMemo( + () => relationships.length === 0 && !isLoading, + [relationships.length, isLoading] + ); + + const total = useMemo(() => data?.pages[0].pageInfo.total ?? 0, [data?.pages]); + + return ( + + + + + + + + + + Name + + + Type + + + Namespace, Datasource + + + Source + + + Target + + + + } + scrollThreshold='200px' + scrollableTarget='relationships-list' + > + {relationships.map(item => ( + + ))} + {isLoading && } + {isEmpty && } + + + + + ); +}; + +export default Relationships; diff --git a/odd-platform-ui/src/components/DataModelling/Relationships/RelationshipsListItem.tsx b/odd-platform-ui/src/components/DataModelling/Relationships/RelationshipsListItem.tsx new file mode 100644 index 000000000..cc05d504f --- /dev/null +++ b/odd-platform-ui/src/components/DataModelling/Relationships/RelationshipsListItem.tsx @@ -0,0 +1,98 @@ +import type { + DataEntity, + DataEntityRelationship, + DataSourceSafe, +} from 'generated-sources'; +import * as Table from 'components/shared/elements/StyledComponents/Table'; +import { Typography } from '@mui/material'; +import React from 'react'; +import { Link } from 'react-router-dom'; +import { dataEntityDetailsPath } from 'routes/dataEntitiesRoutes'; +import { DatasourceLogo, EntityTypeItem } from 'components/shared/elements'; +import * as S from 'components/shared/styled-components'; + +interface Props { + item: DataEntityRelationship; +} + +interface DatasetInfoProps { + dataEntityId?: DataEntity['id']; + oddrn: DataEntity['oddrn']; +} + +const DatasetInfo = ({ dataEntityId, oddrn }: DatasetInfoProps) => + dataEntityId ? ( + + + {oddrn.split('/').pop()} + + + ) : ( + {oddrn} + ); + +interface DataSourceInfoProps { + dataSource: DataSourceSafe; +} + +const DataSourceInfo = ({ dataSource }: DataSourceInfoProps) => { + const { namespace, oddrn, name } = dataSource; + return ( + + {namespace?.name ? ( + + {namespace.name} + + ) : ( + not in any namespace + )} + {name ? ( + + + + {name} + + + ) : ( + manually created + )} + + ); +}; + +const RelationshipsListItem = ({ item }: Props) => ( + + + + + {item.name} + + + + + + + + + + + + + + + + +); + +export default RelationshipsListItem; diff --git a/odd-platform-ui/src/components/DataModelling/Relationships/RelationshipsSearchInput.tsx b/odd-platform-ui/src/components/DataModelling/Relationships/RelationshipsSearchInput.tsx new file mode 100644 index 000000000..8a7bf0c12 --- /dev/null +++ b/odd-platform-ui/src/components/DataModelling/Relationships/RelationshipsSearchInput.tsx @@ -0,0 +1,24 @@ +import React from 'react'; +import { useSearchParams } from 'react-router-dom'; +import { SearchInput } from 'components/shared/elements'; + +const RelationshipsSearchInput = () => { + const [searchParams, setSearchParams] = useSearchParams(); + + const handleSearch = (v?: string) => { + const params = new URLSearchParams(searchParams); + params.set('q', v ?? ''); + setSearchParams(params); + }; + + return ( + + ); +}; + +export default RelationshipsSearchInput; diff --git a/odd-platform-ui/src/components/DataModelling/Relationships/RelationshipsSkeleton.tsx b/odd-platform-ui/src/components/DataModelling/Relationships/RelationshipsSkeleton.tsx new file mode 100644 index 000000000..e7a55f52a --- /dev/null +++ b/odd-platform-ui/src/components/DataModelling/Relationships/RelationshipsSkeleton.tsx @@ -0,0 +1,48 @@ +import React from 'react'; +import { Skeleton } from '@mui/material'; +import { mainSkeletonHeight } from 'lib/constants'; +import { SkeletonWrapper } from 'components/shared/elements'; +import type { CSSObject } from 'styled-components'; +import styled, { css } from 'styled-components'; + +const SkeletonItemBox = styled('div')<{ $flex?: CSSObject['flex'] }>( + ({ theme, $flex = '1 0 16%' }) => css` + margin: ${theme.spacing(1, 0, 1.5, 0)}; + padding-left: ${theme.spacing(1)}; + flex: ${$flex}; + ` +); + +const SkeletonContainer = styled('div')( + ({ theme }) => css` + display: flex; + padding: ${theme.spacing(1.5, 0, 1, 0)}; + border-bottom: 1px solid ${theme.palette.divider}; + ` +); + +const RelationshipsSkeleton: React.FC = () => ( + ( + + + + + + + + + + + + + + + + + + )} + /> +); +export default RelationshipsSkeleton; diff --git a/odd-platform-ui/src/components/DataModelling/Relationships/RelationshipsTabs.tsx b/odd-platform-ui/src/components/DataModelling/Relationships/RelationshipsTabs.tsx new file mode 100644 index 000000000..6908924bc --- /dev/null +++ b/odd-platform-ui/src/components/DataModelling/Relationships/RelationshipsTabs.tsx @@ -0,0 +1,56 @@ +import { type AppTabItem, AppTabs } from 'components/shared/elements'; +import { RelationshipsType } from 'generated-sources'; +import React, { useCallback, useMemo } from 'react'; +import { useSearchParams } from 'react-router-dom'; + +const RelationshipsTabs = () => { + const tabs = useMemo[]>( + () => [ + { + name: 'All', + value: RelationshipsType.ALL, + }, + { + name: 'ERD', + value: RelationshipsType.ERD, + }, + { + name: 'Graph', + value: RelationshipsType.GRAPH, + }, + ], + [] + ); + + const [searchParams, setSearchParams] = useSearchParams(); + + const findInitTabIdx = useCallback(() => { + const type = searchParams.get('type') ?? RelationshipsType.ALL; + return tabs.findIndex(tab => tab.value === type); + }, [searchParams, tabs]); + + const initialTabIdx = useMemo(() => findInitTabIdx(), [findInitTabIdx, searchParams]); + + const onTabChange = useCallback( + (newTab: number) => { + const type = tabs[newTab].value; + if (type) { + const params = new URLSearchParams(searchParams); + params.set('type', type); + setSearchParams(params); + } + }, + [searchParams, tabs] + ); + + return ( + + ); +}; + +export default RelationshipsTabs; diff --git a/odd-platform-ui/src/components/DataModelling/Relationships/RelationshipsTitle.tsx b/odd-platform-ui/src/components/DataModelling/Relationships/RelationshipsTitle.tsx new file mode 100644 index 000000000..37ae52609 --- /dev/null +++ b/odd-platform-ui/src/components/DataModelling/Relationships/RelationshipsTitle.tsx @@ -0,0 +1,26 @@ +import { Typography } from '@mui/material'; +import { NumberFormatted } from 'components/shared/elements'; +import React from 'react'; +import styled from 'styled-components'; + +interface Props { + total: number; +} + +// TODO: create shared styled component for basic and flex box +const Box = styled('div')` + display: flex; + justify-content: space-between; + align-items: center; +`; + +const RelationshipsTitle = ({ total = 0 }: Props) => ( + + Relationships + + relationships overall + + +); + +export default RelationshipsTitle; diff --git a/odd-platform-ui/src/components/Management/Integrations/Integration/IntegrationTabs/IntegrationTabs.tsx b/odd-platform-ui/src/components/Management/Integrations/Integration/IntegrationTabs/IntegrationTabs.tsx index 4fe7aa1ea..6d902db36 100644 --- a/odd-platform-ui/src/components/Management/Integrations/Integration/IntegrationTabs/IntegrationTabs.tsx +++ b/odd-platform-ui/src/components/Management/Integrations/Integration/IntegrationTabs/IntegrationTabs.tsx @@ -12,8 +12,6 @@ interface IntegrationTabsProps { } const IntegrationTabs: React.FC = ({ titles, integrationId }) => { - const [selectedTab, setSelectedTab] = React.useState(0); - const tabs = React.useMemo( () => titles.map(title => ({ @@ -23,16 +21,11 @@ const IntegrationTabs: React.FC = ({ titles, integrationId [titles] ); - useSetSelectedTab(tabs, setSelectedTab); + const selectedTab = useSetSelectedTab(tabs); return ( - {}} - /> + ); }; diff --git a/odd-platform-ui/src/components/Management/ManagementTabs/ManagementTabs.tsx b/odd-platform-ui/src/components/Management/ManagementTabs/ManagementTabs.tsx index 5bf2c49f4..70b75cf8b 100644 --- a/odd-platform-ui/src/components/Management/ManagementTabs/ManagementTabs.tsx +++ b/odd-platform-ui/src/components/Management/ManagementTabs/ManagementTabs.tsx @@ -1,4 +1,4 @@ -import React, { type FC, useMemo, useState } from 'react'; +import React, { type FC, useMemo } from 'react'; import { Grid } from '@mui/material'; import { useTranslation } from 'react-i18next'; import { type AppTabItem, AppTabs } from 'components/shared/elements'; @@ -10,7 +10,6 @@ import useSetSelectedTab from 'components/shared/elements/AppTabs/useSetSelected const ManagementTabs: FC = () => { const { t } = useTranslation(); const { hasAccessTo } = usePermissions(); - const [selectedTab, setSelectedTab] = useState(0); const hideAssociations = useMemo( () => !hasAccessTo(Permission.OWNER_ASSOCIATION_MANAGE), @@ -51,7 +50,7 @@ const ManagementTabs: FC = () => { [hideAssociations, t] ); - useSetSelectedTab(tabs, setSelectedTab); + const selectedTab = useSetSelectedTab(tabs); return ( @@ -60,7 +59,6 @@ const ManagementTabs: FC = () => { type='menu' items={tabs} selectedTab={selectedTab} - handleTabChange={() => {}} /> ); diff --git a/odd-platform-ui/src/components/Management/OwnerAssociations/OwnerAssociationsTabs/OwnerAssociationsTabs.tsx b/odd-platform-ui/src/components/Management/OwnerAssociations/OwnerAssociationsTabs/OwnerAssociationsTabs.tsx index 9def36e1d..52ed6591b 100644 --- a/odd-platform-ui/src/components/Management/OwnerAssociations/OwnerAssociationsTabs/OwnerAssociationsTabs.tsx +++ b/odd-platform-ui/src/components/Management/OwnerAssociations/OwnerAssociationsTabs/OwnerAssociationsTabs.tsx @@ -40,8 +40,7 @@ const OwnerAssociationsTabs: React.FC = ({ [t, newRequestsTabHint] ); - const [selectedTab, setSelectedTab] = React.useState(-1); - useSetSelectedTab(tabs, setSelectedTab); + const selectedTab = useSetSelectedTab(tabs); const onTabChange = React.useCallback(() => { setQuery(''); diff --git a/odd-platform-ui/src/components/Terms/TermDetails/TermDetailsTabs/TermDetailsTabs.tsx b/odd-platform-ui/src/components/Terms/TermDetails/TermDetailsTabs/TermDetailsTabs.tsx index 1d80a4fcb..0083067eb 100644 --- a/odd-platform-ui/src/components/Terms/TermDetails/TermDetailsTabs/TermDetailsTabs.tsx +++ b/odd-platform-ui/src/components/Terms/TermDetails/TermDetailsTabs/TermDetailsTabs.tsx @@ -33,9 +33,7 @@ const TermDetailsTabs: React.FC = () => { [termId, termDetails?.entitiesUsingCount, termDetails?.columnsUsingCount, t] ); - const [selectedTab, setSelectedTab] = React.useState(-1); - - useSetSelectedTab(tabs, setSelectedTab); + const selectedTab = useSetSelectedTab(tabs); return ( <> diff --git a/odd-platform-ui/src/components/shared/elements/AppTabs/AppTabs.tsx b/odd-platform-ui/src/components/shared/elements/AppTabs/AppTabs.tsx index 417696fb1..854fc9e56 100644 --- a/odd-platform-ui/src/components/shared/elements/AppTabs/AppTabs.tsx +++ b/odd-platform-ui/src/components/shared/elements/AppTabs/AppTabs.tsx @@ -21,7 +21,7 @@ export type AppTabItem = { interface AppTabsProps extends Pick { items: AppTabItem[]; - handleTabChange: (newTab: number) => void; + handleTabChange?: (newTab: number) => void; selectedTab?: number | boolean; type: TabType; isHintUpdating?: boolean; @@ -31,7 +31,7 @@ interface AppTabsProps const AppTabs: FC = ({ items, - handleTabChange, + handleTabChange = () => {}, selectedTab, type, orientation, diff --git a/odd-platform-ui/src/components/shared/elements/AppTabs/useSetSelectedTab.ts b/odd-platform-ui/src/components/shared/elements/AppTabs/useSetSelectedTab.ts index 1dc1028e4..447f1ccbd 100644 --- a/odd-platform-ui/src/components/shared/elements/AppTabs/useSetSelectedTab.ts +++ b/odd-platform-ui/src/components/shared/elements/AppTabs/useSetSelectedTab.ts @@ -1,13 +1,10 @@ -import type React from 'react'; -import { useCallback, useEffect } from 'react'; +import { useCallback, useEffect, useState } from 'react'; import { useLocation, useResolvedPath } from 'react-router-dom'; import type { AppTabItem } from './AppTabs'; -const useSetSelectedTab = ( - tabs: AppTabItem[], - setSelectedTab: React.Dispatch> -) => { +const useSetSelectedTab = (tabs: AppTabItem[]) => { const { pathname } = useResolvedPath(useLocation()); + const [selectedTab, setSelectedTab] = useState(0); const findTabIndex = useCallback( () => @@ -19,6 +16,8 @@ const useSetSelectedTab = ( ); useEffect(() => setSelectedTab(findTabIndex()), [tabs, pathname]); + + return selectedTab; }; export default useSetSelectedTab; diff --git a/odd-platform-ui/src/components/shared/elements/AppToolbar/ToolbarTabs/ToolbarTabs.tsx b/odd-platform-ui/src/components/shared/elements/AppToolbar/ToolbarTabs/ToolbarTabs.tsx index 8b5aebadd..bb4defff5 100644 --- a/odd-platform-ui/src/components/shared/elements/AppToolbar/ToolbarTabs/ToolbarTabs.tsx +++ b/odd-platform-ui/src/components/shared/elements/AppToolbar/ToolbarTabs/ToolbarTabs.tsx @@ -50,8 +50,6 @@ const ToolbarTabs: FC = () => { { name: t('Data Modelling'), link: queryExamplesPath(), - hint: t('BETA'), - hintType: 'secondary', value: 'data-modelling', }, { diff --git a/odd-platform-ui/src/components/shared/styled-components/container.ts b/odd-platform-ui/src/components/shared/styled-components/container.ts new file mode 100644 index 000000000..0f3b58d19 --- /dev/null +++ b/odd-platform-ui/src/components/shared/styled-components/container.ts @@ -0,0 +1,13 @@ +import styled, { css, type CSSObject } from 'styled-components'; + +const Container = styled('div')<{ + $flexDirection?: CSSObject['flexDirection']; +}>( + ({ theme, $flexDirection = 'row' }) => css` + display: flex; + flex-direction: ${$flexDirection}; + gap: ${theme.spacing(2)}; + ` +); + +export default Container; diff --git a/odd-platform-ui/src/components/shared/styled-components/index.ts b/odd-platform-ui/src/components/shared/styled-components/index.ts new file mode 100644 index 000000000..756741e9f --- /dev/null +++ b/odd-platform-ui/src/components/shared/styled-components/index.ts @@ -0,0 +1,5 @@ +export { default as Asterisk } from './asterisk'; +export { default as Container } from './container'; +export { default as Section } from './section'; +export { default as ScrollableContainer } from './scrollable-container'; +export * from './layout'; diff --git a/odd-platform-ui/src/components/shared/styled-components/scrollable-container.ts b/odd-platform-ui/src/components/shared/styled-components/scrollable-container.ts new file mode 100644 index 000000000..5eef977c1 --- /dev/null +++ b/odd-platform-ui/src/components/shared/styled-components/scrollable-container.ts @@ -0,0 +1,10 @@ +import styled from 'styled-components'; +import { toolbarHeight } from 'lib/constants'; + +export default styled('div')<{ $offsetY?: number }>(({ $offsetY = 130 }) => ({ + display: 'flex', + flexDirection: 'column', + flexWrap: 'nowrap', + height: `calc(100vh - ${toolbarHeight}px - ${$offsetY}px)`, + overflow: 'auto', +})); diff --git a/odd-platform-ui/src/components/shared/styled-components/section.ts b/odd-platform-ui/src/components/shared/styled-components/section.ts new file mode 100644 index 000000000..3b52fc751 --- /dev/null +++ b/odd-platform-ui/src/components/shared/styled-components/section.ts @@ -0,0 +1,15 @@ +import styled, { css, type CSSObject } from 'styled-components'; + +const Section = styled('section')<{ + $flexDirection?: CSSObject['flexDirection']; + $gap?: CSSObject['gap']; +}>( + ({ $flexDirection = 'row', $gap = '0.5rem' }) => css` + display: flex; + width: 100%; + flex-direction: ${$flexDirection}; + gap: ${$gap}; + ` +); + +export default Section; diff --git a/odd-platform-ui/src/lib/api.ts b/odd-platform-ui/src/lib/api.ts index 1d4e02655..a5ff40e45 100644 --- a/odd-platform-ui/src/lib/api.ts +++ b/odd-platform-ui/src/lib/api.ts @@ -32,6 +32,7 @@ import { DataQualityRunsApi, QueryExampleApi, ReferenceDataApi, + RelationshipApi, } from 'generated-sources'; const HEADERS: ConfigurationParameters = { @@ -78,3 +79,4 @@ export const directoryApi = new DirectoryApi(apiConf); export const dataQualityRunsApi = new DataQualityRunsApi(apiConf); export const queryExampleApi = new QueryExampleApi(apiConf); export const referenceDataApi = new ReferenceDataApi(apiConf); +export const relationshipApi = new RelationshipApi(apiConf); diff --git a/odd-platform-ui/src/lib/constants.ts b/odd-platform-ui/src/lib/constants.ts index 0a60c2dac..3cc83136e 100644 --- a/odd-platform-ui/src/lib/constants.ts +++ b/odd-platform-ui/src/lib/constants.ts @@ -73,6 +73,10 @@ export const DataEntityClassLabelMap: Map< ClassNameEnum.TRANSFORMER_RUN, { short: 'TSR', normal: 'Transformer Run', plural: 'Transformer Runs' }, ], + [ + ClassNameEnum.RELATIONSHIP, + { short: 'REL', normal: 'Relationship', plural: 'Relationships' }, + ], ]); export const DataEntityClassTypeLabelMap: Map< @@ -111,6 +115,14 @@ export const DataEntityClassTypeLabelMap: Map< [TypeNameEnum.API_SERVICE, { normal: 'API service', plural: 'API services' }], [TypeNameEnum.KAFKA_SERVICE, { normal: 'Kafka service', plural: 'Kafka services' }], [TypeNameEnum.DOMAIN, { normal: 'Domain', plural: 'Domains' }], + [ + TypeNameEnum.ENTITY_RELATIONSHIP, + { normal: 'Entity-Relationships', plural: 'Entity-Relationships' }, + ], + [ + TypeNameEnum.GRAPH_RELATIONSHIP, + { normal: 'Graph Relationship', plural: 'Graph Relationships' }, + ], ]); // content width constants diff --git a/odd-platform-ui/src/lib/hooks/api/dataModelling/relatioships.ts b/odd-platform-ui/src/lib/hooks/api/dataModelling/relatioships.ts new file mode 100644 index 000000000..5ede84de7 --- /dev/null +++ b/odd-platform-ui/src/lib/hooks/api/dataModelling/relatioships.ts @@ -0,0 +1,27 @@ +import { useInfiniteQuery } from '@tanstack/react-query'; +import { relationshipApi } from 'lib/api'; +import type { RelationshipApiGetRelationshipsRequest } from 'generated-sources'; +import { addNextPage } from '../utils'; + +export function useSearchRelationships({ + query, + size, + type, +}: Omit) { + return useInfiniteQuery({ + queryKey: ['searchRelationships', query, size, type], + queryFn: async ({ pageParam }) => { + const response = await relationshipApi.getRelationships({ + query, + size, + type, + page: pageParam, + }); + + return addNextPage(response, pageParam, size); + }, + + initialPageParam: 1, + getNextPageParam: lastPage => lastPage.pageInfo.nextPage, + }); +} diff --git a/odd-platform-ui/src/routes/dataModelling/dataModelling.ts b/odd-platform-ui/src/routes/dataModelling/dataModelling.ts new file mode 100644 index 000000000..17df811ad --- /dev/null +++ b/odd-platform-ui/src/routes/dataModelling/dataModelling.ts @@ -0,0 +1,7 @@ +import { generatePath } from 'react-router-dom'; + +export const BASE_PATH = '/data-modelling'; + +export function dataModellingPath() { + return generatePath(BASE_PATH); +} diff --git a/odd-platform-ui/src/routes/dataModelling/index.ts b/odd-platform-ui/src/routes/dataModelling/index.ts new file mode 100644 index 000000000..2821dbb47 --- /dev/null +++ b/odd-platform-ui/src/routes/dataModelling/index.ts @@ -0,0 +1,3 @@ +export * from './queryExamplesRoutes'; +export * from './relationshipsRoutes'; +export * from './dataModelling'; diff --git a/odd-platform-ui/src/routes/dataModellingRoutes.ts b/odd-platform-ui/src/routes/dataModelling/queryExamplesRoutes.ts similarity index 61% rename from odd-platform-ui/src/routes/dataModellingRoutes.ts rename to odd-platform-ui/src/routes/dataModelling/queryExamplesRoutes.ts index c56d9f9e4..0356578bc 100644 --- a/odd-platform-ui/src/routes/dataModellingRoutes.ts +++ b/odd-platform-ui/src/routes/dataModelling/queryExamplesRoutes.ts @@ -1,18 +1,11 @@ import { generatePath, useParams } from 'react-router-dom'; import type { QueryExample } from 'generated-sources'; +import { BASE_PATH } from './dataModelling'; export const URLSearchParams = { QUERY_SEARCH_ID: 'querySearchId', } as const; -const BASE_PATH = '/data-modelling'; -const QUERY_EXAMPLES_PATH = 'query-examples'; - -export function dataModellingPath() { - return BASE_PATH; -} - -const QUERY_EXAMPLE_ID_PARAM = ':queryExampleId'; const QUERY_EXAMPLE_ID = 'queryExampleId'; interface QueryExamplesRouteParams { @@ -34,9 +27,12 @@ export const useQueryExamplesRouteParams = (): AppQueryExamplesRouteParams => { }; export function queryExamplesPath(queryExampleId?: QueryExample['id']) { - let originalPath = `${BASE_PATH}/${QUERY_EXAMPLES_PATH}`; - if (!queryExampleId) return generatePath(originalPath); - - originalPath = `${BASE_PATH}/${QUERY_EXAMPLES_PATH}/${QUERY_EXAMPLE_ID_PARAM}`; - return generatePath(originalPath, { queryExampleId }); + const path = `${BASE_PATH}/query-examples`; + if (queryExampleId) { + return generatePath(`${path}/:queryExampleId`, { + queryExampleId: queryExampleId.toString(), + }); + } + + return generatePath(path); } diff --git a/odd-platform-ui/src/routes/dataModelling/relationshipsRoutes.ts b/odd-platform-ui/src/routes/dataModelling/relationshipsRoutes.ts new file mode 100644 index 000000000..6f08fc5b8 --- /dev/null +++ b/odd-platform-ui/src/routes/dataModelling/relationshipsRoutes.ts @@ -0,0 +1,6 @@ +import { generatePath } from 'react-router-dom'; +import { BASE_PATH } from './dataModelling'; + +export function relationshipsPath() { + return generatePath(`${BASE_PATH}/relationships`); +} diff --git a/odd-platform-ui/src/routes/index.ts b/odd-platform-ui/src/routes/index.ts index ef15af2ab..6878c0ca4 100644 --- a/odd-platform-ui/src/routes/index.ts +++ b/odd-platform-ui/src/routes/index.ts @@ -1,6 +1,5 @@ export * from './alertsRoutes'; export * from './dataEntitiesRoutes'; -export * from './dataModellingRoutes'; export * from './dataQualityRoutes'; export * from './managementRoutes'; export * from './directoryRoutes'; @@ -8,3 +7,4 @@ export * from './searchRoutes'; export * from './activityRoutes'; export * from './termsRoutes'; export * from './masterDataRoutes'; +export * from './dataModelling';