Skip to content

Commit 1d36549

Browse files
committed
refactor(protocol-designer): refactor EditInstrumentsModal
Export most parts and make EditInstrumentsModal as the main component
1 parent 204abed commit 1d36549

File tree

2 files changed

+279
-202
lines changed

2 files changed

+279
-202
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,260 @@
1+
import { useTranslation } from 'react-i18next'
2+
import { useDispatch } from 'react-redux'
3+
import mapValues from 'lodash/mapValues'
4+
5+
import {
6+
ALIGN_CENTER,
7+
Btn,
8+
COLORS,
9+
DIRECTION_COLUMN,
10+
DIRECTION_ROW,
11+
EmptySelectorButton,
12+
Flex,
13+
Icon,
14+
JUSTIFY_SPACE_BETWEEN,
15+
ListItem,
16+
SPACING,
17+
StyledText,
18+
TYPOGRAPHY,
19+
} from '@opentrons/components'
20+
import { FLEX_ROBOT_TYPE } from '@opentrons/shared-data'
21+
22+
import { PipetteInfoItem } from '../PipetteInfoItem'
23+
import { changeSavedStepForm } from '../../steplist/actions'
24+
import { deletePipettes } from '../../step-forms/actions'
25+
import { deleteContainer } from '../../labware-ingred/actions'
26+
import { toggleIsGripperRequired } from '../../step-forms/actions/additionalItems'
27+
import { getSectionsFromPipetteName } from './utils'
28+
import { INITIAL_DECK_SETUP_STEP_ID } from '../../constants'
29+
import { LINK_BUTTON_STYLE } from '../../atoms'
30+
31+
import type { Dispatch, SetStateAction } from 'react'
32+
import type { AdditionalEquipmentName } from '@opentrons/step-generation'
33+
import type { PipetteMount, RobotType } from '@opentrons/shared-data'
34+
import type {
35+
AllTemporalPropertiesForTimelineFrame,
36+
PipetteOnDeck,
37+
} from '../../step-forms'
38+
import type {
39+
Gen,
40+
PipetteType,
41+
} from '../../pages/CreateNewProtocolWizard/types'
42+
import type { ThunkDispatch } from '../../types'
43+
44+
interface Gripper {
45+
name: AdditionalEquipmentName
46+
id: string
47+
location?: string
48+
}
49+
50+
interface PipetteOverviewProps {
51+
has96Channel: boolean
52+
pipettes: AllTemporalPropertiesForTimelineFrame['pipettes']
53+
labware: AllTemporalPropertiesForTimelineFrame['labware']
54+
robotType: RobotType
55+
setPage: Dispatch<SetStateAction<'add' | 'overview'>>
56+
setMount: Dispatch<SetStateAction<PipetteMount>>
57+
setPipetteType: Dispatch<SetStateAction<PipetteType | null>>
58+
setPipetteGen: Dispatch<SetStateAction<Gen | 'flex'>>
59+
setPipetteVolume: Dispatch<SetStateAction<string | null>>
60+
setSelectedTips: Dispatch<SetStateAction<string[]>>
61+
leftPipette?: PipetteOnDeck
62+
rightPipette?: PipetteOnDeck
63+
gripper?: Gripper
64+
}
65+
66+
export function PipetteOverview({
67+
has96Channel,
68+
pipettes,
69+
labware,
70+
robotType,
71+
setPage,
72+
setMount,
73+
setPipetteType,
74+
setPipetteGen,
75+
setPipetteVolume,
76+
setSelectedTips,
77+
leftPipette,
78+
rightPipette,
79+
gripper,
80+
}: PipetteOverviewProps): JSX.Element {
81+
const { i18n, t } = useTranslation('create_new_protocol')
82+
const dispatch = useDispatch<ThunkDispatch<any>>()
83+
84+
const swapPipetteUpdate = mapValues(pipettes, pipette => {
85+
if (!pipette.mount) return pipette.mount
86+
return pipette.mount === 'left' ? 'right' : 'left'
87+
})
88+
89+
const targetPipetteMount = leftPipette == null ? 'left' : 'right'
90+
91+
const rightInfo =
92+
rightPipette != null
93+
? getSectionsFromPipetteName(rightPipette.name, rightPipette.spec)
94+
: null
95+
const leftInfo =
96+
leftPipette != null
97+
? getSectionsFromPipetteName(leftPipette.name, leftPipette.spec)
98+
: null
99+
100+
const previousLeftPipetteTipracks = Object.values(labware)
101+
.filter(lw => lw.def.parameters.isTiprack)
102+
.filter(tip => leftPipette?.tiprackDefURI.includes(tip.labwareDefURI))
103+
const previousRightPipetteTipracks = Object.values(labware)
104+
.filter(lw => lw.def.parameters.isTiprack)
105+
.filter(tip => rightPipette?.tiprackDefURI.includes(tip.labwareDefURI))
106+
107+
return (
108+
<Flex flexDirection={DIRECTION_COLUMN} gridGap={SPACING.spacing24}>
109+
<Flex flexDirection={DIRECTION_COLUMN} gridGap={SPACING.spacing8}>
110+
<Flex justifyContent={JUSTIFY_SPACE_BETWEEN} alignItems={ALIGN_CENTER}>
111+
<StyledText desktopStyle="bodyLargeSemiBold">
112+
{t('your_pipettes')}
113+
</StyledText>
114+
{has96Channel ||
115+
(leftPipette == null && rightPipette == null) ? null : (
116+
<Btn
117+
css={LINK_BUTTON_STYLE}
118+
onClick={() =>
119+
dispatch(
120+
changeSavedStepForm({
121+
stepId: INITIAL_DECK_SETUP_STEP_ID,
122+
update: {
123+
pipetteLocationUpdate: swapPipetteUpdate,
124+
},
125+
})
126+
)
127+
}
128+
>
129+
<Flex flexDirection={DIRECTION_ROW}>
130+
<Icon
131+
name="swap-horizontal"
132+
size="1rem"
133+
transform="rotate(90deg)"
134+
/>
135+
<StyledText desktopStyle="captionSemiBold">
136+
{t('swap_pipette_mounts')}
137+
</StyledText>
138+
</Flex>
139+
</Btn>
140+
)}
141+
</Flex>
142+
<Flex flexDirection={DIRECTION_COLUMN} gridGap={SPACING.spacing8}>
143+
{leftPipette?.tiprackDefURI != null && leftInfo != null ? (
144+
<PipetteInfoItem
145+
mount="left"
146+
pipetteName={leftPipette.name}
147+
tiprackDefURIs={leftPipette.tiprackDefURI}
148+
editClick={() => {
149+
setPage('add')
150+
setMount('left')
151+
setPipetteType(leftInfo.type)
152+
setPipetteGen(leftInfo.gen)
153+
setPipetteVolume(leftInfo.volume)
154+
setSelectedTips(leftPipette.tiprackDefURI as string[])
155+
}}
156+
cleanForm={() => {
157+
dispatch(deletePipettes([leftPipette.id as string]))
158+
previousLeftPipetteTipracks.forEach(tip =>
159+
dispatch(deleteContainer({ labwareId: tip.id }))
160+
)
161+
}}
162+
/>
163+
) : null}
164+
{rightPipette?.tiprackDefURI != null && rightInfo != null ? (
165+
<PipetteInfoItem
166+
mount="right"
167+
pipetteName={rightPipette.name}
168+
tiprackDefURIs={rightPipette.tiprackDefURI}
169+
editClick={() => {
170+
setPage('add')
171+
setMount('right')
172+
setPipetteType(rightInfo.type)
173+
setPipetteGen(rightInfo.gen)
174+
setPipetteVolume(rightInfo.volume)
175+
setSelectedTips(rightPipette.tiprackDefURI as string[])
176+
}}
177+
cleanForm={() => {
178+
dispatch(deletePipettes([rightPipette.id as string]))
179+
previousRightPipetteTipracks.forEach(tip =>
180+
dispatch(deleteContainer({ labwareId: tip.id }))
181+
)
182+
}}
183+
/>
184+
) : null}
185+
{has96Channel ||
186+
(leftPipette != null && rightPipette != null) ? null : (
187+
<EmptySelectorButton
188+
onClick={() => {
189+
setPage('add')
190+
setMount(targetPipetteMount)
191+
}}
192+
text={t('add_pipette')}
193+
textAlignment="left"
194+
iconName="plus"
195+
/>
196+
)}
197+
</Flex>
198+
</Flex>
199+
{robotType === FLEX_ROBOT_TYPE ? (
200+
<Flex flexDirection={DIRECTION_COLUMN} gridGap={SPACING.spacing8}>
201+
<Flex
202+
justifyContent={JUSTIFY_SPACE_BETWEEN}
203+
alignItems={ALIGN_CENTER}
204+
>
205+
<StyledText desktopStyle="bodyLargeSemiBold">
206+
{t('protocol_overview:your_gripper')}
207+
</StyledText>
208+
</Flex>
209+
<Flex flexDirection={DIRECTION_COLUMN} gridGap={SPACING.spacing8}>
210+
{gripper != null ? (
211+
<ListItem type="noActive">
212+
<Flex
213+
padding={SPACING.spacing12}
214+
justifyContent={JUSTIFY_SPACE_BETWEEN}
215+
width="100%"
216+
>
217+
<Flex
218+
gridGap={SPACING.spacing4}
219+
flexDirection={DIRECTION_COLUMN}
220+
>
221+
<StyledText desktopStyle="bodyDefaultSemiBold">
222+
{t('protocol_overview:extension')}
223+
</StyledText>
224+
<StyledText
225+
desktopStyle="bodyDefaultRegular"
226+
color={COLORS.grey60}
227+
>
228+
{i18n.format(t('gripper'), 'capitalize')}
229+
</StyledText>
230+
</Flex>
231+
<Btn
232+
css={LINK_BUTTON_STYLE}
233+
textDecoration={TYPOGRAPHY.textDecorationUnderline}
234+
padding={SPACING.spacing4}
235+
onClick={() => {
236+
dispatch(toggleIsGripperRequired())
237+
}}
238+
>
239+
<StyledText desktopStyle="bodyDefaultRegular">
240+
{t('remove')}
241+
</StyledText>
242+
</Btn>
243+
</Flex>
244+
</ListItem>
245+
) : (
246+
<EmptySelectorButton
247+
onClick={() => {
248+
dispatch(toggleIsGripperRequired())
249+
}}
250+
text={t('protocol_overview:add_gripper')}
251+
textAlignment="left"
252+
iconName="plus"
253+
/>
254+
)}
255+
</Flex>
256+
</Flex>
257+
) : null}
258+
</Flex>
259+
)
260+
}

0 commit comments

Comments
 (0)