Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
27cf0cd
feat(app): skeleton pages for attach and remove waste chute
rclarke0 Oct 3, 2025
adf092d
add pages and text assets
rclarke0 Oct 6, 2025
55774cb
include pages in test:
rclarke0 Oct 6, 2025
5c37800
chore(app): add tests for waste chute pages
rclarke0 Oct 6, 2025
f16df42
remove unused library
rclarke0 Oct 6, 2025
fe781db
refactor(app): remove waste chute banner from probe page
rclarke0 Oct 7, 2025
69f0b25
fix(app): correct tests and add tools for waste chute removal
rclarke0 Oct 7, 2025
6943173
fix(app): pages appear conditionally
rclarke0 Oct 7, 2025
60267a0
lint fixes
rclarke0 Oct 7, 2025
ca023d5
fix lint js errors
rclarke0 Oct 7, 2025
78063c3
fix(app): detecting all waste chute types
rclarke0 Oct 7, 2025
179e9fd
Fix prettier
rclarke0 Oct 7, 2025
c2b0443
fix(app): Clarify error message and reformat pages"
rclarke0 Oct 8, 2025
45027d9
feat(app): isWasteChuteOnDeck function and further use in pipette flows
rclarke0 Oct 8, 2025
d12334f
fix(app): lint fixes
rclarke0 Oct 8, 2025
52f825d
capitalize
rclarke0 Oct 8, 2025
81e9fcc
fix(app): make app text match test
rclarke0 Oct 8, 2025
aad8043
fix(app): correct selectedPipette bug
rclarke0 Oct 9, 2025
4af673e
fix (app): Fix flows and page prints to be error messages/warnings
rclarke0 Oct 14, 2025
4f012c7
Merge branch 'edge' into EXEC-1100-wastechute-pages
rclarke0 Oct 14, 2025
43edb31
fix(app): prettier
rclarke0 Oct 14, 2025
c6f9630
fix(app): pass attachedInstruments to wizard steps to ensure wasteChu…
rclarke0 Oct 15, 2025
3b413c2
fix(app): check right mount for 96ch
rclarke0 Oct 15, 2025
7bb39ce
refactor(app): change order of waste chute page and localize calibrat…
rclarke0 Oct 17, 2025
dca37e3
fix(app): special casing for button positioning on ODD
rclarke0 Oct 17, 2025
3e0e29e
fix(app): pass deckConfig from coordinating component
rclarke0 Oct 18, 2025
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
9 changes: 7 additions & 2 deletions app/src/assets/localization/en/pipette_wizard_flows.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
"attach_pip": "attach pipette",
"attach_pipette": "attach {{mount}} pipette",
"attach_probe": "attach calibration probe",
"attach_wastechute": "Re-attach waste chute",
"backmost": "backmost",
"before_you_begin": "Before you begin",
"begin_calibration": "Begin calibration",
Expand Down Expand Up @@ -47,6 +48,7 @@
"hold_pipette_carefully": "Hold onto the pipette so it does not fall. Connect the pipette by aligning the two protruding rods on the mounting plate. Ensure a secure attachment by screwing in the four front screws with the provided screwdriver.",
"how_to_reattach": "<block>Push the right pipette mount up to the top of the z-axis. Then tighten the captive screw at the top right of the gantry carriage.</block><block>When reattached, the right mount should no longer freely move up and down.</block>",
"install_probe": "Take the calibration probe from its storage location. Ensure its collar is unlocked. Push the pipette ejector up and press the probe firmly onto the <bold>{{location}}</bold> pipette nozzle. Twist the collar to lock the probe. Test that the probe is secure by gently pulling it back and forth.",
"install_waste_chute": "Attach waste chute to the deck plate adapter",
"loose_detach": "Loosen screws and detach ",
"move_gantry_to_front": "Move gantry to front",
"must_detach_mounting_plate": "You must detach the mounting plate and reattach the z-axis carriage before using other pipettes. We do not recommend exiting this process before completion.",
Expand Down Expand Up @@ -74,6 +76,7 @@
"remove_labware": "<block>To get started, remove labware from the deck and clean up the working area to make attachment and calibration easier. Also gather the needed equipment shown to the right.</block><block>The calibration probe is included with the robot and should be stored on the front pillar of the robot.</block>",
"remove_labware_to_get_started": "<block>To get started, remove labware from the deck and clean up the working area to make calibration easier. Also gather the needed equipment shown to the right.</block><block>The calibration probe is included with the robot and should be stored on the front pillar of the robot.</block>",
"remove_probe": "unlock the calibration probe, remove it from the nozzle, and return it to its storage location.",
"remove_wastechute": "Remove waste chute",
"replace_pipette": "replace {{mount}} pipette",
"return_probe_error": "<bold>Return the calibration probe to its storage location before exiting.</bold> <block>{{error}}</block>",
"single_mount_attached_error": "Single mount pipette is selected when this is the 96 channel flow",
Expand All @@ -84,8 +87,10 @@
"unscrew_and_detach": "Loosen Screws and Detach Mounting Plate",
"unscrew_at_top": "<block>Loosen the captive screw on the top right of the carriage. This releases the right pipette mount, which should then freely move up and down.</block>",
"unscrew_carriage": "unscrew z-axis carriage",
"waste_chute_error": "Remove the waste chute from the deck plate adapter before proceeding.",
"waste_chute_warning": "If the waste chute is installed, remove it from the deck plate adapter before proceeding.",
"waste_chute_error": "Remove waste chute before calibrating.",
"waste_chute_warning": "A collision will occur with the pipette if the waste chute remains on deck.",
"waste_chute_warning_probe": "If the waste chute is installed, remove it from the Deck Plate Adapter before proceeding.",
"waste_chute_attach_warning": "Re-attach waste chute to match current deck configuration.",
"wrong_pip": "wrong instrument installed",
"z_axis_still_attached": "z-axis screw still secure"
}
122 changes: 43 additions & 79 deletions app/src/organisms/PipetteWizardFlows/AttachProbe.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { useState } from 'react'
import { Trans, useTranslation } from 'react-i18next'
import capitalize from 'lodash/capitalize'
import { css } from 'styled-components'

