Skip to content

Commit

Permalink
feat(app): Implement hasEverEnteredErrorRecovery (#15876)
Browse files Browse the repository at this point in the history
Closes EXEC-636 and EXEC-637 and RQA-2900

Because ER always requires the users to go through drop tip flows, it's redundant and mildly annoying for users to see a second set of now irrelevant CTAs. Now, we only show the CTAs at the end of the run if the run did not enter error recovery (and the conditions for detecting potential tip attachment occur).
  • Loading branch information
mjhuff authored Aug 2, 2024
1 parent 9e6eb12 commit 446cb2e
Show file tree
Hide file tree
Showing 2 changed files with 31 additions and 13 deletions.
16 changes: 10 additions & 6 deletions app/src/organisms/Devices/ProtocolRun/ProtocolRunHeader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,6 @@ export function ProtocolRunHeader({
const { startedAt, stoppedAt, completedAt } = useRunTimestamps(runId)
const [showRunFailedModal, setShowRunFailedModal] = React.useState(false)
const [showDropTipBanner, setShowDropTipBanner] = React.useState(true)
const [enteredER, setEnteredER] = React.useState(false)
const isResetRunLoadingRef = React.useRef(false)
const { data: runRecord } = useNotifyRunQuery(runId, { staleTime: Infinity })
const highestPriorityError =
Expand Down Expand Up @@ -224,6 +223,8 @@ export function ProtocolRunHeader({
isMostRecentRunCurrent: mostRecentRunId === runId,
})

const enteredER = runRecord?.data.hasEverEnteredErrorRecovery

React.useEffect(() => {
if (isFlex) {
if (runStatus === RUN_STATUS_IDLE) {
Expand All @@ -232,7 +233,8 @@ export function ProtocolRunHeader({
} else if (
runStatus != null &&
// @ts-expect-error runStatus expected to possibly not be terminal
RUN_STATUSES_TERMINAL.includes(runStatus)
RUN_STATUSES_TERMINAL.includes(runStatus) &&
enteredER === false
) {
void determineTipStatus()
}
Expand All @@ -245,9 +247,14 @@ export function ProtocolRunHeader({
}
}, [protocolData, isRobotViewable, navigate])

React.useEffect(() => {
if (isRunCurrent && typeof enteredER === 'boolean') {
reportRecoveredRunResult(runStatus, enteredER)
}
}, [isRunCurrent, enteredER])

// Side effects dependent on the current run state.
React.useEffect(() => {
reportRecoveredRunResult(runStatus, enteredER)
// After a user-initiated stopped run, close the run current run automatically.
if (runStatus === RUN_STATUS_STOPPED && isRunCurrent && runId != null) {
trackProtocolRunEvent({
Expand All @@ -258,9 +265,6 @@ export function ProtocolRunHeader({
})
closeCurrentRun()
}
if (runStatus === RUN_STATUS_AWAITING_RECOVERY) {
setEnteredER(true)
}
}, [runStatus, isRunCurrent, runId, closeCurrentRun])

const startedAtTimestamp =
Expand Down
28 changes: 21 additions & 7 deletions app/src/pages/RunSummary/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ import { formatTimeWithUtcLabel, useNotifyRunQuery } from '../../resources/runs'
import { handleTipsAttachedModal } from '../../organisms/DropTipWizardFlows/TipsAttachedModal'
import { useMostRecentRunId } from '../../organisms/ProtocolUpload/hooks/useMostRecentRunId'
import { useTipAttachmentStatus } from '../../organisms/DropTipWizardFlows'
import { useRecoveryAnalytics } from '../../organisms/ErrorRecoveryFlows/hooks'

import type { OnDeviceRouteParams } from '../../App/types'
import type { PipetteWithTip } from '../../organisms/DropTipWizardFlows'
Expand Down Expand Up @@ -109,18 +110,30 @@ export function RunSummary(): JSX.Element {
)
const localRobot = useSelector(getLocalRobot)
const robotName = localRobot?.name ?? 'no name'
const { trackProtocolRunEvent } = useTrackProtocolRunEvent(runId, robotName)

const onCloneRunSuccess = (): void => {
if (isQuickTransfer) {
deleteRun(runId)
}
}

const { trackProtocolRunEvent } = useTrackProtocolRunEvent(
runId,
robotName as string
)
const robotAnalyticsData = useRobotAnalyticsData(robotName as string)
const { reportRecoveredRunResult } = useRecoveryAnalytics()

const enteredER = runRecord?.data.hasEverEnteredErrorRecovery
React.useEffect(() => {
if (isRunCurrent && typeof enteredER === 'boolean') {
reportRecoveredRunResult(runStatus, enteredER)
}
}, [isRunCurrent, enteredER])

const { reset, isResetRunLoading } = useRunControls(runId, onCloneRunSuccess)
const trackEvent = useTrackEvent()
const { closeCurrentRun, isClosingCurrentRun } = useCloseCurrentRun()
const robotAnalyticsData = useRobotAnalyticsData(robotName)
const [showRunFailedModal, setShowRunFailedModal] = React.useState<boolean>(
false
)
Expand Down Expand Up @@ -151,10 +164,12 @@ export function RunSummary(): JSX.Element {
isFlex: true,
})

// Determine tip status on initial render only.
// Determine tip status on initial render only. Error Recovery always handles tip status, so don't show it twice.
React.useEffect(() => {
determineTipStatus()
}, [])
if (isRunCurrent && enteredER === false) {
void determineTipStatus()
}
}, [isRunCurrent, enteredER])

// TODO(jh, 08-02-24): Revisit useCurrentRunRoute and top level redirects.
const queryClient = useQueryClient()
Expand All @@ -164,7 +179,6 @@ export function RunSummary(): JSX.Element {
queryClient.setQueryData([host, 'runs', runId, 'details'], () => undefined)
navigate('/')
}
// TODO(jh, 07-24-24): After EXEC-504, add reportRecoveredRunResult here.

const returnToQuickTransfer = (): void => {
if (!isRunCurrent) {
Expand Down Expand Up @@ -215,7 +229,7 @@ export function RunSummary(): JSX.Element {
}

const handleRunAgain = (pipettesWithTip: PipetteWithTip[]): void => {
if (isRunCurrent && pipettesWithTip.length > 0) {
if (mostRecentRunId === runId && pipettesWithTip.length > 0) {
void handleTipsAttachedModal({
setTipStatusResolved: setTipStatusResolvedAndRoute(handleRunAgain),
host,
Expand Down

0 comments on commit 446cb2e

Please sign in to comment.