diff --git a/protocol-designer/src/assets/localization/en/application.json b/protocol-designer/src/assets/localization/en/application.json index 78394f35ab7..b4d1ebf8157 100644 --- a/protocol-designer/src/assets/localization/en/application.json +++ b/protocol-designer/src/assets/localization/en/application.json @@ -43,7 +43,7 @@ "thermocycler": "thermocycler" }, "temperature": "Temperature (˚C)", - "time": "Time (hh:mm:ss)", + "time": "Time", "units": { "cycles": "cycles", "degrees": "°C", @@ -55,6 +55,7 @@ "rpm": "rpm", "seconds": "s", "time": "mm:ss", + "time_hms": "hh:mm:ss", "times": "x" }, "update": "UPDATE", diff --git a/protocol-designer/src/assets/localization/en/form.json b/protocol-designer/src/assets/localization/en/form.json index 80bef5b5a20..995c6ae9d0e 100644 --- a/protocol-designer/src/assets/localization/en/form.json +++ b/protocol-designer/src/assets/localization/en/form.json @@ -72,23 +72,24 @@ "range": "between {{min}} and {{max}}" }, "heaterShaker": { + "duration": "Duration", "latch": { - "setLatch": "Labware Latch", - "toggleOff": "Closed & Locked", + "setLatch": "Labware latch", + "toggleOff": "Close", "toggleOn": "Open" }, "shaker": { - "setShake": "Set shake speed", - "toggleOff": "Deactivated", + "setShake": "Shake speed", + "toggleOff": "Deactivate", "toggleOn": "Active" }, "temperature": { - "setTemperature": "Set temperature", - "toggleOff": "Deactivated", + "setTemperature": "Temperature", + "toggleOff": "Deactivate", "toggleOn": "Active" }, "timer": { - "heaterShakerSetTimer": "Set timer" + "heaterShakerSetTimer": "Timer" } }, "location": { @@ -144,6 +145,7 @@ } }, "pauseAction": { + "duration": "Delay duration", "options": { "untilResume": "Pause until told to resume", "untilTemperature": "Pause until temperature reached", diff --git a/protocol-designer/src/assets/localization/en/protocol_steps.json b/protocol-designer/src/assets/localization/en/protocol_steps.json index b2f00290059..4f7e3a00ed6 100644 --- a/protocol-designer/src/assets/localization/en/protocol_steps.json +++ b/protocol-designer/src/assets/localization/en/protocol_steps.json @@ -93,6 +93,7 @@ "select_volume": "Select a volume", "shake": "Shake", "single": "Single path", + "speed": "Speed", "starting_deck_state": "Starting deck state", "step_substeps": "{{stepType}} details", "temperature": "Temperature", diff --git a/protocol-designer/src/atoms/ToggleButton/index.tsx b/protocol-designer/src/atoms/ToggleButton/index.tsx index 9bb4c45a330..0dfb605ec7b 100644 --- a/protocol-designer/src/atoms/ToggleButton/index.tsx +++ b/protocol-designer/src/atoms/ToggleButton/index.tsx @@ -1,7 +1,7 @@ import type * as React from 'react' import { css } from 'styled-components' -import { Btn, Icon, COLORS } from '@opentrons/components' +import { Btn, Icon, COLORS, Flex } from '@opentrons/components' import type { StyleProps } from '@opentrons/components' @@ -38,8 +38,8 @@ const TOGGLE_ENABLED_STYLES = css` ` interface ToggleButtonProps extends StyleProps { - label: string toggledOn: boolean + label?: string | null disabled?: boolean | null id?: string onClick?: (e: React.MouseEvent) => void @@ -59,7 +59,9 @@ export function ToggleButton(props: ToggleButtonProps): JSX.Element { css={props.toggledOn ? TOGGLE_ENABLED_STYLES : TOGGLE_DISABLED_STYLES} {...buttonProps} > - + + + ) } diff --git a/protocol-designer/src/components/StepEditForm/index.tsx b/protocol-designer/src/components/StepEditForm/index.tsx index 8b54c56c891..747651e7268 100644 --- a/protocol-designer/src/components/StepEditForm/index.tsx +++ b/protocol-designer/src/components/StepEditForm/index.tsx @@ -187,7 +187,7 @@ const StepEditFormManager = ( : '' } handleCancelClick={saveStepForm} - handleContinueClick={confirmAddPauseUntilTempStep} + handleContinueClick={handleSave} // TODO (nd: 10/21/2024) can remove this prop once redesign FF removed moduleType={ showAddPauseUntilTempStepModal diff --git a/protocol-designer/src/form-types.ts b/protocol-designer/src/form-types.ts index 0dc4497cdae..58da6b5676b 100644 --- a/protocol-designer/src/form-types.ts +++ b/protocol-designer/src/form-types.ts @@ -366,6 +366,7 @@ export interface HydratedHeaterShakerFormData { heaterShakerSetTimer: 'true' | 'false' | null heaterShakerTimerMinutes: string | null heaterShakerTimerSeconds: string | null + heaterShakerTimer?: string | null id: string latchOpen: boolean moduleId: string diff --git a/protocol-designer/src/molecules/ToggleExpandStepFormField/index.tsx b/protocol-designer/src/molecules/ToggleExpandStepFormField/index.tsx index ed57de37f3b..b27b72b7821 100644 --- a/protocol-designer/src/molecules/ToggleExpandStepFormField/index.tsx +++ b/protocol-designer/src/molecules/ToggleExpandStepFormField/index.tsx @@ -18,12 +18,11 @@ interface ToggleExpandStepFormFieldProps extends FieldProps { fieldTitle: string isSelected: boolean units: string - onLabel: string - offLabel: string toggleUpdateValue: (value: unknown) => void toggleValue: unknown + onLabel?: string + offLabel?: string caption?: string - islabel?: boolean } export function ToggleExpandStepFormField( props: ToggleExpandStepFormFieldProps @@ -38,16 +37,24 @@ export function ToggleExpandStepFormField( toggleUpdateValue, toggleValue, caption, - islabel, ...restProps } = props + const resetFieldValue = (): void => { + restProps.updateValue('null') + } + const onToggleUpdateValue = (): void => { if (typeof toggleValue === 'boolean') { toggleUpdateValue(!toggleValue) + if (toggleValue) { + resetFieldValue() + } } else if (toggleValue === 'engage' || toggleValue === 'disengage') { const newToggleValue = toggleValue === 'engage' ? 'disengage' : 'engage' toggleUpdateValue(newToggleValue) + } else if (toggleValue == null) { + toggleUpdateValue(true) } } @@ -60,16 +67,10 @@ export function ToggleExpandStepFormField( > {title} - - {islabel ? ( - - {isSelected ? onLabel : offLabel} - - ) : null} - + + + {isSelected ? onLabel : offLabel ?? null} + { onToggleUpdateValue() diff --git a/protocol-designer/src/molecules/ToggleStepFormField/index.tsx b/protocol-designer/src/molecules/ToggleStepFormField/index.tsx index 51bc2a1ef25..9ce244c7d57 100644 --- a/protocol-designer/src/molecules/ToggleStepFormField/index.tsx +++ b/protocol-designer/src/molecules/ToggleStepFormField/index.tsx @@ -62,14 +62,16 @@ export function ToggleStepFormField( > {isSelected ? onLabel : offLabel} - { - toggleUpdateValue(!toggleValue) - }} - label={isSelected ? onLabel : offLabel} - toggledOn={isSelected} - /> + {isDisabled ? null : ( + { + toggleUpdateValue(!toggleValue) + }} + label={isSelected ? onLabel : offLabel} + toggledOn={isSelected} + /> + )} diff --git a/protocol-designer/src/pages/Designer/ProtocolSteps/StepForm/StepTools/HeaterShakerTools/index.tsx b/protocol-designer/src/pages/Designer/ProtocolSteps/StepForm/StepTools/HeaterShakerTools/index.tsx index eefc9d36717..e82230adeb0 100644 --- a/protocol-designer/src/pages/Designer/ProtocolSteps/StepForm/StepTools/HeaterShakerTools/index.tsx +++ b/protocol-designer/src/pages/Designer/ProtocolSteps/StepForm/StepTools/HeaterShakerTools/index.tsx @@ -12,9 +12,7 @@ import { } from '@opentrons/components' import { getHeaterShakerLabwareOptions } from '../../../../../../ui/modules/selectors' import { - CheckboxExpandStepFormField, DropdownStepFormField, - InputStepFormField, ToggleExpandStepFormField, ToggleStepFormField, } from '../../../../../../molecules' @@ -90,7 +88,7 @@ export function HeaterShakerTools(props: StepFormProps): JSX.Element { toggleValue={propsForFields.setShake.value} toggleUpdateValue={propsForFields.setShake.updateValue} title={t('form:step_edit_form.field.heaterShaker.shaker.setShake')} - fieldTitle={t('protocol_steps:shake')} + fieldTitle={t('protocol_steps:speed')} isSelected={formData.setShake === true} units={t('units.rpm')} onLabel={t('form:step_edit_form.field.heaterShaker.shaker.toggleOn')} @@ -112,25 +110,17 @@ export function HeaterShakerTools(props: StepFormProps): JSX.Element { : null } /> - - {/* TODO: wire up the new timer with the combined field */} - {formData.heaterShakerSetTimer === true ? ( - - ) : null} - + fieldTitle={t('form:step_edit_form.field.heaterShaker.duration')} + isSelected={formData.heaterShakerSetTimer === true} + units={t('application:units.time')} + /> ) diff --git a/protocol-designer/src/pages/Designer/ProtocolSteps/StepForm/StepTools/MagnetTools/index.tsx b/protocol-designer/src/pages/Designer/ProtocolSteps/StepForm/StepTools/MagnetTools/index.tsx index e98727dd045..42768144177 100644 --- a/protocol-designer/src/pages/Designer/ProtocolSteps/StepForm/StepTools/MagnetTools/index.tsx +++ b/protocol-designer/src/pages/Designer/ProtocolSteps/StepForm/StepTools/MagnetTools/index.tsx @@ -123,7 +123,6 @@ export function MagnetTools(props: StepFormProps): JSX.Element { 'form:step_edit_form.field.magnetAction.options.disengage' )} caption={engageHeightCaption} - islabel={true} /> diff --git a/protocol-designer/src/pages/Designer/ProtocolSteps/StepForm/StepTools/PauseTools/index.tsx b/protocol-designer/src/pages/Designer/ProtocolSteps/StepForm/StepTools/PauseTools/index.tsx index e76153a102e..1ab9d90ce44 100644 --- a/protocol-designer/src/pages/Designer/ProtocolSteps/StepForm/StepTools/PauseTools/index.tsx +++ b/protocol-designer/src/pages/Designer/ProtocolSteps/StepForm/StepTools/PauseTools/index.tsx @@ -139,10 +139,11 @@ export function PauseTools(props: StepFormProps): JSX.Element { > @@ -193,7 +194,7 @@ export function PauseTools(props: StepFormProps): JSX.Element { gridGap={SPACING.spacing4} paddingX={SPACING.spacing16} > - + {i18n.format( t('form:step_edit_form.field.pauseMessage.label'), 'capitalize' diff --git a/protocol-designer/src/pages/Designer/ProtocolSteps/StepForm/index.tsx b/protocol-designer/src/pages/Designer/ProtocolSteps/StepForm/index.tsx index 0272a35e618..0b7049bd546 100644 --- a/protocol-designer/src/pages/Designer/ProtocolSteps/StepForm/index.tsx +++ b/protocol-designer/src/pages/Designer/ProtocolSteps/StepForm/index.tsx @@ -180,7 +180,7 @@ function StepFormManager(props: StepFormManagerProps): JSX.Element | null { : '' } handleCancelClick={saveStepForm} - handleContinueClick={confirmAddPauseUntilTempStep} + handleContinueClick={handleSave} moduleType={ showAddPauseUntilTempStepModal ? TEMPERATURE_MODULE_TYPE diff --git a/protocol-designer/src/pages/Designer/ProtocolSteps/StepSummary.tsx b/protocol-designer/src/pages/Designer/ProtocolSteps/StepSummary.tsx index 0d78ea5dc08..18938c3975b 100644 --- a/protocol-designer/src/pages/Designer/ProtocolSteps/StepSummary.tsx +++ b/protocol-designer/src/pages/Designer/ProtocolSteps/StepSummary.tsx @@ -327,8 +327,7 @@ export function StepSummary(props: StepSummaryProps): JSX.Element | null { case 'heaterShaker': const { latchOpen, - heaterShakerTimerMinutes, - heaterShakerTimerSeconds, + heaterShakerTimer, moduleId: heaterShakerModuleId, targetHeaterShakerTemperature, targetSpeed, @@ -343,14 +342,14 @@ export function StepSummary(props: StepSummaryProps): JSX.Element | null { i18nKey="protocol_steps:heater_shaker.active.temperature" values={{ module: moduleDisplayName }} tagText={ - targetHeaterShakerTemperature != null + targetHeaterShakerTemperature ? `${targetHeaterShakerTemperature}${t( 'application:units.degrees' )}` : t('protocol_steps:heater_shaker.active.ambient') } /> - {targetSpeed != null ? ( + {targetSpeed ? ( - {heaterShakerTimerMinutes != null && - heaterShakerTimerSeconds != null ? ( + {heaterShakerTimer ? ( ) : null} !value ? FIELD_ERRORS.REQUIRED : null export const isTimeFormat: ErrorChecker = (value: unknown): string | null => { const timeRegex = new RegExp(/^\d{1,2}:\d{1,2}:\d{1,2}$/g) - return (typeof value === 'string' && timeRegex.test(value)) || value == null + return (typeof value === 'string' && timeRegex.test(value)) || !value ? null : FIELD_ERRORS.BAD_TIME_HMS } @@ -43,7 +43,7 @@ export const isTimeFormatMinutesSeconds: ErrorChecker = ( value: unknown ): string | null => { const timeRegex = new RegExp(/^\d{1,2}:\d{1,2}$/g) - return (typeof value === 'string' && timeRegex.test(value)) || value == null + return (typeof value === 'string' && timeRegex.test(value)) || !value ? null : FIELD_ERRORS.BAD_TIME_MS } diff --git a/protocol-designer/src/steplist/fieldLevel/index.ts b/protocol-designer/src/steplist/fieldLevel/index.ts index b9367adaa60..96e04bfed07 100644 --- a/protocol-designer/src/steplist/fieldLevel/index.ts +++ b/protocol-designer/src/steplist/fieldLevel/index.ts @@ -8,6 +8,7 @@ import { temperatureRangeFieldValue, realNumber, isTimeFormat, + isTimeFormatMinutesSeconds, } from './errors' import { maskToInteger, @@ -345,6 +346,11 @@ const stepFieldHelperMap: Record = { maskValue: composeMaskers(maskToInteger, onlyPositiveNumbers), castValue: Number, }, + heaterShakerTimer: { + maskValue: composeMaskers(maskToTime), + getErrors: composeErrors(isTimeFormatMinutesSeconds), + castValue: String, + }, pauseAction: { getErrors: composeErrors(requiredField), }, diff --git a/protocol-designer/src/steplist/formLevel/errors.ts b/protocol-designer/src/steplist/formLevel/errors.ts index ada2cef64fc..e5203cdc84e 100644 --- a/protocol-designer/src/steplist/formLevel/errors.ts +++ b/protocol-designer/src/steplist/formLevel/errors.ts @@ -15,7 +15,7 @@ import { import { getPipetteCapacity } from '../../pipettes/pipetteData' import { canPipetteUseLabware } from '../../utils' import { getWellRatio } from '../utils' -import { getTimeFromPauseForm } from '../utils/getTimeFromPauseForm' +import { getTimeFromForm } from '../utils/getTimeFromForm' import type { LabwareDefinition2, PipetteV2Specs } from '@opentrons/shared-data' import type { LabwareEntities, PipetteEntity } from '@opentrons/step-generation' @@ -137,6 +137,22 @@ const LID_TEMPERATURE_HOLD_REQUIRED: FormError = { title: 'Temperature is required', dependentFields: ['lidIsActiveHold', 'lidTargetTempHold'], } +const SHAKE_SPEED_REQUIRED: FormError = { + title: 'Shake speed required', + dependentFields: ['setShake', 'targetSpeed'], +} +const SHAKE_TIME_REQUIRED: FormError = { + title: 'Shake duration required', + dependentFields: ['heaterShakerSetTimer', 'heaterShakerTimer'], +} +const HS_TEMPERATURE_REQUIRED: FormError = { + title: 'Temperature required', + dependentFields: [ + 'targetHeaterShakerTemperature', + 'setHeaterShakerTemperature', + ], +} + export interface HydratedFormData { [key: string]: any } @@ -198,7 +214,13 @@ export const pauseForTimeOrUntilTold = ( const { pauseAction, moduleId, pauseTemperature } = fields if (pauseAction === PAUSE_UNTIL_TIME) { - const { hours, minutes, seconds } = getTimeFromPauseForm(fields) + const { hours, minutes, seconds } = getTimeFromForm( + fields, + 'pauseTime', + 'pauseSeconds', + 'pauseMinutes', + 'pauseSeconds' + ) // user selected pause for amount of time const totalSeconds = hours * 3600 + minutes * 60 + seconds return totalSeconds <= 0 ? TIME_PARAM_REQUIRED : null @@ -342,6 +364,26 @@ export const lidTemperatureHoldRequired = ( ? LID_TEMPERATURE_HOLD_REQUIRED : null } +export const shakeSpeedRequired = ( + fields: HydratedFormData +): FormError | null => { + const { targetSpeed, setShake } = fields + return setShake && !targetSpeed ? SHAKE_SPEED_REQUIRED : null +} +export const shakeTimeRequired = ( + fields: HydratedFormData +): FormError | null => { + const { heaterShakerTimer, heaterShakerSetTimer } = fields + return heaterShakerSetTimer && !heaterShakerTimer ? SHAKE_TIME_REQUIRED : null +} +export const temperatureRequired = ( + fields: HydratedFormData +): FormError | null => { + const { setHeaterShakerTemperature, targetHeaterShakerTemperature } = fields + return setHeaterShakerTemperature && !targetHeaterShakerTemperature + ? HS_TEMPERATURE_REQUIRED + : null +} export const engageHeightRangeExceeded = ( fields: HydratedFormData ): FormError | null => { diff --git a/protocol-designer/src/steplist/formLevel/getDefaultsForStepType.ts b/protocol-designer/src/steplist/formLevel/getDefaultsForStepType.ts index b669b865e4e..40b35b3ccad 100644 --- a/protocol-designer/src/steplist/formLevel/getDefaultsForStepType.ts +++ b/protocol-designer/src/steplist/formLevel/getDefaultsForStepType.ts @@ -120,6 +120,7 @@ export function getDefaultsForStepType( return { moduleId: null, pauseAction: null, + // TODO: (nd: 10/23/2024) remove individual time unit fields pauseHour: null, pauseMessage: '', pauseMinute: null, @@ -151,8 +152,10 @@ export function getDefaultsForStepType( case 'heaterShaker': return { heaterShakerSetTimer: null, + // TODO: (nd: 10/23/2024) remove individual time unit fields heaterShakerTimerMinutes: null, heaterShakerTimerSeconds: null, + heaterShakerTimer: null, latchOpen: false, moduleId: null, setHeaterShakerTemperature: null, diff --git a/protocol-designer/src/steplist/formLevel/index.ts b/protocol-designer/src/steplist/formLevel/index.ts index efa9334315e..1d67206c82b 100644 --- a/protocol-designer/src/steplist/formLevel/index.ts +++ b/protocol-designer/src/steplist/formLevel/index.ts @@ -17,6 +17,9 @@ import { blockTemperatureHoldRequired, lidTemperatureHoldRequired, volumeTooHigh, + shakeSpeedRequired, + temperatureRequired, + shakeTimeRequired, } from './errors' import { @@ -51,6 +54,13 @@ interface FormHelpers { getWarnings?: (arg: unknown) => FormWarning[] } const stepFormHelperMap: Partial> = { + heaterShaker: { + getErrors: composeErrors( + shakeSpeedRequired, + shakeTimeRequired, + temperatureRequired + ), + }, mix: { getErrors: composeErrors(incompatibleLabware, volumeTooHigh), getWarnings: composeWarnings( diff --git a/protocol-designer/src/steplist/formLevel/stepFormToArgs/heaterShakerFormToArgs.ts b/protocol-designer/src/steplist/formLevel/stepFormToArgs/heaterShakerFormToArgs.ts index 8366864f190..eda1e073fb2 100644 --- a/protocol-designer/src/steplist/formLevel/stepFormToArgs/heaterShakerFormToArgs.ts +++ b/protocol-designer/src/steplist/formLevel/stepFormToArgs/heaterShakerFormToArgs.ts @@ -1,3 +1,4 @@ +import { getTimeFromForm } from '../../utils/getTimeFromForm' import type { HeaterShakerArgs } from '@opentrons/step-generation' import type { HydratedHeaterShakerFormData } from '../../../form-types' @@ -22,6 +23,14 @@ export const heaterShakerFormToArgs = ( setShake ? !Number.isNaN(targetSpeed) : true, 'heaterShakerFormToArgs expected targeShake to be a number when setShake is true' ) + const { minutes, seconds } = getTimeFromForm( + formData, + 'heaterShakerTimer', + 'heaterShakerTimerSeconds', + 'heaterShakerTimerMinutes' + ) + + const isNullTime = minutes === 0 && seconds === 0 const targetTemperature = setHeaterShakerTemperature && targetHeaterShakerTemperature != null @@ -36,13 +45,7 @@ export const heaterShakerFormToArgs = ( targetTemperature: targetTemperature, rpm: targetShake, latchOpen: latchOpen, - timerMinutes: - formData.heaterShakerTimerMinutes != null - ? parseInt(formData.heaterShakerTimerMinutes) - : null, - timerSeconds: - formData.heaterShakerTimerSeconds != null - ? parseInt(formData.heaterShakerTimerSeconds) - : null, + timerMinutes: isNullTime ? null : minutes, + timerSeconds: isNullTime ? null : seconds, } } diff --git a/protocol-designer/src/steplist/formLevel/stepFormToArgs/pauseFormToArgs.ts b/protocol-designer/src/steplist/formLevel/stepFormToArgs/pauseFormToArgs.ts index cc7a32a13e3..88de52bacec 100644 --- a/protocol-designer/src/steplist/formLevel/stepFormToArgs/pauseFormToArgs.ts +++ b/protocol-designer/src/steplist/formLevel/stepFormToArgs/pauseFormToArgs.ts @@ -1,4 +1,4 @@ -import { getTimeFromPauseForm } from '../../utils/getTimeFromPauseForm' +import { getTimeFromForm } from '../../utils/getTimeFromForm' import { PAUSE_UNTIL_TIME, PAUSE_UNTIL_TEMP, @@ -13,8 +13,14 @@ import type { export const pauseFormToArgs = ( formData: FormData ): PauseArgs | WaitForTemperatureArgs | null => { - const { hours, minutes, seconds } = getTimeFromPauseForm(formData) - const totalSeconds = hours * 3600 + minutes * 60 + seconds + const { hours, minutes, seconds } = getTimeFromForm( + formData, + 'pauseTime', + 'pauseSecond', + 'pauseMinute', + 'pauseHour' + ) + const totalSeconds = (hours ?? 0) * 3600 + minutes * 60 + seconds const temperature = parseFloat(formData.pauseTemperature as string) const message = formData.pauseMessage ?? '' diff --git a/protocol-designer/src/steplist/formLevel/test/getDefaultsForStepType.test.ts b/protocol-designer/src/steplist/formLevel/test/getDefaultsForStepType.test.ts index 5af511e500d..ba0897607ac 100644 --- a/protocol-designer/src/steplist/formLevel/test/getDefaultsForStepType.test.ts +++ b/protocol-designer/src/steplist/formLevel/test/getDefaultsForStepType.test.ts @@ -164,6 +164,7 @@ describe('getDefaultsForStepType', () => { heaterShakerSetTimer: null, heaterShakerTimerMinutes: null, heaterShakerTimerSeconds: null, + heaterShakerTimer: null, }) }) }) diff --git a/protocol-designer/src/steplist/utils/getTimeFromPauseForm.ts b/protocol-designer/src/steplist/utils/getTimeFromForm.ts similarity index 62% rename from protocol-designer/src/steplist/utils/getTimeFromPauseForm.ts rename to protocol-designer/src/steplist/utils/getTimeFromForm.ts index ed1b2243b49..ff35bdaeb09 100644 --- a/protocol-designer/src/steplist/utils/getTimeFromPauseForm.ts +++ b/protocol-designer/src/steplist/utils/getTimeFromForm.ts @@ -4,28 +4,33 @@ import type { HydratedFormData } from '../formLevel/errors' const TIME_DELIMITER = ':' interface TimeData { - hours: number minutes: number seconds: number + hours: number } -export const getTimeFromPauseForm = ( - formData: FormData | HydratedFormData +export const getTimeFromForm = ( + formData: FormData | HydratedFormData, + timeField: string, + secondsField: string, + minutesField: string, + hoursField?: string ): TimeData => { let hoursFromForm let minutesFromForm let secondsFromForm // importing results in stringified "null" value - if (formData.pauseTime != null && formData.pauseTime !== 'null') { - const timeSplit = formData.pauseTime.split(TIME_DELIMITER) - ;[hoursFromForm, minutesFromForm, secondsFromForm] = timeSplit + if (formData[timeField] != null && formData[timeField] !== 'null') { + const timeSplit = formData[timeField].split(TIME_DELIMITER) + ;[hoursFromForm, minutesFromForm, secondsFromForm] = + timeSplit.length === 3 ? timeSplit : [0, ...timeSplit] } else { // TODO (nd 09/23/2024): remove individual time units after redesign FF is removed ;[hoursFromForm, minutesFromForm, secondsFromForm] = [ - formData.pauseHour, - formData.pauseMinute, - formData.pauseSecond, + hoursField != null ? formData[hoursField] : null, + formData[minutesField], + formData[secondsField], ] } const hours = isNaN(parseFloat(hoursFromForm as string))