diff --git a/waltz-ng/client/data-flow/components/svelte/flow-detail-tab/FlowDetailPanel.svelte b/waltz-ng/client/data-flow/components/svelte/flow-detail-tab/FlowDetailPanel.svelte index 7d9270426e..c8eb9be772 100644 --- a/waltz-ng/client/data-flow/components/svelte/flow-detail-tab/FlowDetailPanel.svelte +++ b/waltz-ng/client/data-flow/components/svelte/flow-detail-tab/FlowDetailPanel.svelte @@ -8,13 +8,21 @@ import LogicalFlowTable from "./LogicalFlowTable.svelte"; import {filters, resetFlowDetailsStore, selectedLogicalFlow, selectedPhysicalFlow} from "./flow-details-store"; import PhysicalFlowTable from "./PhysicalFlowTable.svelte"; - import {mkAssessmentFilters, mkFlowDetails, mkLogicalFromFlowDetails} from "./flow-detail-utils"; + import { + Directions, + FilterKinds, + mkAssessmentFilters, + mkFlowDetails, + mkLogicalFromFlowDetails + } from "./flow-detail-utils"; import SelectedFlowDetail from "./SelectedFlowDetail.svelte"; import AssessmentFilters from "./AssessmentFilters.svelte"; import DataTypeFilters from "./DataTypeFilters.svelte"; import InboundOutboundFilters from "./InboundOutboundFilters.svelte"; import {onMount} from "svelte"; import PhysicalFlowAttributeFilters from "./PhysicalFlowAttributeFilters.svelte"; + import Icon from "../../../../common/svelte/Icon.svelte"; + import DataExtractLink from "../../../../common/svelte/DataExtractLink.svelte"; export let parentEntityRef; @@ -74,6 +82,8 @@ $: filteredFlows = filterFlows(allFlows, $filters); + $: physicalFlows = _.filter(filteredFlows, d => !_.isEmpty(d.physicalFlow)); + $: logicalFlows = _ .chain(filteredFlows) .map(d => mkLogicalFromFlowDetails(d)) @@ -102,37 +112,78 @@
- Flow Direction + Flow Direction + {#if _.some($filters, d => d.kind === FilterKinds.DIRECTION) && _.find($filters, d => d.kind === FilterKinds.DIRECTION).direction !== Directions.ALL} + + + + {/if}
- Data Types + Data Types + {#if _.some($filters, d => d.kind === FilterKinds.DATA_TYPE)} + + + + {/if}
- Assessments + Assessments + {#if _.some($filters, d => d.kind === FilterKinds.ASSESSMENT)} + + + + {/if}
- Physical Flow + Physical Flow + {#if _.some($filters, d => d.kind === FilterKinds.PHYSICAL_FLOW_ATTRIBUTE)} + + + + {/if} - +

- + + +
+ + + | + + +
{#if $selectedLogicalFlow || $selectedPhysicalFlow} @@ -160,7 +211,7 @@ } .filter-set { - background-color: white; + background-color: #fafafa; } \ No newline at end of file diff --git a/waltz-ng/client/data-flow/components/svelte/flow-detail-tab/LogicalFlowTable.svelte b/waltz-ng/client/data-flow/components/svelte/flow-detail-tab/LogicalFlowTable.svelte index d834045dcb..bd7cafea4c 100644 --- a/waltz-ng/client/data-flow/components/svelte/flow-detail-tab/LogicalFlowTable.svelte +++ b/waltz-ng/client/data-flow/components/svelte/flow-detail-tab/LogicalFlowTable.svelte @@ -8,6 +8,7 @@ import {truncate} from "../../../../common/string-utils"; import Tooltip from "../../../../common/svelte/Tooltip.svelte"; import DataTypeTooltipContent from "./DataTypeTooltipContent.svelte"; + import NoData from "../../../../common/svelte/NoData.svelte"; export let logicalFlows = []; export let assessments; @@ -116,6 +117,12 @@ {/each} + {:else} + + + There are no logical flows to show, these may have been filtered. + + {/each} @@ -127,10 +134,19 @@ table { display: table; white-space: nowrap; + position: relative; + border-collapse: separate; + } + + th { + position: sticky; + top: 0; + background: white; } .table-container { overflow-x: auto; + padding-top: 0; } .rating-col { diff --git a/waltz-ng/client/data-flow/components/svelte/flow-detail-tab/PhysicalFlowAttributeFilters.svelte b/waltz-ng/client/data-flow/components/svelte/flow-detail-tab/PhysicalFlowAttributeFilters.svelte index e9786ec6f6..7f0751ff6c 100644 --- a/waltz-ng/client/data-flow/components/svelte/flow-detail-tab/PhysicalFlowAttributeFilters.svelte +++ b/waltz-ng/client/data-flow/components/svelte/flow-detail-tab/PhysicalFlowAttributeFilters.svelte @@ -12,15 +12,24 @@ import _ from "lodash"; import {filters, updateFilters} from "./flow-details-store"; import {enumValueStore} from "../../../../svelte-stores/enum-value-store"; + import NoData from "../../../../common/svelte/NoData.svelte"; export let flows = []; export let criticalities = []; let enumsCall = enumValueStore.load(); - $: usedFrequencies = _.uniq(_.map(flows, d => d.physicalFlow.frequency)); - $: usedCriticalities = _.uniq(_.map(flows, d => d.physicalFlow.criticality)); - $: usedTransport = _.uniq(_.map(flows, d => d.physicalFlow.transport)); + function mapOverPhysicals(flows, attr) { + return _.chain(flows) + .map(d => _.get(d, ["physicalFlow", attr])) + .uniq() + .compact() + .value(); + } + + $: usedFrequencies = mapOverPhysicals(flows, "frequency"); + $: usedCriticalities = mapOverPhysicals(flows, "criticality"); + $: usedTransport = mapOverPhysicals(flows, "transport"); $: criticalities = _ .chain($enumsCall.data) @@ -125,6 +134,11 @@ return _.includes(filteredTransportKinds, transportKind); } + $: hasCriticalityFilter = _.some($filters, d => d.id === mkCriticalityFilterId()); + $: hasFrequencyFilter = _.some($filters, d => d.id === mkFrequencyFilterId()); + $: hasTransportKindFilter = _.some($filters, d => d.id === mkTransportKindFilterId()); + +
Criticality + {#if hasCriticalityFilter} + {/if} @@ -153,6 +169,12 @@ Use the physical flow attributes to filter the flows. Both logical and physical {criticality.name} + {:else} + + + There are no physical flow criticalities to filter over. + + {/each} @@ -163,10 +185,12 @@ Use the physical flow attributes to filter the flows. Both logical and physical Frequency + {#if hasFrequencyFilter} + {/if} @@ -179,6 +203,12 @@ Use the physical flow attributes to filter the flows. Both logical and physical {frequency.name} + {:else} + + + There are no physical flow frequencies to filter over. + + {/each} @@ -189,10 +219,12 @@ Use the physical flow attributes to filter the flows. Both logical and physical Transport Kind + {#if hasTransportKindFilter} + {/if} @@ -205,6 +237,12 @@ Use the physical flow attributes to filter the flows. Both logical and physical {transportKind.name} + {:else} + + + There are no physical flow transport kinds to filter over. + + {/each} diff --git a/waltz-ng/client/data-flow/components/svelte/flow-detail-tab/PhysicalFlowTable.svelte b/waltz-ng/client/data-flow/components/svelte/flow-detail-tab/PhysicalFlowTable.svelte index 10b0a3973e..cccd32cb5a 100644 --- a/waltz-ng/client/data-flow/components/svelte/flow-detail-tab/PhysicalFlowTable.svelte +++ b/waltz-ng/client/data-flow/components/svelte/flow-detail-tab/PhysicalFlowTable.svelte @@ -12,6 +12,7 @@ } from "../../../../physical-flows/svelte/physical-flow-registration-utils"; import {selectedPhysicalFlow, selectedLogicalFlow} from "./flow-details-store"; import {mkLogicalFromFlowDetails} from "./flow-detail-utils"; + import NoData from "../../../../common/svelte/NoData.svelte"; export let physicalFlows; @@ -109,6 +110,12 @@ {toTransportKindName(nestedEnums, flow.physicalFlow.transport)} + {:else} + + + There are no physical flows to show, these may have been filtered. + + {/each} @@ -120,10 +127,19 @@ table { display: table; white-space: nowrap; + position: relative; + border-collapse: separate; + } + + th { + position: sticky; + top: 0; + background: white; } .table-container { overflow-x: auto; + padding-top: 0; } diff --git a/waltz-ng/client/data-flow/components/svelte/flow-detail-tab/SelectedFlowDetail.svelte b/waltz-ng/client/data-flow/components/svelte/flow-detail-tab/SelectedFlowDetail.svelte index 08b24e2f0f..81d44302f2 100644 --- a/waltz-ng/client/data-flow/components/svelte/flow-detail-tab/SelectedFlowDetail.svelte +++ b/waltz-ng/client/data-flow/components/svelte/flow-detail-tab/SelectedFlowDetail.svelte @@ -61,6 +61,24 @@ }; } + function goToLogicalFlowPage(flow) { + $pageInfo = { + state: "main.logical-flow.view", + params: { + id: flow.logicalFlow.id + } + } + } + + function goToPhysicalFlowPage(flow) { + $pageInfo = { + state: "main.physical-flow.view", + params: { + id: flow.physicalFlow.id + } + } + } + @@ -68,7 +86,7 @@
-
{/if} - {#if hasEditPermission} -
-
    +
    +
      + {#if hasEditPermission}
    • + + + This will open the flow registration page + +
    • + {/if} +
    • + -
    • -
    -
    - {/if} + {#if hasEditPermission} + To remove the flow or edit it's data types + {/if} + + +
+
{/if} {#if $selectedPhysicalFlow} @@ -258,6 +290,22 @@
+
+
+
    +
  • + + + {#if hasEditPermission} + To remove the flow or edit it's attributes + {/if} + +
  • +
+
{/if}
@@ -265,11 +313,11 @@ \ No newline at end of file diff --git a/waltz-ng/client/data-flow/components/svelte/flow-detail-tab/flow-detail-utils.js b/waltz-ng/client/data-flow/components/svelte/flow-detail-tab/flow-detail-utils.js index 6c6140ad23..801cc4c7c2 100644 --- a/waltz-ng/client/data-flow/components/svelte/flow-detail-tab/flow-detail-utils.js +++ b/waltz-ng/client/data-flow/components/svelte/flow-detail-tab/flow-detail-utils.js @@ -2,6 +2,13 @@ import _ from "lodash"; import {sameRef} from "../../../../common/entity-utils"; +export const FilterKinds = { + DIRECTION: "DIRECTION", + DATA_TYPE: "DATA_TYPE", + ASSESSMENT: "ASSESSMENT", + PHYSICAL_FLOW_ATTRIBUTE: "PHYSICAL_FLOW_ATTRIBUTE" +} + export const Directions = { INBOUND: "INBOUND", OUTBOUND: "OUTBOUND", @@ -23,15 +30,24 @@ export function mkFlowDetails(flowView, parentEntityRef) { const decoratorsByFlowId = _.groupBy(flowView.dataTypeDecorators, d => d.dataFlowId); const specsById = _.keyBy(flowView.physicalSpecifications, d => d.id); const logicalFlowsById = _.keyBy(flowView.flows, d => d.id); + const physicalFlowsByLogicalFlowId = _.groupBy(flowView.physicalFlows, d => d.logicalFlowId); return _ - .chain(flowView.physicalFlows) - .map(d => { + .chain(flowView.flows) + .flatMap(d => { + const physicalFlows = _.get(physicalFlowsByLogicalFlowId, d.id, []); + return _.isEmpty(physicalFlows) + ? [{logicalFlow: d, physicalFlow: null}] + : _.map(physicalFlows, p => ({logicalFlow: d, physicalFlow: p})) + }) + .map(t => { + + const logicalFlow = t.logicalFlow; + const physicalFlow = t.physicalFlow; - const logicalFlow = _.get(logicalFlowsById, d.logicalFlowId); - const assessmentRatingsForLogicalFlow = _.get(ratingsByFlowId, d.logicalFlowId, []); - const dataTypesForLogicalFlow = _.get(decoratorsByFlowId, d.logicalFlowId, []); - const specification = _.get(specsById, d.specificationId); + const assessmentRatingsForLogicalFlow = _.get(ratingsByFlowId, logicalFlow.id, []); + const dataTypesForLogicalFlow = _.get(decoratorsByFlowId, logicalFlow.id, []); + const specification = _.get(specsById, physicalFlow?.specificationId); const assessmentRatings = _.map( assessmentRatingsForLogicalFlow, @@ -54,7 +70,7 @@ export function mkFlowDetails(flowView, parentEntityRef) { logicalFlow, ratingsByDefId, dataTypesForLogicalFlow, - physicalFlow: d, + physicalFlow, specification, assessmentRatings, direction @@ -114,6 +130,7 @@ export function mkDirectionFilterId() { export function mkAssessmentFilter(id, ratings) { return { id, + kind: FilterKinds.ASSESSMENT, ratings, test: (r) => _.isEmpty(ratings) ? x => true @@ -124,36 +141,40 @@ export function mkAssessmentFilter(id, ratings) { export function mkCriticalityFilter(id, criticalities) { return { id, + kind: FilterKinds.PHYSICAL_FLOW_ATTRIBUTE, criticalities, test: (r) => _.isEmpty(criticalities) ? x => true - : _.includes(criticalities, r.physicalFlow.criticality) + : _.includes(criticalities, r.physicalFlow?.criticality) }; } export function mkFrequencyFilter(id, frequencies) { return { id, + kind: FilterKinds.PHYSICAL_FLOW_ATTRIBUTE, frequencies, test: (r) => _.isEmpty(frequencies) ? x => true - : _.includes(frequencies, r.physicalFlow.frequency) + : _.includes(frequencies, r.physicalFlow?.frequency) }; } export function mkTransportKindFilter(id, transportKinds) { return { id, + kind: FilterKinds.PHYSICAL_FLOW_ATTRIBUTE, transportKinds, test: (r) => _.isEmpty(transportKinds) ? x => true - : _.includes(transportKinds, r.physicalFlow.transport) + : _.includes(transportKinds, r.physicalFlow?.transport) }; } export function mkDataTypeFilter(id, dataTypes) { return { id, + kind: FilterKinds.DATA_TYPE, dataTypes, test: (r) => _.isEmpty(dataTypes) ? x => true @@ -164,6 +185,7 @@ export function mkDataTypeFilter(id, dataTypes) { export function mkDirectionFilter(id, direction) { return { id, + kind: FilterKinds.DIRECTION, direction, test: (r) => direction === Directions.ALL ? true diff --git a/waltz-web/src/main/java/org/finos/waltz/web/endpoints/extracts/LogicalFlowViewExtractor.java b/waltz-web/src/main/java/org/finos/waltz/web/endpoints/extracts/LogicalFlowViewExtractor.java index d0c96b89c6..4b4e88bbdc 100644 --- a/waltz-web/src/main/java/org/finos/waltz/web/endpoints/extracts/LogicalFlowViewExtractor.java +++ b/waltz-web/src/main/java/org/finos/waltz/web/endpoints/extracts/LogicalFlowViewExtractor.java @@ -38,6 +38,7 @@ import java.util.ArrayList; import java.util.Collection; import java.util.Collections; +import java.util.Comparator; import java.util.List; import java.util.Map; import java.util.Objects; @@ -45,6 +46,7 @@ import static java.util.stream.Collectors.toList; import static org.finos.waltz.common.StringUtilities.joinUsing; +import static org.finos.waltz.model.utils.IdUtilities.indexById; import static spark.Spark.post; @@ -111,7 +113,7 @@ private List> prepareReportRows(LogicalFlowView viewData) { Map> dataTypesByFlowId = MapUtilities.groupBy(viewData.dataTypeDecorators(), d -> d.dataFlowId()); Map> physicalsByLogicalFlowId = MapUtilities.groupBy(viewData.physicalFlows(), PhysicalFlow::logicalFlowId); Map> ratingsByFlowId = MapUtilities.groupBy(viewData.flowRatings(), d -> d.entityReference().id()); - Map ratingSchemeItemsById = MapUtilities.indexBy(viewData.ratingSchemeItems(), d -> d.id()); + Map ratingSchemeItemsById = indexById(viewData.ratingSchemeItems()); return viewData .flows() @@ -137,9 +139,9 @@ private List> prepareReportRows(LogicalFlowView viewData) { viewData.primaryAssessmentDefinitions() .stream() - .sorted() + .sorted(Comparator.comparing(NameProvider::name)) .forEach(defn -> { - String ratingsStrForDefn = ratingsByDefnId.getOrDefault(defn.id(), Collections.emptySet()) + String ratingsStrForDefn = ratingsByDefnId.getOrDefault(defn.id().get(), Collections.emptySet()) .stream() .map(d -> ratingSchemeItemsById.getOrDefault(d.ratingId(), null)) .filter(Objects::nonNull)