diff --git a/app/src/assets/localization/en/device_details.json b/app/src/assets/localization/en/device_details.json index b1900f73885..d217718af42 100644 --- a/app/src/assets/localization/en/device_details.json +++ b/app/src/assets/localization/en/device_details.json @@ -13,6 +13,7 @@ "an_error_occurred_while_updating": "An error occurred while updating your pipette's settings.", "attach_gripper": "Attach gripper", "attach_pipette": "Attach pipette", + "bad_run": "run could not be loaded", "both_mounts": "Both Mounts", "bundle_firmware_file_not_found": "Bundled fw file not found for module of type: {{module}}", "calibrate_gripper": "Calibrate gripper", diff --git a/app/src/organisms/OnDeviceDisplay/RobotDashboard/RecentRunProtocolCard.tsx b/app/src/organisms/OnDeviceDisplay/RobotDashboard/RecentRunProtocolCard.tsx index ac8ecf347c2..6120614f954 100644 --- a/app/src/organisms/OnDeviceDisplay/RobotDashboard/RecentRunProtocolCard.tsx +++ b/app/src/organisms/OnDeviceDisplay/RobotDashboard/RecentRunProtocolCard.tsx @@ -32,7 +32,7 @@ import { useTrackEvent } from '../../../redux/analytics' import { Skeleton } from '../../../atoms/Skeleton' import { useMissingProtocolHardware } from '../../../pages/Protocols/hooks' import { useCloneRun } from '../../ProtocolUpload/hooks' -import { useHardwareStatusText } from './hooks' +import { useRerunnableStatusText } from './hooks' import { useRobotInitializationStatus, INIT_STATUS, @@ -77,8 +77,10 @@ export function ProtocolWithLastRun({ conflictedSlots, } = useMissingProtocolHardware(protocolData.id) const history = useHistory() - const isReadyToBeReRun = missingProtocolHardware.length === 0 - const chipText = useHardwareStatusText( + const isOk = 'ok' in runData ? !(runData?.ok === false) : true + const isReadyToBeReRun = isOk && missingProtocolHardware.length === 0 + const chipText = useRerunnableStatusText( + isOk, missingProtocolHardware, conflictedSlots ) @@ -162,7 +164,13 @@ export function ProtocolWithLastRun({ flexDirection={DIRECTION_COLUMN} padding={SPACING.spacing24} gridGap={SPACING.spacing24} - backgroundColor={isReadyToBeReRun ? COLORS.green35 : COLORS.yellow35} + backgroundColor={ + isOk + ? isReadyToBeReRun + ? COLORS.green35 + : COLORS.yellow35 + : COLORS.red35 + } width="25.8125rem" height="24.5rem" borderRadius={BORDERS.borderRadius16} @@ -171,7 +179,7 @@ export function ProtocolWithLastRun({ diff --git a/app/src/organisms/OnDeviceDisplay/RobotDashboard/__tests__/RecentRunProtocolCard.test.tsx b/app/src/organisms/OnDeviceDisplay/RobotDashboard/__tests__/RecentRunProtocolCard.test.tsx index 1cac85c3727..1584e3ce723 100644 --- a/app/src/organisms/OnDeviceDisplay/RobotDashboard/__tests__/RecentRunProtocolCard.test.tsx +++ b/app/src/organisms/OnDeviceDisplay/RobotDashboard/__tests__/RecentRunProtocolCard.test.tsx @@ -16,7 +16,7 @@ import { useMissingProtocolHardware } from '../../../../pages/Protocols/hooks' import { useTrackProtocolRunEvent } from '../../../Devices/hooks' import { useTrackEvent } from '../../../../redux/analytics' import { useCloneRun } from '../../../ProtocolUpload/hooks' -import { useHardwareStatusText } from '../hooks' +import { useRerunnableStatusText } from '../hooks' import { RecentRunProtocolCard } from '../' import { useNotifyAllRunsQuery } from '../../../../resources/runs' import { @@ -82,6 +82,14 @@ const mockRunData = { status: RUN_STATUS_FAILED, } as any +const mockBadRunData = { + ...mockRunData, + ok: false, + dataError: { + title: 'Bad run oh no', + }, +} as any + const mockCloneRun = vi.fn() const render = (props: React.ComponentProps) => { @@ -109,7 +117,7 @@ describe('RecentRunProtocolCard', () => { } vi.mocked(Skeleton).mockReturnValue(
mock Skeleton
) - vi.mocked(useHardwareStatusText).mockReturnValue('Ready to run') + vi.mocked(useRerunnableStatusText).mockReturnValue('Ready to run') vi.mocked(useTrackEvent).mockReturnValue(mockTrackEvent) vi.mocked(useMissingProtocolHardware).mockReturnValue({ missingProtocolHardware: [], @@ -157,7 +165,7 @@ describe('RecentRunProtocolCard', () => { isLoading: false, conflictedSlots: [], }) - vi.mocked(useHardwareStatusText).mockReturnValue('Missing 1 pipette') + vi.mocked(useRerunnableStatusText).mockReturnValue('Missing 1 pipette') render(props) screen.getByText('Missing 1 pipette') }) @@ -168,7 +176,7 @@ describe('RecentRunProtocolCard', () => { isLoading: false, conflictedSlots: ['cutoutD3'], }) - vi.mocked(useHardwareStatusText).mockReturnValue('Location conflicts') + vi.mocked(useRerunnableStatusText).mockReturnValue('Location conflicts') render(props) screen.getByText('Location conflicts') }) @@ -179,7 +187,7 @@ describe('RecentRunProtocolCard', () => { isLoading: false, conflictedSlots: [], }) - vi.mocked(useHardwareStatusText).mockReturnValue('Missing 1 module') + vi.mocked(useRerunnableStatusText).mockReturnValue('Missing 1 module') render(props) screen.getByText('Missing 1 module') }) @@ -190,11 +198,23 @@ describe('RecentRunProtocolCard', () => { isLoading: false, conflictedSlots: [], }) - vi.mocked(useHardwareStatusText).mockReturnValue('Missing hardware') + vi.mocked(useRerunnableStatusText).mockReturnValue('Missing hardware') render(props) screen.getByText('Missing hardware') }) + it('should render bad protocol chip when the protocol is bad even if hardware matches', () => { + vi.mocked(useNotifyAllRunsQuery).mockReturnValue({ + data: { data: [mockRunData] }, + } as any) + const propsWithBadRun = { runData: mockBadRunData } + vi.mocked(useRerunnableStatusText).mockReturnValue( + 'Run could not be loaded' + ) + render(propsWithBadRun) + screen.getByText('Run could not be loaded') + }) + it('when tapping a card, mock functions is called and loading state is activated', () => { render(props) const button = screen.getByLabelText('RecentRunProtocolCard') diff --git a/app/src/organisms/OnDeviceDisplay/RobotDashboard/hooks/index.ts b/app/src/organisms/OnDeviceDisplay/RobotDashboard/hooks/index.ts index 2fd616db9c5..d0596ce974d 100644 --- a/app/src/organisms/OnDeviceDisplay/RobotDashboard/hooks/index.ts +++ b/app/src/organisms/OnDeviceDisplay/RobotDashboard/hooks/index.ts @@ -1 +1,2 @@ export * from './useHardwareStatusText' +export * from './useRerunnableStatusText' diff --git a/app/src/organisms/OnDeviceDisplay/RobotDashboard/hooks/useRerunnableStatusText.ts b/app/src/organisms/OnDeviceDisplay/RobotDashboard/hooks/useRerunnableStatusText.ts new file mode 100644 index 00000000000..77ecd30a5b9 --- /dev/null +++ b/app/src/organisms/OnDeviceDisplay/RobotDashboard/hooks/useRerunnableStatusText.ts @@ -0,0 +1,16 @@ +import { useTranslation } from 'react-i18next' +import { useHardwareStatusText } from './useHardwareStatusText' +import type { ProtocolHardware } from '../../../../pages/Protocols/hooks' + +export function useRerunnableStatusText( + runOk: boolean, + missingProtocolhardware: ProtocolHardware[], + conflictedSlots: string[] +): string { + const hardwareStatus = useHardwareStatusText( + missingProtocolhardware, + conflictedSlots + ) + const { t, i18n } = useTranslation('device_details') + return runOk ? hardwareStatus : i18n.format(t('bad_run'), 'capitalize') +}