diff --git a/editor.planx.uk/src/pages/FlowEditor/lib/store/preview.ts b/editor.planx.uk/src/pages/FlowEditor/lib/store/preview.ts index e08d4ab04d..3781ba2348 100644 --- a/editor.planx.uk/src/pages/FlowEditor/lib/store/preview.ts +++ b/editor.planx.uk/src/pages/FlowEditor/lib/store/preview.ts @@ -223,19 +223,27 @@ export const previewStore: StateCreator< if (passportValue.length > 0) { const existingValue = acc.data?.[key] ?? []; - const combined = key === planningConstraintsFn - ? passportValue.concat(existingValue) // Planning constraints uniquely store all-levels of granularity, rather than most granular only - : passportValue - .concat(existingValue) - .reduce( - (acc: string[], curr: string, _i: number, arr: string[]) => { - if (!arr.some((x) => x !== curr && x?.startsWith(curr))) { - acc.push(curr); - } - return acc; - }, - [], - ); + const combined = + key === planningConstraintsFn + ? passportValue.concat(existingValue) // Planning constraints uniquely store all-levels of granularity, rather than most granular only + : passportValue + .concat(existingValue) + .reduce( + ( + acc: string[], + curr: string, + _i: number, + arr: string[], + ) => { + if ( + !arr.some((x) => x !== curr && x?.startsWith(curr)) + ) { + acc.push(curr); + } + return acc; + }, + [], + ); passportData[key] = uniq(combined); } @@ -414,8 +422,7 @@ export const previewStore: StateCreator< })); const hidden = !selections.some( (selection) => - selection.data?.flags && - selection.data.flags.includes(flag?.value) + selection.data?.flags && selection.data.flags.includes(flag?.value), ); return { @@ -521,7 +528,7 @@ export const previewStore: StateCreator< // Only proceed if the user has seen at least one node with this fn before const visitedFns = Object.entries(breadcrumbs).filter( ([nodeId, _breadcrumb]) => - flow[nodeId].data?.fn === data.fn || + flow[nodeId]?.data?.fn === data.fn || // Account for nodes like FindProperty that don't have `data.fn` prop but still set passport vars like `property.region` etc Object.keys(passport?.data || {}).includes(data.fn), ); @@ -530,7 +537,7 @@ export const previewStore: StateCreator< // For each visited node, get the data values of its' options (aka edges or Answer nodes) const visitedOptionVals: string[] = []; visitedFns.forEach(([nodeId, _breadcrumb]) => { - flow[nodeId].edges?.map((edgeId) => { + flow[nodeId]?.edges?.map((edgeId) => { if (flow[edgeId].type === TYPES.Answer && flow[edgeId].data?.val) { visitedOptionVals.push(flow[edgeId].data.val); } @@ -563,11 +570,12 @@ export const previewStore: StateCreator< // Get existing passport value(s) for this node's fn, accounting for Planning Constraints special `_nots` const passportValues = passport.data?.[data.fn]; - const nots: string[] | undefined = passport.data?.["_nots"]?.[planningConstraintsFn]; + const nots: string[] | undefined = + passport.data?.["_nots"]?.[planningConstraintsFn]; const foundPassportValues = Array.isArray(passportValues) && passportValues.length > 0; - + // If we have existing passport value(s) for this fn in an eligible automation format (eg not numbers or plain strings), // then proceed through the matching option(s) or the blank option independent if other vals have been seen before if (foundPassportValues && data.fn !== planningConstraintsFn) { @@ -598,20 +606,22 @@ export const previewStore: StateCreator< }); }); } else { - if (blankOption?.id) { optionsThatCanBeAutoAnswered.push(blankOption.id) }; + if (blankOption?.id) { + optionsThatCanBeAutoAnswered.push(blankOption.id); + } } } if (data.fn === planningConstraintsFn && (foundPassportValues || nots)) { - // Planning constraints queried from an external source are stored via two separate passport vars: + // Planning constraints queried from an external source are stored via two separate passport vars: // - One for intersections aka `planningConstraintsFn` // - Another for not-intersections aka `_nots` const matchingIntersectingConstraints = passportValues?.filter( (passportValue: any) => - sortedOptions.some((option) => passportValue === option.data?.val) + sortedOptions.some((option) => passportValue === option.data?.val), ); - const matchingNots = nots?.filter( - (not) => sortedOptions.some((option => not === option.data?.val)) + const matchingNots = nots?.filter((not) => + sortedOptions.some((option) => not === option.data?.val), ); if (matchingIntersectingConstraints?.length > 0) { @@ -629,12 +639,13 @@ export const previewStore: StateCreator< sortedOptions.forEach((option) => { nots?.forEach((not) => { if (not === option.data?.val) { - if (blankOption?.id) optionsThatCanBeAutoAnswered.push(blankOption.id); + if (blankOption?.id) + optionsThatCanBeAutoAnswered.push(blankOption.id); } }); }); } else { - // If this node is asking about a constraint that we have NOT queried from an external source, + // If this node is asking about a constraint that we have NOT queried from an external source, // Then put it to the user exactly once and automate future instances of it if (blankOption?.id && hasVisitedEveryOption) optionsThatCanBeAutoAnswered.push(blankOption.id); @@ -831,9 +842,9 @@ export const sortBreadcrumbs = ( return editingNodes?.length ? nextBreadcrumbs : sortIdsDepthFirst(flow)(new Set(Object.keys(nextBreadcrumbs))).reduce( - (acc, id) => ({ ...acc, [id]: nextBreadcrumbs[id] }), - {} as Store.Breadcrumbs, - ); + (acc, id) => ({ ...acc, [id]: nextBreadcrumbs[id] }), + {} as Store.Breadcrumbs, + ); }; function handleNodesWithPassport({