Skip to content

Commit

Permalink
feat(app): Display run setup task completion (#15889)
Browse files Browse the repository at this point in the history
In the run page on both app and ODD, you get an indication of completion
when you get all your instruments and modules and deck stuff present and
calibrated. But those are just two of the steps presented equally in the
run setup page, and the rest of the steps don't get anything similar. It
leads people to wonder whether they've set things up properly.

This PR adds similar styling and completion semantics for the other
tasks in the run setup screen to fix this issue. Specifically, LPC gets
a "confirm offsets" button (which will confirm offsets even if you
haven't run LPC - makes it more apparent that that's a separate option)
and labware and liquids get generic confirm buttons.

There's also a couple other visual fixes:
- On desktop, the "back to top" button in run setup is now where figma
thinks it is, outside the run-setup content area. This allows some
refactoring of component props
- On desktop, there was an issue with the react-router upgrade (I think
- it's also in the latest IR alpha) that means that if you had an
ongoing run, you couldn't view anything but run details without getting
instantly navigated back to run details

This implements this figma:
https://www.figma.com/design/Rwdt9R0aERFC55oTLDTlqY/8.0-September-Release-File?node-id=39-35830&t=l6vwJjQsfyVeovfC-4

## To come out of draft
- [x] implement for ODD
- [x] rebase onto release
- [x] "are you sure" modal on desktop
- [x] "are you sure" modal on ODD

## Review requests
- This is some pretty complex UI - do you agree with how I've done this?
- Some of this is pretty ugly, in large part because this is old code
that I'm cleaning up. There's some duplicated logic in the run details
and some pretty ugly typing. What I'd like to do is merge this since it
implements some features nicely and then follow up with a refactor to
get the size of some of these files down and enforce nicer separation
between everything.

## Testing
- [x] Desktop green checks on flex
- [x] Desktop green checks on OT-2 (yes, this has to be different
because the steps can be different here)

Closes RSQ-7
  • Loading branch information
sfoster1 authored Aug 6, 2024
1 parent d3eac90 commit 4c3305a
Show file tree
Hide file tree
Showing 21 changed files with 1,054 additions and 281 deletions.
20 changes: 18 additions & 2 deletions app/src/assets/localization/en/protocol_setup.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
"add_to_slot": "Add to slot {{slotName}}",
"additional_labware": "{{count}} additional labware",
"additional_off_deck_labware": "Additional Off-Deck Labware",
"applied_labware_offsets": "applied labware offsets",
"are_you_sure_you_want_to_proceed": "Are you sure you want to proceed to run?",
"attach_gripper_failure_reason": "Attach the required gripper to continue",
"attach_gripper": "attach gripper",
"attach_module": "Attach module before calibrating",
Expand Down Expand Up @@ -47,6 +49,9 @@
"configured": "configured",
"confirm_heater_shaker_module_modal_description": "Before the run begins, module should have both anchors fully extended for a firm attachment. The thermal adapter should be attached to the module. ",
"confirm_heater_shaker_module_modal_title": "Confirm Heater-Shaker Module is attached",
"confirm_offsets": "Confirm offsets",
"confirm_liquids": "Confirm liquids",
"confirm_placements": "Confirm placements",
"confirm_selection": "Confirm selection",
"confirm_values": "Confirm values",
"connect_all_hardware": "Connect and calibrate all hardware first",
Expand Down Expand Up @@ -101,6 +106,7 @@
"labware_latch": "Labware Latch",
"labware_location": "Labware Location",
"labware_name": "Labware name",
"labware_placement": "labware placement",
"labware_position_check_not_available_analyzing_on_robot": "Labware Position Check is not available while protocol is analyzing on robot",
"labware_position_check_not_available_empty_protocol": "Labware Position Check requires that the protocol loads labware and pipettes",
"labware_position_check_not_available": "Labware Position Check is not available after run has started",
Expand All @@ -118,11 +124,13 @@
"learn_more": "Learn more",
"liquid_information": "Liquid information",
"liquid_name": "Liquid name",
"liquids": "liquids",
"liquid_setup_step_description": "View liquid starting locations and volumes",
"liquid_setup_step_title": "Liquids",
"liquids_not_in_setup": "No liquids used in this protocol",
"liquids_not_in_the_protocol": "no liquids are specified for this protocol.",
"liquids": "Liquids",
"liquids_ready": "Liquids ready",
"liquids_confirmed": "Liquids confirmed",
"list_view": "List View",
"loading_data": "Loading data...",
"loading_labware_offsets": "Loading labware offsets",
Expand All @@ -149,6 +157,7 @@
"module_name": "Module",
"module_not_connected": "Not connected",
"module_setup_step_title": "Deck hardware",
"module_setup_step_ready": "Calibration ready",
"module_slot_location": "Slot {{slotName}}, {{moduleName}}",
"module": "Module",
"modules_connected_plural": "{{count}} modules attached",
Expand Down Expand Up @@ -191,6 +200,7 @@
"offset_data": "Offset Data",
"offsets_applied_plural": "{{count}} offsets applied",
"offsets_applied": "{{count}} offset applied",
"offsets_ready": "Offsets ready",
"on_adapter_in_mod": "on {{adapterName}} in {{moduleName}}",
"on_adapter": "on {{adapterName}}",
"on_deck": "On deck",
Expand All @@ -206,6 +216,8 @@
"pipette_offset_cal_description": "This measures a pipette’s X, Y and Z values in relation to the pipette mount and the deck. Pipette Offset Calibration relies on Deck Calibration and Tip Length Calibration. ",
"pipette_offset_cal": "Pipette Offset Calibration",
"placement": "Placement",
"placements_ready": "Placements ready",
"placements_confirmed": "Placements confirmed",
"plug_in_module_to_configure": "Plug in a {{module}} to add it to the slot",
"plug_in_required_module_plural": "Plug in and power up the required modules to continue",
"plug_in_required_module": "Plug in and power up the required module to continue",
Expand Down Expand Up @@ -246,6 +258,7 @@
"robot_calibration_step_description_pipettes_only": "Review required instruments and calibrations for this protocol.",
"robot_calibration_step_description": "Review required pipettes and tip length calibrations for this protocol.",
"robot_calibration_step_title": "Instruments",
"robot_calibration_step_ready": "Calibration ready",
"run_disabled_calibration_not_complete": "Make sure robot calibration is complete before proceeding to run",
"run_disabled_modules_and_calibration_not_complete": "Make sure robot calibration is complete and all modules are connected before proceeding to run",
"run_disabled_modules_not_connected": "Make sure all modules are connected before proceeding to run",
Expand All @@ -260,6 +273,7 @@
"setup_is_view_only": "Setup is view-only once run has started",
"slot_location": "Slot {{slotName}}",
"slot_number": "Slot Number",
"start_run": "Start run",
"status": "Status",
"step": "STEP {{index}}",
"there_are_no_unconfigured_modules": "No {{module}} is connected. Attach one and place it in {{slot}}.",
Expand All @@ -271,6 +285,7 @@
"total_liquid_volume": "Total volume",
"update_deck_config": "Update deck configuration",
"update_deck": "Update deck",
"update_offsets": "Update offsets",
"updated": "Updated",
"usb_connected_no_port_info": "USB Port Connected",
"usb_drive_notification": "Leave USB drive attached until run starts",
Expand All @@ -286,5 +301,6 @@
"view_setup_instructions": "View setup instructions",
"volume": "Volume",
"what_labware_offset_is": "A Labware Offset is a type of positional adjustment that accounts for small, real-world variances in the overall position of the labware on a robot’s deck. Labware Offset data is unique to a specific combination of labware definition, deck slot, and robot.",
"with_the_chosen_value": "With the chosen values, the following error occurred:"
"with_the_chosen_value": "With the chosen values, the following error occurred:",
"you_havent_confirmed": "You haven't confirmed the {{missingSteps}} yet. Ensure these are correct before proceeding to run the protocol."
}
65 changes: 65 additions & 0 deletions app/src/organisms/Devices/ProtocolRun/ConfirmMissingStepsModal.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import * as React from 'react'
import { useTranslation } from 'react-i18next'
import {
ALIGN_CENTER,
DIRECTION_COLUMN,
DIRECTION_ROW,
Flex,
JUSTIFY_FLEX_END,
PrimaryButton,
SecondaryButton,
SPACING,
LegacyStyledText,
TYPOGRAPHY,
} from '@opentrons/components'
import { LegacyModal } from '../../../molecules/LegacyModal'

interface ConfirmMissingStepsModalProps {
onCloseClick: () => void
onConfirmClick: () => void
missingSteps: string[]
}
export const ConfirmMissingStepsModal = (
props: ConfirmMissingStepsModalProps
): JSX.Element | null => {
const { missingSteps, onCloseClick, onConfirmClick } = props
const { t, i18n } = useTranslation(['protocol_setup', 'shared'])

const confirmAttached = (): void => {
onConfirmClick()
onCloseClick()
}

return (
<LegacyModal
title={t('are_you_sure_you_want_to_proceed')}
type="warning"
onClose={onCloseClick}
>
<Flex flexDirection={DIRECTION_COLUMN} fontSize={TYPOGRAPHY.fontSizeP}>
<LegacyStyledText paddingBottom={SPACING.spacing4}>
{t('you_havent_confirmed', {
missingSteps: new Intl.ListFormat('en', {
style: 'short',
type: 'conjunction',
}).format(missingSteps.map(step => t(step))),
})}
</LegacyStyledText>
</Flex>
<Flex
flexDirection={DIRECTION_ROW}
paddingTop={SPACING.spacing32}
justifyContent={JUSTIFY_FLEX_END}
alignItems={ALIGN_CENTER}
gap={SPACING.spacing8}
>
<SecondaryButton onClick={onCloseClick}>
{i18n.format(t('shared:go_back'), 'capitalize')}
</SecondaryButton>
<PrimaryButton onClick={confirmAttached}>
{t('start_run')}
</PrimaryButton>
</Flex>
</LegacyModal>
)
}
35 changes: 31 additions & 4 deletions app/src/organisms/Devices/ProtocolRun/ProtocolRunHeader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ import {
} from '../../../organisms/RunTimeControl/hooks'
import { useIsHeaterShakerInProtocol } from '../../ModuleCard/hooks'
import { ConfirmAttachmentModal } from '../../ModuleCard/ConfirmAttachmentModal'
import { ConfirmMissingStepsModal } from './ConfirmMissingStepsModal'
import {
useProtocolDetailsForRun,
useProtocolAnalysisErrors,
Expand Down Expand Up @@ -132,13 +133,15 @@ interface ProtocolRunHeaderProps {
robotName: string
runId: string
makeHandleJumpToStep: (index: number) => () => void
missingSetupSteps: string[]
}

export function ProtocolRunHeader({
protocolRunHeaderRef,
robotName,
runId,
makeHandleJumpToStep,
missingSetupSteps,
}: ProtocolRunHeaderProps): JSX.Element | null {
const { t } = useTranslation(['run_details', 'shared'])
const navigate = useNavigate()
Expand Down Expand Up @@ -447,6 +450,7 @@ export function ProtocolRunHeader({
isDoorOpen={isDoorOpen}
isFixtureMismatch={isFixtureMismatch}
isResetRunLoadingRef={isResetRunLoadingRef}
missingSetupSteps={missingSetupSteps}
/>
</Flex>
</Box>
Expand Down Expand Up @@ -591,6 +595,7 @@ interface ActionButtonProps {
isDoorOpen: boolean
isFixtureMismatch: boolean
isResetRunLoadingRef: React.MutableRefObject<boolean>
missingSetupSteps: string[]
}

// TODO(jh, 04-22-2024): Refactor switch cases into separate factories to increase readability and testability.
Expand All @@ -603,6 +608,7 @@ function ActionButton(props: ActionButtonProps): JSX.Element {
isDoorOpen,
isFixtureMismatch,
isResetRunLoadingRef,
missingSetupSteps,
} = props
const navigate = useNavigate()
const { t } = useTranslation(['run_details', 'shared'])
Expand Down Expand Up @@ -682,12 +688,20 @@ function ActionButton(props: ActionButtonProps): JSX.Element {
)
const {
confirm: confirmAttachment,
showConfirmation: showConfirmationModal,
cancel: cancelExit,
showConfirmation: showHSConfirmationModal,
cancel: cancelExitHSConfirmation,
} = useConditionalConfirm(
handleProceedToRunClick,
!configBypassHeaterShakerAttachmentConfirmation
)
const {
confirm: confirmMissingSteps,
showConfirmation: showMissingStepsConfirmationModal,
cancel: cancelExitMissingStepsConfirmation,
} = useConditionalConfirm(
handleProceedToRunClick,
missingSetupSteps.length !== 0
)
const robotAnalyticsData = useRobotAnalyticsData(robotName)

const isHeaterShakerInProtocol = useIsHeaterShakerInProtocol()
Expand Down Expand Up @@ -745,6 +759,11 @@ function ActionButton(props: ActionButtonProps): JSX.Element {
handleButtonClick = () => {
if (isHeaterShakerShaking && isHeaterShakerInProtocol) {
setShowIsShakingModal(true)
} else if (
missingSetupSteps.length !== 0 &&
(runStatus === RUN_STATUS_IDLE || runStatus === RUN_STATUS_STOPPED)
) {
confirmMissingSteps()
} else if (
isHeaterShakerInProtocol &&
!isHeaterShakerShaking &&
Expand Down Expand Up @@ -825,13 +844,21 @@ function ActionButton(props: ActionButtonProps): JSX.Element {
startRun={play}
/>
)}
{showConfirmationModal && (
{showHSConfirmationModal && (
<ConfirmAttachmentModal
onCloseClick={cancelExit}
onCloseClick={cancelExitHSConfirmation}
isProceedToRunModal={true}
onConfirmClick={handleProceedToRunClick}
/>
)}
{showMissingStepsConfirmationModal && (
<ConfirmMissingStepsModal
onCloseClick={cancelExitMissingStepsConfirmation}
onConfirmClick={handleProceedToRunClick}
missingSteps={missingSetupSteps}
/>
)}
{}
</>
)
}
Expand Down
Loading

0 comments on commit 4c3305a

Please sign in to comment.