diff --git a/app/src/organisms/Devices/RobotSettings/AdvancedTab/AdvancedTabSlideouts/FactoryModeSlideout.tsx b/app/src/organisms/Devices/RobotSettings/AdvancedTab/AdvancedTabSlideouts/FactoryModeSlideout.tsx index 870f76fde80..1f615e76bc5 100644 --- a/app/src/organisms/Devices/RobotSettings/AdvancedTab/AdvancedTabSlideouts/FactoryModeSlideout.tsx +++ b/app/src/organisms/Devices/RobotSettings/AdvancedTab/AdvancedTabSlideouts/FactoryModeSlideout.tsx @@ -28,6 +28,7 @@ import { FileUpload } from '../../../../../molecules/FileUpload' import { UploadInput } from '../../../../../molecules/UploadInput' import { restartRobot } from '../../../../../redux/robot-admin' +import type { FieldError, Resolver } from 'react-hook-form' import type { RobotSettingsField } from '@opentrons/api-client' import type { Dispatch } from '../../../../../redux/types' @@ -36,10 +37,11 @@ interface FactoryModeSlideoutProps { isRobotBusy: boolean onCloseClick: () => void robotName: string + sn: string | null } interface FormValues { - passwordInput: string + factoryModeInput: string } export function FactoryModeSlideout({ @@ -47,6 +49,7 @@ export function FactoryModeSlideout({ isRobotBusy, onCloseClick, robotName, + sn, }: FactoryModeSlideoutProps): JSX.Element { const { t } = useTranslation(['device_settings', 'shared', 'branded']) @@ -58,6 +61,8 @@ export function FactoryModeSlideout({ ) const isOEMMode = oemModeSetting?.value ?? null + const last = sn?.substring(sn.length - 4) + const [currentStep, setCurrentStep] = React.useState(1) const [toggleValue, setToggleValue] = React.useState(false) const [file, setFile] = React.useState(null) @@ -86,22 +91,54 @@ export function FactoryModeSlideout({ }, }) + const validate = ( + data: FormValues, + errors: Record + ): Record => { + const factoryModeInput = data.factoryModeInput + let errorMessage: string | undefined + if (factoryModeInput !== last) { + errorMessage = t('invalid_password') + } + + const updatedErrors = + errorMessage != null + ? { + ...errors, + factoryModeInput: { + type: 'error', + message: errorMessage, + }, + } + : errors + return updatedErrors + } + + const resolver: Resolver = values => { + let errors = {} + errors = validate(values, errors) + return { values, errors } + } + const { - handleSubmit, + clearErrors, control, formState: { errors }, - trigger, + handleSubmit, } = useForm({ defaultValues: { - passwordInput: '', + factoryModeInput: '', }, + mode: 'onSubmit', + resolver, + reValidateMode: 'onSubmit', }) - const onSubmit = (data: FormValues): void => { + + const onSubmit = (): void => { setCurrentStep(2) } const handleSubmitFactoryPassword = (): void => { - // TODO: validation and errors: PLAT-281 void handleSubmit(onSubmit)() } @@ -153,7 +190,11 @@ export function FactoryModeSlideout({ footer={ <> {currentStep === 1 ? ( - + {t('shared:next')} ) : null} @@ -182,15 +223,15 @@ export function FactoryModeSlideout({ ( ) => { field.onChange(e) - trigger('passwordInput') + clearErrors() }} value={field.value} error={fieldState.error?.message && ' '} @@ -199,13 +240,13 @@ export function FactoryModeSlideout({ /> )} /> - {errors.passwordInput != null ? ( + {errors.factoryModeInput != null ? ( - {errors.passwordInput.message} + {errors.factoryModeInput.message} ) : null} diff --git a/app/src/organisms/Devices/RobotSettings/AdvancedTab/FactoryMode.tsx b/app/src/organisms/Devices/RobotSettings/AdvancedTab/FactoryMode.tsx index 8d2fda7c386..3ca4f7cb7c1 100644 --- a/app/src/organisms/Devices/RobotSettings/AdvancedTab/FactoryMode.tsx +++ b/app/src/organisms/Devices/RobotSettings/AdvancedTab/FactoryMode.tsx @@ -17,11 +17,13 @@ import { TertiaryButton } from '../../../../atoms/buttons' interface FactoryModeProps { isRobotBusy: boolean setShowFactoryModeSlideout: React.Dispatch> + sn: string | null } export function FactoryMode({ isRobotBusy, setShowFactoryModeSlideout, + sn, }: FactoryModeProps): JSX.Element { const { t } = useTranslation('device_settings') @@ -37,7 +39,7 @@ export function FactoryMode({ { setShowFactoryModeSlideout(true) diff --git a/app/src/organisms/Devices/RobotSettings/RobotSettingsAdvanced.tsx b/app/src/organisms/Devices/RobotSettings/RobotSettingsAdvanced.tsx index c497446ba9f..585dbcf69db 100644 --- a/app/src/organisms/Devices/RobotSettings/RobotSettingsAdvanced.tsx +++ b/app/src/organisms/Devices/RobotSettings/RobotSettingsAdvanced.tsx @@ -42,7 +42,7 @@ import { DeviceResetSlideout } from './AdvancedTab/AdvancedTabSlideouts/DeviceRe import { DeviceResetModal } from './AdvancedTab/AdvancedTabSlideouts/DeviceResetModal' import { FactoryModeSlideout } from './AdvancedTab/AdvancedTabSlideouts/FactoryModeSlideout' import { handleUpdateBuildroot } from './UpdateBuildroot' -import { UNREACHABLE } from '../../../redux/discovery' +import { getRobotSerialNumber, UNREACHABLE } from '../../../redux/discovery' import { getTopPortalEl } from '../../../App/portal' import { useIsEstopNotDisengaged } from '../../../resources/devices/hooks/useIsEstopNotDisengaged' @@ -89,6 +89,7 @@ export function RobotSettingsAdvanced({ getRobotSettings(state, robotName) ) const reachable = robot?.status !== UNREACHABLE + const sn = robot?.status != null ? getRobotSerialNumber(robot) : null const [isRobotReachable, setIsRobotReachable] = React.useState( reachable @@ -143,6 +144,7 @@ export function RobotSettingsAdvanced({ isRobotBusy={isRobotBusy || isEstopNotDisengaged} onCloseClick={() => setShowFactoryModeSlideout(false)} robotName={robotName} + sn={sn} /> )} {showDeviceResetSlideout && ( @@ -215,6 +217,7 @@ export function RobotSettingsAdvanced({ ) : null}