import {
Expand All @@ -12,7 +13,6 @@ import {
SPACING,
TYPOGRAPHY,
} from '@opentrons/components'
import { LEFT, WASTE_CHUTE_CUTOUT } from '@opentrons/shared-data'

import pipetteProbe1 from '/app/assets/videos/pipette-wizard-flows/Pipette_Probing_1.webm'
import pipetteProbe8 from '/app/assets/videos/pipette-wizard-flows/Pipette_Probing_8.webm'
Expand All @@ -22,17 +22,22 @@ import {
SimpleWizardBody,
SimpleWizardInProgressBody,
} from '/app/molecules/SimpleWizardBody'
import { useNotifyDeckConfigurationQuery } from '/app/resources/deck_configuration'

import { BODY_STYLE, FLOWS, SECTIONS } from './constants'
import { ProbeNotAttached } from './ProbeNotAttached'
import { getPipetteAnimations } from './utils'
import {
getPipetteAnimations,
isWasteChuteOnDeck,
startCalibrationOnClick,
} from './utils'

import type { CreateCommand, MotorAxes } from '@opentrons/shared-data'
import type { UseQueryResult } from 'react-query'
import type { DeckConfiguration } from '@opentrons/shared-data'
import type { PipetteWizardStepProps } from './types'

interface AttachProbeProps extends PipetteWizardStepProps {
isExiting: boolean
deckConfig: UseQueryResult<DeckConfiguration>
}

const IN_PROGRESS_STYLE = css`
Expand All @@ -48,86 +53,37 @@ const IN_PROGRESS_STYLE = css`

export const AttachProbe = (props: AttachProbeProps): JSX.Element | null => {
const {
proceed,
attachedPipettes,
chainRunCommands,
mount,
isRobotMoving,
goBack,
proceed,
isExiting,
setShowErrorMessage,
errorMessage,
isOnDevice,
flowType,
deckConfig,
} = props

const handleOnClick = (): void => {
proceed()
}

const { t, i18n } = useTranslation('pipette_wizard_flows')
const pipetteWizardStep = { mount, flowType, section: SECTIONS.ATTACH_PROBE }
const [showUnableToDetect, setShowUnableToDetect] = useState<boolean>(false)

const pipetteId = attachedPipettes[mount]?.serialNumber
if (pipetteId == null) return null
const displayName = attachedPipettes[mount]?.displayName
const is8Channel = attachedPipettes[mount]?.data.channels === 8
const is96Channel = attachedPipettes[mount]?.data.channels === 96
const calSlotNum = 'C2'
const axes: MotorAxes = mount === LEFT ? ['leftZ'] : ['rightZ']
const deckConfig = useNotifyDeckConfigurationQuery().data
const isWasteChuteOnDeck =
deckConfig?.find(fixture => fixture.cutoutId === WASTE_CHUTE_CUTOUT) ??
false

if (pipetteId == null) return null
const handleOnClick = (): void => {
const verifyCommands: CreateCommand[] = [
{
commandType: 'verifyTipPresence',
params: {
pipetteId,
expectedState: 'present',
followSingularSensor: 'primary',
},
},
]
const homeCommands: CreateCommand[] = [
{
commandType: 'home' as const,
params: {
axes,
},
},
{
commandType: 'home' as const,
params: {
skipIfMountPositionOk: mount,
},
},
{
commandType: 'calibration/calibratePipette' as const,
params: {
mount,
},
},
{
commandType: 'calibration/moveToMaintenancePosition' as const,
params: {
mount,
},
},
]
chainRunCommands?.(verifyCommands, false)
.then(() => {
chainRunCommands?.(homeCommands, false)
.then(() => {
proceed()
})
.catch(error => {
setShowErrorMessage(error.message as string)
})
})
.catch((e: Error) => {
setShowUnableToDetect(true)
})
}
const startCalibration = startCalibrationOnClick(
props,
setShowUnableToDetect,
pipetteId
)

const calSlotNum = 'C2'
let src = pipetteProbe1
if (is8Channel) {
src = pipetteProbe8
Expand Down Expand Up @@ -179,7 +135,11 @@ export const AttachProbe = (props: AttachProbeProps): JSX.Element | null => {
else if (showUnableToDetect)
return (
<ProbeNotAttached
handleOnClick={handleOnClick}
handleOnClick={
is96Channel && isWasteChuteOnDeck(deckConfig)
? handleOnClick
: startCalibration
}
setShowUnableToDetect={setShowUnableToDetect}
isOnDevice={isOnDevice ?? false}
/>
Expand Down Expand Up @@ -226,23 +186,27 @@ export const AttachProbe = (props: AttachProbeProps): JSX.Element | null => {
}}
/>
</LegacyStyledText>
{is96Channel && (
{is96Channel && !isWasteChuteOnDeck(deckConfig) && (
<Banner
type={Boolean(isWasteChuteOnDeck) ? 'error' : 'warning'}
size={Boolean(isOnDevice) ? '1.5rem' : '1rem'}
marginTop={
Boolean(isOnDevice) ? SPACING.spacing24 : SPACING.spacing16
}
type="warning"
size={isOnDevice ? '1.5rem' : '1rem'}
marginTop={isOnDevice ? SPACING.spacing24 : SPACING.spacing16}
>
{Boolean(isWasteChuteOnDeck)
? t('waste_chute_error')
: t('waste_chute_warning')}
{t('waste_chute_warning_probe')}
</Banner>
)}
</>
}
proceedButtonText={t('begin_calibration')}
proceed={handleOnClick}
proceedButtonText={
is96Channel && isWasteChuteOnDeck(deckConfig)
? capitalize('shared:continue')
: t('begin_calibration')
}
proceed={
is96Channel && isWasteChuteOnDeck(deckConfig)
? handleOnClick
: startCalibration
}
back={flowType === FLOWS.ATTACH ? undefined : goBack}
/>
)
Expand Down
56 changes: 56 additions & 0 deletions app/src/organisms/PipetteWizardFlows/AttachWasteChute.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import { useTranslation } from 'react-i18next'

import { COLORS, JUSTIFY_FLEX_END, PrimaryButton } from '@opentrons/components'

import { SmallButton } from '/app/atoms/buttons'
import {
SimpleWizardBody,
SimpleWizardInProgressBody,
} from '/app/molecules/SimpleWizardBody'

import type { PipetteWizardStepProps } from './types'

export const AttachWasteChute = (
props: PipetteWizardStepProps
): JSX.Element => {
const { isRobotMoving, errorMessage, proceed, isOnDevice } = props

const { t, i18n } = useTranslation(['pipette_wizard_flows', 'shared'])

const handleOnClick = (): void => {
proceed()
}

if (isRobotMoving) {
return <SimpleWizardInProgressBody description={t('stand_back')} />
}

return errorMessage != null ? (
<SimpleWizardBody
iconColor={COLORS.red50}
header={t('shared:error_encountered')}
isSuccess={false}
subHeader={errorMessage}
/>
) : (
<SimpleWizardBody
justifyContentForOddButton={JUSTIFY_FLEX_END}
header={t('attach_wastechute')}
subHeader={t('waste_chute_attach_warning')}
iconColor={COLORS.yellow50}
isSuccess={false}
>
{isOnDevice ? (
<SmallButton
buttonType="primary"
onClick={handleOnClick}
buttonText={i18n.format(t('shared:continue'), 'capitalize')}
/>
) : (
<PrimaryButton onClick={handleOnClick}>
{t('shared:confirm')}
</PrimaryButton>
)}
</SimpleWizardBody>
)
}
Loading
Loading