Skip to content
Draft
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
54 changes: 47 additions & 7 deletions assets/js/dashboard/extra/exploration.js
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,11 @@ function isSameStep(step, otherStep) {
)
}

function truncateFunnelAtFirstZero(funnel) {
const cutoff = funnel.findIndex((entry) => entry.visitors === 0)
return cutoff === -1 ? funnel : funnel.slice(0, cutoff)
}

function truncateFrozenResultsAtIndex(frozenResults, fromIndex) {
const next = {}
Object.keys(frozenResults).forEach((key) => {
Expand Down Expand Up @@ -577,6 +582,9 @@ export function FunnelExploration() {
const prevDashboardStateRef = useRef(dashboardState)
const preloadFiredRef = useRef(false)
const funnelFromPreloadRef = useRef(false)
// Set to true when steps are trimmed to match a truncated funnel response.
// Prevents the resulting steps change from triggering a redundant funnel re-fetch.
const funnelTruncatedStepsRef = useRef(false)
// Bumped whenever the user actively changes the journey or direction.
// Used to discard stale preload-driven candidate fetches that resolve
// after the user has already navigated away from the preloaded prefix.
Expand Down Expand Up @@ -696,6 +704,7 @@ export function FunnelExploration() {
prevDashboardStateRef.current = dashboardState

let cancelled = false
let pendingFollowUpFetch = false

if (!preloadFiredRef.current) {
preloadFiredRef.current = true
Expand All @@ -706,9 +715,14 @@ export function FunnelExploration() {
if (cancelled) return
if (response && response.funnel && response.funnel.length > 0) {
funnelFromPreloadRef.current = true
const preloadedSteps = response.funnel.map(({ step }) => step)
setSteps(preloadedSteps)
setFunnel(response.funnel)
// When dahshboard state changes for an already set journey,
// we might end with some of the trailing steps having zero visitors.
// In such case we should truncate the journey, allow further candidate
// selection (if present) instead of drawing connections
// to non-selectable nodes.
const truncatedFunnel = truncateFunnelAtFirstZero(response.funnel)
setSteps(truncatedFunnel.map(({ step }) => step))
setFunnel(truncatedFunnel)
setFrozenColumnResults(response.candidates)
} else {
// Nothing to preload, fall back to a plain next-steps fetch
Expand Down Expand Up @@ -749,8 +763,14 @@ export function FunnelExploration() {
const funnelAlreadyLoaded = funnelFromPreloadRef.current
funnelFromPreloadRef.current = false

const funnelTruncatedSteps = funnelTruncatedStepsRef.current
funnelTruncatedStepsRef.current = false

const includeFunnel =
journeyChanged && steps.length > 0 && !funnelAlreadyLoaded
journeyChanged &&
steps.length > 0 &&
!funnelAlreadyLoaded &&
!funnelTruncatedSteps

if (journeyChanged && steps.length === 0) {
setFunnel([])
Expand All @@ -766,19 +786,39 @@ export function FunnelExploration() {
)
.then((response) => {
if (cancelled) return
setActiveColumnResults(response?.next || [])
if (includeFunnel) {
setFunnel(response?.funnel || [])
const truncatedFunnel = truncateFunnelAtFirstZero(
response?.funnel || []
)
setFunnel(truncatedFunnel)
// The funnel response may be shorter than the current steps either
// because truncateFunnelAtFirstZero cut it, or because the new
// dashboard state (e.g. date range switch) resulted with an empty funnel
// for the existing journey. In both cases the steps must be trimmed
// so the UI doesn't show stale selected steps with no funnel
// backing them. Next step suggestions in this response are for the
// step after the original (untrimmed) journey - skip them and let the
// effect re-run via setSteps to fetch correct suggestions for the new
// active column. Keep activeColumnLoading as true so the spinner
// stays visible while it's getting fetched.
if (truncatedFunnel.length < steps.length) {
funnelTruncatedStepsRef.current = true
setSteps((prev) => prev.slice(0, truncatedFunnel.length))
setProvisionalFunnelEntries({})
pendingFollowUpFetch = true
return
}
setProvisionalFunnelEntries({})
}
setActiveColumnResults(response?.next || [])
})
.catch(() => {
if (cancelled) return
setActiveColumnResults([])
if (includeFunnel) setFunnel([])
})
.finally(() => {
if (!cancelled) setActiveColumnLoading(false)
if (!cancelled && !pendingFollowUpFetch) setActiveColumnLoading(false)
})

setConnectorsKey(randomKey)
Expand Down
4 changes: 2 additions & 2 deletions priv/repo/seeds.exs
Original file line number Diff line number Diff line change
Expand Up @@ -45,10 +45,10 @@ long_random_paths =
|> Enum.take(Enum.random(1..20))
|> Enum.join("/")

"/#{path}.html"
"/index/#{path}.html"
end

long_random_paths = ["/", "/register", "/login", "/about"] ++ long_random_paths
long_random_paths = ["/", "/register", "/login", "/about", "/index"] ++ long_random_paths

long_random_urls =
for path <- long_random_paths do
Expand Down
Loading