Skip to content

Commit

Permalink
fixture group shape update and validation
Browse files Browse the repository at this point in the history
  • Loading branch information
CaseyBatten committed Apr 3, 2024
1 parent 2b4ddd9 commit 2c8fe4a
Show file tree
Hide file tree
Showing 25 changed files with 423 additions and 199 deletions.
105 changes: 65 additions & 40 deletions app/src/organisms/DeviceDetailsDeckConfiguration/AddFixtureModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -97,10 +97,13 @@ export function AddFixtureModal({
let initialStage: OptionStage = SINGLE_CENTER_CUTOUTS.includes(cutoutId) // only mag block (a module) can be configured in column 2
? 'moduleOptions'
: 'modulesOrFixtures'
if (providedFixtureOptions != null) { // only show provided options if given as props
if (providedFixtureOptions != null) {
// only show provided options if given as props
initialStage = 'providedOptions'
}
const [optionStage, setOptionStage] = React.useState<OptionStage>(initialStage)
const [optionStage, setOptionStage] = React.useState<OptionStage>(
initialStage
)

const modalHeader: ModalHeaderBaseProps = {
title: t('add_to_slot', {
Expand All @@ -120,50 +123,57 @@ export function AddFixtureModal({
width: '26.75rem',
}


let availableOptions: CutoutConfig[][] = []

if (providedFixtureOptions != null) {
availableOptions = providedFixtureOptions?.map(o => ([{
cutoutId,
cutoutFixtureId: o,
opentronsModuleSerialNumber: undefined,
}]))
availableOptions = providedFixtureOptions?.map(o => [
{
cutoutId,
cutoutFixtureId: o,
opentronsModuleSerialNumber: undefined,
},
])
} else if (optionStage === 'fixtureOptions') {
if (
SINGLE_RIGHT_CUTOUTS.includes(cutoutId) ||
SINGLE_LEFT_CUTOUTS.includes(cutoutId)
) {
availableOptions = [
...availableOptions,
[{
cutoutId,
cutoutFixtureId: TRASH_BIN_ADAPTER_FIXTURE
}],
[
{
cutoutId,
cutoutFixtureId: TRASH_BIN_ADAPTER_FIXTURE,
},
],
]
}
if (STAGING_AREA_CUTOUTS.includes(cutoutId)) {
availableOptions = [
...availableOptions,
[{
cutoutId,
cutoutFixtureId: STAGING_AREA_RIGHT_SLOT_FIXTURE
}],
[
{
cutoutId,
cutoutFixtureId: STAGING_AREA_RIGHT_SLOT_FIXTURE,
},
],
]
}
} else if (optionStage === 'moduleOptions') {
availableOptions = [
...availableOptions,
[{
cutoutId,
cutoutFixtureId: MAGNETIC_BLOCK_V1_FIXTURE
}],
[
{
cutoutId,
cutoutFixtureId: MAGNETIC_BLOCK_V1_FIXTURE,
},
],
]
if (unconfiguredMods.length > 0) {
if (THERMOCYCLER_MODULE_CUTOUTS.includes(cutoutId)) {
const unconfiguredTCs = unconfiguredMods
.filter(mod => mod.moduleModel === THERMOCYCLER_MODULE_V2)
.map(mod => ([
.map(mod => [
{
cutoutId: THERMOCYCLER_MODULE_CUTOUTS[0],
cutoutFixtureId: THERMOCYCLER_V2_REAR_FIXTURE,
Expand All @@ -173,8 +183,8 @@ export function AddFixtureModal({
cutoutId: THERMOCYCLER_MODULE_CUTOUTS[1],
cutoutFixtureId: THERMOCYCLER_V2_FRONT_FIXTURE,
opentronsModuleSerialNumber: mod.serialNumber,
}
]))
},
])
availableOptions = [...availableOptions, ...unconfiguredTCs]
}
if (
Expand All @@ -183,11 +193,13 @@ export function AddFixtureModal({
) {
const unconfiguredHeaterShakers = unconfiguredMods
.filter(mod => mod.moduleModel === HEATERSHAKER_MODULE_V1)
.map(mod => ([{
cutoutId,
cutoutFixtureId: HEATERSHAKER_MODULE_V1_FIXTURE,
opentronsModuleSerialNumber: mod.serialNumber,
}]))
.map(mod => [
{
cutoutId,
cutoutFixtureId: HEATERSHAKER_MODULE_V1_FIXTURE,
opentronsModuleSerialNumber: mod.serialNumber,
},
])
availableOptions = [...availableOptions, ...unconfiguredHeaterShakers]
}
if (
Expand All @@ -196,22 +208,26 @@ export function AddFixtureModal({
) {
const unconfiguredTemperatureModules = unconfiguredMods
.filter(mod => mod.moduleModel === TEMPERATURE_MODULE_V2)
.map(mod => ([{
cutoutId,
cutoutFixtureId: TEMPERATURE_MODULE_V2_FIXTURE,
opentronsModuleSerialNumber: mod.serialNumber,
}]))
.map(mod => [
{
cutoutId,
cutoutFixtureId: TEMPERATURE_MODULE_V2_FIXTURE,
opentronsModuleSerialNumber: mod.serialNumber,
},
])
availableOptions = [
...availableOptions,
...unconfiguredTemperatureModules,
]
}
}
} else if (optionStage === 'wasteChuteOptions') {
availableOptions = WASTE_CHUTE_FIXTURES.map(fixture => ([{
cutoutId,
cutoutFixtureId: fixture,
}]))
availableOptions = WASTE_CHUTE_FIXTURES.map(fixture => [
{
cutoutId,
cutoutFixtureId: fixture,
},
])
}

const handleAddODD = (cutoutConfigs: CutoutConfig[]): void => {
Expand Down Expand Up @@ -254,7 +270,10 @@ export function AddFixtureModal({
/>
</>
)
} else if (optionStage === 'fixtureOptions' && cutoutId === WASTE_CHUTE_CUTOUT) {
} else if (
optionStage === 'fixtureOptions' &&
cutoutId === WASTE_CHUTE_CUTOUT
) {
nextStageOptions = (
<>
<FixtureOption
Expand All @@ -272,7 +291,9 @@ export function AddFixtureModal({

const handleAddDesktop = (addedCutoutConfigs: CutoutConfig[]): void => {
const newDeckConfig = deckConfig.map(fixture => {
const replacementCutoutConfig = addedCutoutConfigs.find(c => c.cutoutId === fixture.cutoutId)
const replacementCutoutConfig = addedCutoutConfigs.find(
c => c.cutoutId === fixture.cutoutId
)
return replacementCutoutConfig ?? fixture
})

Expand Down Expand Up @@ -319,7 +340,11 @@ export function AddFixtureModal({
key={cutoutConfigs[0].cutoutFixtureId}
optionName={getFixtureDisplayName(
cutoutConfigs[0].cutoutFixtureId,
(modulesData?.data ?? []).find(m => m.serialNumber === cutoutConfigs[0].opentronsModuleSerialNumber)?.usbPort.port
(modulesData?.data ?? []).find(
m =>
m.serialNumber ===
cutoutConfigs[0].opentronsModuleSerialNumber
)?.usbPort.port
)}
buttonText={t('add')}
onClickHandler={() => handleAddDesktop(cutoutConfigs)}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -164,8 +164,6 @@ describe('Desktop AddFixtureModal', () => {
expect(screen.getByRole('button', { name: 'Add' })).toBeInTheDocument()
})



it('should call update deck config when add button is clicked', () => {
props = { ...props, cutoutId: 'cutoutA1' }
render(props)
Expand Down
87 changes: 62 additions & 25 deletions app/src/organisms/DeviceDetailsDeckConfiguration/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ interface DeviceDetailsDeckConfigurationProps {
}

function getDisplayLocationForFixtureGroup(cutouts: CutoutId[]): string {
return cutouts.map(cutoutId => getCutoutDisplayName(cutoutId)).join(" + ")
return cutouts.map(cutoutId => getCutoutDisplayName(cutoutId)).join(' + ')
}

export function DeviceDetailsDeckConfiguration({
Expand Down Expand Up @@ -92,66 +92,101 @@ export function DeviceDetailsDeckConfiguration({
setShowAddFixtureModal(true)
}

const handleClickRemove = (cutoutId: CutoutId, cutoutFixtureId: CutoutFixtureId): void => {
const handleClickRemove = (
cutoutId: CutoutId,
cutoutFixtureId: CutoutFixtureId
): void => {
let replacementFixtureId: CutoutFixtureId = SINGLE_CENTER_SLOT_FIXTURE
if (SINGLE_RIGHT_CUTOUTS.includes(cutoutId)) {
replacementFixtureId = SINGLE_RIGHT_SLOT_FIXTURE
} else if (SINGLE_LEFT_CUTOUTS.includes(cutoutId)) {
replacementFixtureId = SINGLE_LEFT_SLOT_FIXTURE
}

const fixtureGroup = deckDef.cutoutFixtures.find(cf => cf.id === cutoutFixtureId)?.fixtureGroup ?? []
const fixtureGroup =
deckDef.cutoutFixtures.find(cf => cf.id === cutoutFixtureId)
?.fixtureGroup ?? []

let newDeckConfig = deckConfig
if (fixtureGroup.length > 0) {
newDeckConfig = deckConfig.map(cutoutConfig => (
newDeckConfig = deckConfig.map(cutoutConfig =>
fixtureGroup.includes(cutoutConfig.cutoutFixtureId)
? {
...cutoutConfig,
cutoutFixtureId: replacementFixtureId,
opentronsModuleSerialNumber: undefined,
}
...cutoutConfig,
cutoutFixtureId: replacementFixtureId,
opentronsModuleSerialNumber: undefined,
}
: cutoutConfig
))
)
} else {
newDeckConfig = deckConfig.map(cutoutConfig => (
newDeckConfig = deckConfig.map(cutoutConfig =>
cutoutConfig.cutoutId === cutoutId
? {
...cutoutConfig,
cutoutFixtureId: replacementFixtureId,
opentronsModuleSerialNumber: undefined,
}
...cutoutConfig,
cutoutFixtureId: replacementFixtureId,
opentronsModuleSerialNumber: undefined,
}
: cutoutConfig
))
)
}
updateDeckConfiguration(newDeckConfig)
}

// do not show standard slot in fixture display list
const { displayList: fixtureDisplayList } = deckConfig.reduce<{ displayList: { displayLocation: string, displayName: string }[], groupedCutoutIds: CutoutId[] }>(
const { displayList: fixtureDisplayList } = deckConfig.reduce<{
displayList: { displayLocation: string; displayName: string }[]

Check failure on line 137 in app/src/organisms/DeviceDetailsDeckConfiguration/index.tsx

View workflow job for this annotation

GitHub Actions / js checks

Array type using 'T[]' is forbidden for non-simple types. Use 'Array<T>' instead

Check failure on line 137 in app/src/organisms/DeviceDetailsDeckConfiguration/index.tsx

View workflow job for this annotation

GitHub Actions / js checks

Array type using 'T[]' is forbidden for non-simple types. Use 'Array<T>' instead
groupedCutoutIds: CutoutId[]
}>(
(acc, { cutoutId, cutoutFixtureId, opentronsModuleSerialNumber }) => {
if (cutoutFixtureId == null || SINGLE_SLOT_FIXTURES.includes(cutoutFixtureId)) {
if (
cutoutFixtureId == null ||
SINGLE_SLOT_FIXTURES.includes(cutoutFixtureId)
) {
return acc
}
const displayName = getFixtureDisplayName(cutoutFixtureId, modulesData?.data.find(m => m.serialNumber === opentronsModuleSerialNumber)?.usbPort.port)
const fixtureGroup = getFixtureGroupForCutoutFixture(cutoutFixtureId, deckDef.cutoutFixtures)
const displayName = getFixtureDisplayName(
cutoutFixtureId,
modulesData?.data.find(
m => m.serialNumber === opentronsModuleSerialNumber
)?.usbPort.port
)
const fixtureGroup = getFixtureGroupForCutoutFixture(
cutoutFixtureId,
deckDef.cutoutFixtures
)
if (fixtureGroup.length > 1) {
const groupedCutoutIds = fixtureGroup.map(groupedFixtureId => deckConfig.find(cc => cc.cutoutFixtureId === groupedFixtureId)?.cutoutId).filter((cc): cc is CutoutId => cc !== null)
const displayLocation = getDisplayLocationForFixtureGroup(groupedCutoutIds)
const groupedCutoutIds = fixtureGroup
.map(
groupedFixtureId =>
deckConfig.find(cc => cc.cutoutFixtureId === groupedFixtureId)
?.cutoutId
)
.filter((cc): cc is CutoutId => cc !== null)
const displayLocation = getDisplayLocationForFixtureGroup(
groupedCutoutIds
)
if (acc.groupedCutoutIds.includes(cutoutId)) {
return acc // only list grouped fixtures once
} else {
return {
displayList: [...acc.displayList, { displayLocation, displayName }],
groupedCutoutIds: [...acc.groupedCutoutIds, ...groupedCutoutIds]
groupedCutoutIds: [...acc.groupedCutoutIds, ...groupedCutoutIds],
}
}
}
return {
...acc,
displayList: [...acc.displayList, { displayLocation: getDisplayLocationForFixtureGroup([cutoutId]), displayName }]
displayList: [
...acc.displayList,
{
displayLocation: getDisplayLocationForFixtureGroup([cutoutId]),
displayName,
},
],
}
}, { displayList: [], groupedCutoutIds: [] })
},
{ displayList: [], groupedCutoutIds: [] }
)

return (
<>
Expand Down Expand Up @@ -247,7 +282,9 @@ export function DeviceDetailsDeckConfiguration({
css={TYPOGRAPHY.labelSemiBold}
>
<StyledText flex="1 0 30px">{t('location')}</StyledText>
<StyledText flex="9 1 0">{i18n.format(t('deck_hardware'), 'capitalize')}</StyledText>
<StyledText flex="9 1 0">
{i18n.format(t('deck_hardware'), 'capitalize')}
</StyledText>
</Flex>
{fixtureDisplayList.length > 0 ? (
fixtureDisplayList.map(({ displayLocation, displayName }) => (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -110,15 +110,31 @@ export const LocationConflictModal = (

updateDeckConfiguration(newRequiredFixtureDeckConfig)
} else if (requiredModule != null) {
const addressableAreas = getAddressableAreaNamesFromLoadedModule(requiredModule, cutoutId.replace('cutout', ''), deckDef)
const cutoutFixtures = getCutoutFixturesForModuleModel(requiredModule, deckDef)
const addressableAreas = getAddressableAreaNamesFromLoadedModule(
requiredModule,
cutoutId.replace('cutout', ''),
deckDef
)
const cutoutFixtures = getCutoutFixturesForModuleModel(
requiredModule,
deckDef
)
const newDeckConfig = deckConfig.map(fixture => {
const replacementFixture = cutoutFixtures.find(cf => (
cf.mayMountTo.includes(fixture.cutoutId)
&& addressableAreas.some(aa => cf.providesAddressableAreas[cutoutId].includes(aa))
))
const replacementFixture = cutoutFixtures.find(
cf =>
cf.mayMountTo.includes(fixture.cutoutId) &&
addressableAreas.some(aa =>
cf.providesAddressableAreas[cutoutId].includes(aa)
)
)
return cutoutId === fixture.cutoutId && replacementFixture != null
? { ...fixture, cutoutFixtureId: replacementFixture?.id, opentronsModuleSerialNumber: attachedModules.find(m => m.moduleModel === requiredModule)?.serialNumber }
? {
...fixture,
cutoutFixtureId: replacementFixture?.id,
opentronsModuleSerialNumber: attachedModules.find(
m => m.moduleModel === requiredModule
)?.serialNumber,
}
: fixture
})
updateDeckConfiguration(newDeckConfig)
Expand Down
Loading

0 comments on commit 2c8fe4a

Please sign in to comment.