From c3cfaf1c9a3b97137683b1d85daeabe79fa1bfb6 Mon Sep 17 00:00:00 2001 From: AaronW Date: Fri, 4 Oct 2024 16:46:18 +0000 Subject: [PATCH 1/8] wrapped in changes from previous merge --- pkg/services/move/move_router.go | 10 + .../servicesCounselingMobileHome.spec.js | 47 +++ .../office/txo/tooFlowsMobileHome.spec.js | 58 +++ .../MobileHomeShipmentInfoList.jsx | 394 ++++++++++++++++++ .../MobileHomeShipmentInfoList.stories.jsx | 99 +++++ .../MobileHomeShipmentInfoList.test.jsx | 123 ++++++ .../ShipmentInfoListSelector.jsx | 16 + .../MobileHomeShipmentForm.jsx | 207 +++++++++ .../MobileHomeShipmentForm.module.scss | 121 ++++++ .../mobileHomeShipmentSchema.js | 108 +++++ .../Office/ShipmentForm/ShipmentForm.jsx | 126 +++++- .../Office/ShipmentForm/ShipmentForm.test.jsx | 183 ++++++++ .../ShipmentFormRemarks.jsx | 4 + src/pages/Office/AddShipment/AddShipment.jsx | 4 + src/pages/Office/MoveDetails/MoveDetails.jsx | 2 + .../ServicesCounselingAddShipment.jsx | 4 + .../ServicesCounselingMoveDetails.jsx | 6 + src/shared/constants.js | 1 + src/utils/formatMtoShipment.js | 73 ++++ 19 files changed, 1568 insertions(+), 18 deletions(-) create mode 100644 playwright/tests/office/servicescounseling/servicesCounselingMobileHome.spec.js create mode 100644 playwright/tests/office/txo/tooFlowsMobileHome.spec.js create mode 100644 src/components/Office/DefinitionLists/MobileHomeShipmentInfoList.jsx create mode 100644 src/components/Office/DefinitionLists/MobileHomeShipmentInfoList.stories.jsx create mode 100644 src/components/Office/DefinitionLists/MobileHomeShipmentInfoList.test.jsx create mode 100644 src/components/Office/ShipmentForm/MobileHomeShipmentForm/MobileHomeShipmentForm.jsx create mode 100644 src/components/Office/ShipmentForm/MobileHomeShipmentForm/MobileHomeShipmentForm.module.scss create mode 100644 src/components/Office/ShipmentForm/MobileHomeShipmentForm/mobileHomeShipmentSchema.js diff --git a/pkg/services/move/move_router.go b/pkg/services/move/move_router.go index 771ed557fa1..9b3ccc44d49 100644 --- a/pkg/services/move/move_router.go +++ b/pkg/services/move/move_router.go @@ -211,6 +211,16 @@ func (router moveRouter) sendToServiceCounselor(appCtx appcontext.AppContext, mo if move.MTOShipments[i].ShipmentType == models.MTOShipmentTypeBoatHaulAway || move.MTOShipments[i].ShipmentType == models.MTOShipmentTypeBoatTowAway { move.MTOShipments[i].Status = models.MTOShipmentStatusSubmitted + if verrs, err := appCtx.DB().ValidateAndUpdate(&move.MTOShipments[i]); verrs.HasAny() || err != nil { + msg := "failure saving shipment when routing move submission" + appCtx.Logger().Error(msg, zap.Error(err)) + return apperror.NewInvalidInputError(move.MTOShipments[i].ID, err, verrs, msg) + } + } + // update status for mobile home shipment + if move.MTOShipments[i].ShipmentType == models.MTOShipmentTypeMobileHome { + move.MTOShipments[i].Status = models.MTOShipmentStatusSubmitted + if verrs, err := appCtx.DB().ValidateAndUpdate(&move.MTOShipments[i]); verrs.HasAny() || err != nil { msg := "failure saving shipment when routing move submission" appCtx.Logger().Error(msg, zap.Error(err)) diff --git a/playwright/tests/office/servicescounseling/servicesCounselingMobileHome.spec.js b/playwright/tests/office/servicescounseling/servicesCounselingMobileHome.spec.js new file mode 100644 index 00000000000..0588fe6cd0d --- /dev/null +++ b/playwright/tests/office/servicescounseling/servicesCounselingMobileHome.spec.js @@ -0,0 +1,47 @@ +// @ts-check +import { test, expect } from './servicesCounselingTestFixture'; + +test.describe('Services counselor user', () => { + test.beforeEach(async ({ scPage }) => { + const move = await scPage.testHarness.buildHHGMoveWithNTSAndNeedsSC(); + await scPage.navigateToMove(move.locator); + }); + + test('Services Counselor can create a mobile home shipment and view shipment card info', async ({ page, scPage }) => { + const deliveryDate = new Date().toLocaleDateString('en-US'); + await page.getByTestId('dropdown').selectOption({ label: 'Mobile Home' }); + + await expect(page.getByRole('heading', { level: 1 })).toHaveText('Add shipment details'); + await expect(page.getByTestId('tag')).toHaveText('Mobile Home'); + + await page.getByLabel('Year').fill('2022'); + await page.getByLabel('Make').fill('make'); + await page.getByLabel('Model').fill('model'); + await page.getByTestId('lengthFeet').fill('22'); + await page.getByTestId('widthFeet').fill('22'); + await page.getByTestId('heightFeet').fill('22'); + + await page.locator('#requestedPickupDate').fill(deliveryDate); + await page.locator('#requestedPickupDate').blur(); + await page.getByText('Use current address').click(); + await page.locator('#requestedDeliveryDate').fill('16 Mar 2022'); + await page.locator('#requestedDeliveryDate').blur(); + + await page.getByLabel('Counselor remarks').fill('Sample counselor remarks'); + + // Save the shipment + await page.getByRole('button', { name: 'Save' }).click(); + await scPage.waitForPage.moveDetails(); + + await expect(page.getByTestId('ShipmentContainer')).toHaveCount(2); + + await expect(page.getByText('Mobile home year')).toBeVisible(); + await expect(page.getByTestId('year')).toHaveText('2022'); + await expect(page.getByText('Mobile home make')).toBeVisible(); + await expect(page.getByTestId('make')).toHaveText('make'); + await expect(page.getByText('Mobile home model')).toBeVisible(); + await expect(page.getByTestId('model')).toHaveText('model'); + await expect(page.getByText('Dimensions')).toBeVisible(); + await expect(page.getByTestId('dimensions')).toHaveText("22' L x 22' W x 22' H"); + }); +}); diff --git a/playwright/tests/office/txo/tooFlowsMobileHome.spec.js b/playwright/tests/office/txo/tooFlowsMobileHome.spec.js new file mode 100644 index 00000000000..0d98f4c5ee7 --- /dev/null +++ b/playwright/tests/office/txo/tooFlowsMobileHome.spec.js @@ -0,0 +1,58 @@ +/** + * Semi-automated converted from a cypress test, and thus may contain + * non best-practices, in particular: heavy use of `page.locator` + * instead of `page.getBy*`. + */ + +// @ts-check +import { test, expect } from '../../utils/office/officeTest'; + +import { TooFlowPage } from './tooTestFixture'; + +test.describe('TOO user', () => { + /** @type {TooFlowPage} */ + let tooFlowPage; + + test.beforeEach(async ({ officePage }) => { + const move = await officePage.testHarness.buildHHGMoveWithServiceItemsAndPaymentRequestsAndFilesForTOO(); + await officePage.signInAsNewTOOUser(); + tooFlowPage = new TooFlowPage(officePage, move); + await tooFlowPage.waitForLoading(); + await officePage.tooNavigateToMove(tooFlowPage.moveLocator); + }); + + test('TOO can create a mobile home shipment and view shipment card info', async ({ page }) => { + const deliveryDate = new Date().toLocaleDateString('en-US'); + await page.getByTestId('dropdown').selectOption({ label: 'Mobile Home' }); + + await expect(page.getByRole('heading', { level: 1 })).toHaveText('Add shipment details'); + await expect(page.getByTestId('tag')).toHaveText('Mobile Home'); + + await page.getByLabel('Year').fill('2022'); + await page.getByLabel('Make').fill('make'); + await page.getByLabel('Model').fill('model'); + await page.getByTestId('lengthFeet').fill('22'); + await page.getByTestId('widthFeet').fill('22'); + await page.getByTestId('heightFeet').fill('22'); + + await page.locator('#requestedPickupDate').fill(deliveryDate); + await page.locator('#requestedPickupDate').blur(); + await page.getByText('Use current address').click(); + await page.locator('#requestedDeliveryDate').fill('16 Mar 2022'); + await page.locator('#requestedDeliveryDate').blur(); + + // Save the shipment + await page.getByRole('button', { name: 'Save' }).click(); + + await expect(page.getByTestId('ShipmentContainer')).toHaveCount(2); + + await expect(page.getByText('Mobile home year')).toBeVisible(); + await expect(page.getByTestId('year')).toHaveText('2022'); + await expect(page.getByText('Mobile home make')).toBeVisible(); + await expect(page.getByTestId('make')).toHaveText('make'); + await expect(page.getByText('Mobile home model')).toBeVisible(); + await expect(page.getByTestId('model')).toHaveText('model'); + await expect(page.getByText('Dimensions')).toBeVisible(); + await expect(page.getByTestId('dimensions')).toHaveText("22' L x 22' W x 22' H"); + }); +}); diff --git a/src/components/Office/DefinitionLists/MobileHomeShipmentInfoList.jsx b/src/components/Office/DefinitionLists/MobileHomeShipmentInfoList.jsx new file mode 100644 index 00000000000..8854c983d75 --- /dev/null +++ b/src/components/Office/DefinitionLists/MobileHomeShipmentInfoList.jsx @@ -0,0 +1,394 @@ +import React, { useEffect, useState } from 'react'; +import * as PropTypes from 'prop-types'; +import classNames from 'classnames'; + +import shipmentDefinitionListsStyles from './ShipmentDefinitionLists.module.scss'; + +import styles from 'styles/descriptionList.module.scss'; +import { formatDateWithUTC } from 'shared/dates'; +import { ShipmentShape } from 'types/shipment'; +import { formatAddress, formatAgent } from 'utils/shipmentDisplay'; +import { convertInchesToFeetAndInches } from 'utils/formatMtoShipment'; +import { + setFlagStyles, + setDisplayFlags, + getDisplayFlags, + getMissingOrDash, + fieldValidationShape, +} from 'utils/displayFlags'; +import { ADDRESS_UPDATE_STATUS } from 'constants/shipments'; +import { isBooleanFlagEnabled } from 'utils/featureFlags'; + +const ShipmentInfoList = ({ + className, + shipment, + warnIfMissing, + errorIfMissing, + showWhenCollapsed, + isExpanded, + isForEvaluationReport, +}) => { + const { + actualPickupDate, + requestedPickupDate, + requiredDeliveryDate, + scheduledDeliveryDate, + requestedDeliveryDate, + scheduledPickupDate, + actualDeliveryDate, + pickupAddress, + secondaryPickupAddress, + tertiaryPickupAddress, + destinationAddress, + destinationType, + displayDestinationType, + secondaryDeliveryAddress, + tertiaryDeliveryAddress, + mtoAgents, + counselorRemarks, + customerRemarks, + deliveryAddressUpdate, + } = shipment; + + const { year, make, model, lengthInInches, widthInInches, heightInInches } = shipment?.mobileHomeShipment || {}; + + setFlagStyles({ + row: styles.row, + warning: shipmentDefinitionListsStyles.warning, + missingInfoError: shipmentDefinitionListsStyles.missingInfoError, + }); + setDisplayFlags(errorIfMissing, warnIfMissing, showWhenCollapsed, null, shipment); + + const [isTertiaryAddressEnabled, setIsTertiaryAddressEnabled] = useState(false); + useEffect(() => { + const fetchData = async () => { + setIsTertiaryAddressEnabled(await isBooleanFlagEnabled('third_address_available')); + }; + if (!isForEvaluationReport) fetchData(); + }, [isForEvaluationReport]); + + const showElement = (elementFlags) => { + return (isExpanded || elementFlags.alwaysShow) && !elementFlags.hideRow; + }; + + const length = convertInchesToFeetAndInches(lengthInInches); + const width = convertInchesToFeetAndInches(widthInInches); + const height = convertInchesToFeetAndInches(heightInInches); + + const formattedDimensions = `${length?.feet}'${length?.inches > 0 ? ` ${length.inches}"` : ''} L x ${width?.feet}'${ + width?.inches > 0 ? ` ${width.inches}"` : '' + } W x ${height?.feet}'${height?.inches > 0 ? ` ${height.inches}"` : ''} H`; + + const releasingAgent = mtoAgents ? mtoAgents.find((agent) => agent.agentType === 'RELEASING_AGENT') : false; + const receivingAgent = mtoAgents ? mtoAgents.find((agent) => agent.agentType === 'RECEIVING_AGENT') : false; + + const agentsElementFlags = getDisplayFlags('mtoAgents'); + const releasingAgentElement = !releasingAgent ? ( +
+
Releasing agent
+
+
+ ) : ( +
+
Releasing agent
+
{formatAgent(releasingAgent)}
+
+ ); + + const receivingAgentElement = !receivingAgent ? ( +
+
Receiving agent
+
+
+ ) : ( +
+
Receiving agent
+
{formatAgent(receivingAgent)}
+
+ ); + + const scheduledPickupDateElementFlags = getDisplayFlags('scheduledPickupDate'); + const scheduledPickupDateElement = ( +
+
Scheduled pickup date
+
+ {(scheduledPickupDate && formatDateWithUTC(scheduledPickupDate, 'DD MMM YYYY')) || + getMissingOrDash('scheduledPickupDate')} +
+
+ ); + + const requestedPickupDateElementFlags = getDisplayFlags('scheduledPickupDate'); + const requestedPickupDateElement = ( +
+
Requested pickup date
+
+ {(requestedPickupDate && formatDateWithUTC(requestedPickupDate, 'DD MMM YYYY')) || + getMissingOrDash('requestedPickupDate')} +
+
+ ); + + const actualPickupDateElementFlags = getDisplayFlags('actualPickupDate'); + const actualPickupDateElement = ( +
+
Actual pickup date
+
+ {(actualPickupDate && formatDateWithUTC(actualPickupDate, 'DD MMM YYYY')) || + getMissingOrDash('actualPickupDate')} +
+
+ ); + + const requestedDeliveryDateElementFlags = getDisplayFlags('requestedDeliveryDate'); + const requestedDeliveryDateElement = ( +
+
Requested delivery date
+
+ {(requestedDeliveryDate && formatDateWithUTC(requestedDeliveryDate, 'DD MMM YYYY')) || + getMissingOrDash('requestedDeliveryDate')} +
+
+ ); + + const scheduledDeliveryDateElementFlags = getDisplayFlags('scheduledDeliveryDate'); + const scheduledDeliveryDateElement = ( +
+
Scheduled delivery date
+
+ {(scheduledDeliveryDate && formatDateWithUTC(scheduledDeliveryDate, 'DD MMM YYYY')) || + getMissingOrDash('scheduledDeliveryDate')} +
+
+ ); + + const requiredDeliveryDateElementFlags = getDisplayFlags('requiredDeliveryDate'); + const requiredDeliveryDateElement = ( +
+
Required delivery date
+
+ {(requiredDeliveryDate && formatDateWithUTC(requiredDeliveryDate, 'DD MMM YYYY')) || + getMissingOrDash('requiredDeliveryDate')} +
+
+ ); + const actualDeliveryDateElementFlags = getDisplayFlags('actualDeliveryDate'); + const actualDeliveryDateElement = ( +
+
Actual delivery date
+
+ {(actualDeliveryDate && formatDateWithUTC(actualDeliveryDate, 'DD MMM YYYY')) || + getMissingOrDash('actualDeliverDate')} +
+
+ ); + + const pickupAddressElementFlags = getDisplayFlags('pickupAddress'); + const pickupAddressElement = ( +
+
Origin address
+
+ {(pickupAddress && formatAddress(pickupAddress)) || getMissingOrDash('pickupAddress')} +
+
+ ); + + const secondaryPickupAddressElementFlags = getDisplayFlags('secondaryPickupAddress'); + const secondaryPickupAddressElement = ( +
+
Second pickup address
+
+ {secondaryPickupAddress ? formatAddress(secondaryPickupAddress) : '—'} +
+
+ ); + + const tertiaryPickupAddressElementFlags = getDisplayFlags('tertiaryPickupAddress'); + const tertiaryPickupAddressElement = ( +
+
Third pickup address
+
{tertiaryPickupAddress ? formatAddress(tertiaryPickupAddress) : '—'}
+
+ ); + + const destinationTypeFlags = getDisplayFlags('destinationType'); + const destinationTypeElement = ( +
+
Destination type
+
{destinationType || getMissingOrDash('destinationType')}
+
+ ); + + const destinationAddressElementFlags = getDisplayFlags('destinationAddress'); + const destinationAddressElement = ( +
+
Destination address
+
+ {deliveryAddressUpdate?.status === ADDRESS_UPDATE_STATUS.REQUESTED + ? 'Review required' + : (destinationAddress && formatAddress(destinationAddress)) || '—'} +
+
+ ); + + const secondaryDeliveryAddressElementFlags = getDisplayFlags('secondaryDeliveryAddress'); + const secondaryDeliveryAddressElement = ( +
+
Second destination address
+
+ {secondaryDeliveryAddress ? formatAddress(secondaryDeliveryAddress) : '—'} +
+
+ ); + + const tertiaryDeliveryAddressElementFlags = getDisplayFlags('tertiaryDeliveryAddress'); + const tertiaryDeliveryAddressElement = ( +
+
Third destination address
+
+ {tertiaryDeliveryAddress ? formatAddress(tertiaryDeliveryAddress) : '—'} +
+
+ ); + + const yearElementFlags = getDisplayFlags('year'); + const yearElement = ( +
+
Mobile home year
+
{year}
+
+ ); + + const makeElementFlags = getDisplayFlags('make'); + const makeElement = ( +
+
Mobile home make
+
{make}
+
+ ); + + const modelElementFlags = getDisplayFlags('model'); + const modelElement = ( +
+
Mobile home model
+
{model}
+
+ ); + + const dimensionsElementFlags = getDisplayFlags('dimensions'); + const dimensionsElement = ( +
+
Dimensions
+
{formattedDimensions}
+
+ ); + + const counselorRemarksElementFlags = getDisplayFlags('counselorRemarks'); + const counselorRemarksElement = ( +
+
Counselor remarks
+
{counselorRemarks || '—'}
+
+ ); + + const customerRemarksElementFlags = getDisplayFlags('customerRemarks'); + const customerRemarksElement = ( +
+
Customer remarks
+
{customerRemarks || '—'}
+
+ ); + + const defaultDetails = ( +
+ {requestedPickupDateElement} + {pickupAddressElement} + {secondaryPickupAddressElement} + {isTertiaryAddressEnabled ? tertiaryPickupAddressElement : null} + {showElement(agentsElementFlags) && releasingAgentElement} + {showElement(requestedDeliveryDateElementFlags) && requestedDeliveryDateElement} + {requestedDeliveryDateElement} + {destinationAddressElement} + {showElement(destinationTypeFlags) && displayDestinationType && destinationTypeElement} + {secondaryDeliveryAddressElement} + {isTertiaryAddressEnabled ? tertiaryDeliveryAddressElement : null} + {showElement(agentsElementFlags) && receivingAgentElement} + {yearElement} + {makeElement} + {modelElement} + {dimensionsElement} + {counselorRemarksElement} + {customerRemarksElement} +
+ ); + + const evaluationReportDetails = ( +
+
+
+ {showElement(scheduledPickupDateElement) && scheduledPickupDateElement} + {showElement(actualPickupDateElementFlags) && actualPickupDateElement} + {showElement(requestedDeliveryDateElementFlags) && requestedDeliveryDateElement} + {showElement(agentsElementFlags) && releasingAgentElement} +
+
+
+
+ {showElement(scheduledDeliveryDateElementFlags) && scheduledDeliveryDateElement} + {showElement(requiredDeliveryDateElementFlags) && requiredDeliveryDateElement} + {showElement(actualDeliveryDateElementFlags) && actualDeliveryDateElement} + {showElement(agentsElementFlags) && receivingAgentElement} +
+
+
+ ); + + return
{isForEvaluationReport ? evaluationReportDetails : defaultDetails}
; +}; + +ShipmentInfoList.propTypes = { + className: PropTypes.string, + shipment: ShipmentShape.isRequired, + warnIfMissing: PropTypes.arrayOf(fieldValidationShape), + errorIfMissing: PropTypes.arrayOf(fieldValidationShape), + showWhenCollapsed: PropTypes.arrayOf(PropTypes.string), + isExpanded: PropTypes.bool, +}; + +ShipmentInfoList.defaultProps = { + className: '', + warnIfMissing: [], + errorIfMissing: [], + showWhenCollapsed: [], + isExpanded: false, +}; + +export default ShipmentInfoList; diff --git a/src/components/Office/DefinitionLists/MobileHomeShipmentInfoList.stories.jsx b/src/components/Office/DefinitionLists/MobileHomeShipmentInfoList.stories.jsx new file mode 100644 index 00000000000..735c38d3ff4 --- /dev/null +++ b/src/components/Office/DefinitionLists/MobileHomeShipmentInfoList.stories.jsx @@ -0,0 +1,99 @@ +import React from 'react'; +import { object, text } from '@storybook/addon-knobs'; + +import MobileHomeShipmentInfoList from './MobileHomeShipmentInfoList'; + +export default { + title: 'Office Components/Mobile Home Shipment Info List', + component: MobileHomeShipmentInfoList, +}; + +const mobileHomeShipment = { + mobileHomeShipment: { + year: 2020, + make: 'Yamaha', + model: '242X E-Series', + lengthInInches: 276, + widthInInches: 102, + heightInInches: 120, + }, + pickupAddress: { + streetAddress1: '123 Harbor Dr', + city: 'Miami', + state: 'FL', + postalCode: '33101', + }, + destinationAddress: { + streetAddress1: '456 Marina Blvd', + city: 'Key West', + state: 'FL', + postalCode: '33040', + }, + secondaryPickupAddress: { + streetAddress1: '789 Seaport Ln', + city: 'Fort Lauderdale', + state: 'FL', + postalCode: '33316', + }, + tertiaryPickupAddress: { + streetAddress1: '101 Yacht Club Rd', + city: 'Naples', + state: 'FL', + postalCode: '34102', + }, + secondaryDeliveryAddress: { + streetAddress1: '111 Ocean Dr', + city: 'Palm Beach', + state: 'FL', + postalCode: '33480', + }, + tertiaryDeliveryAddress: { + streetAddress1: '222 Shoreline Dr', + city: 'Clearwater', + state: 'FL', + postalCode: '33767', + }, + mtoAgents: [ + { + agentType: 'RELEASING_AGENT', + firstName: 'John', + lastName: 'Doe', + phone: '123-456-7890', + email: 'john.doe@example.com', + }, + { + agentType: 'RECEIVING_AGENT', + firstName: 'Jane', + lastName: 'Doe', + phone: '987-654-3210', + email: 'jane.doe@example.com', + }, + ], + counselorRemarks: 'Please be cautious with the mobile home.', + customerRemarks: 'Handle with care.', +}; + +export const Basic = () => ( + +); + +export const DefaultView = () => ( + +); diff --git a/src/components/Office/DefinitionLists/MobileHomeShipmentInfoList.test.jsx b/src/components/Office/DefinitionLists/MobileHomeShipmentInfoList.test.jsx new file mode 100644 index 00000000000..3d50715bc35 --- /dev/null +++ b/src/components/Office/DefinitionLists/MobileHomeShipmentInfoList.test.jsx @@ -0,0 +1,123 @@ +import React from 'react'; +import { render, screen, within, act } from '@testing-library/react'; + +import MobileHomeShipmentInfoList from './MobileHomeShipmentInfoList'; + +import { isBooleanFlagEnabled } from 'utils/featureFlags'; + +jest.mock('utils/featureFlags', () => ({ + ...jest.requireActual('utils/featureFlags'), + isBooleanFlagEnabled: jest.fn().mockImplementation(() => Promise.resolve(false)), +})); + +const shipment = { + mobileHomeShipment: { + year: 2022, + make: 'Fleetwood', + model: 'Southwind', + lengthInInches: 3600, + widthInInches: 1020, + heightInInches: 1440, + }, + requestedPickupDate: '2020-03-26', + pickupAddress: { + streetAddress1: '123 Harbor Dr', + city: 'Miami', + state: 'FL', + postalCode: '33101', + }, + destinationAddress: { + streetAddress1: '456 Marina Blvd', + city: 'Key West', + state: 'FL', + postalCode: '33040', + }, + mtoAgents: [ + { + agentType: 'RELEASING_AGENT', + firstName: 'John', + lastName: 'Doe', + phone: '123-456-7890', + email: 'john.doe@example.com', + }, + { + agentType: 'RECEIVING_AGENT', + firstName: 'Jane', + lastName: 'Smith', + phone: '987-654-3210', + email: 'jane.smith@example.com', + }, + ], + counselorRemarks: 'Handle with care.', + customerRemarks: 'Please avoid scratches.', +}; + +const labels = { + requestedPickupDate: 'Requested pickup date', + pickupAddress: 'Origin address', + destinationAddress: 'Destination address', + mtoAgents: ['Releasing agent', 'Receiving agent'], + counselorRemarks: 'Counselor remarks', + customerRemarks: 'Customer remarks', + dimensions: 'Dimensions', +}; + +describe('Shipment Info List - Mobile Home Shipment', () => { + it('renders all mobile home shipment fields when provided and expanded', async () => { + isBooleanFlagEnabled.mockImplementation(() => Promise.resolve(true)); + + await act(async () => { + render(); + }); + + const requestedPickupDate = screen.getByText(labels.requestedPickupDate); + expect(within(requestedPickupDate.parentElement).getByText('26 Mar 2020')).toBeInTheDocument(); + + const pickupAddress = screen.getByText(labels.pickupAddress); + expect( + within(pickupAddress.parentElement).getByText(shipment.pickupAddress.streetAddress1, { exact: false }), + ).toBeInTheDocument(); + + const destinationAddress = screen.getByText(labels.destinationAddress); + expect( + within(destinationAddress.parentElement).getByText(shipment.destinationAddress.streetAddress1, { + exact: false, + }), + ).toBeInTheDocument(); + + const releasingAgent = screen.getByText(labels.mtoAgents[0]); + expect( + within(releasingAgent.parentElement).getByText(shipment.mtoAgents[0].email, { exact: false }), + ).toBeInTheDocument(); + + const receivingAgent = screen.getByText(labels.mtoAgents[1]); + expect( + within(receivingAgent.parentElement).getByText(shipment.mtoAgents[1].email, { exact: false }), + ).toBeInTheDocument(); + + const counselorRemarks = screen.getByText(labels.counselorRemarks); + expect(within(counselorRemarks.parentElement).getByText(shipment.counselorRemarks)).toBeInTheDocument(); + + const customerRemarks = screen.getByText(labels.customerRemarks); + expect(within(customerRemarks.parentElement).getByText(shipment.customerRemarks)).toBeInTheDocument(); + + const dimensions = screen.getByText(labels.dimensions); + expect(within(dimensions.parentElement).getByText("300' L x 85' W x 120' H", { exact: false })).toBeInTheDocument(); + }); + + it('does not render mtoAgents when not provided', async () => { + await act(async () => { + render( + , + ); + }); + + expect(screen.queryByText(labels.mtoAgents[0])).not.toBeInTheDocument(); + expect(screen.queryByText(labels.mtoAgents[1])).not.toBeInTheDocument(); + }); +}); diff --git a/src/components/Office/DefinitionLists/ShipmentInfoListSelector.jsx b/src/components/Office/DefinitionLists/ShipmentInfoListSelector.jsx index 20d4638f06a..26158988a45 100644 --- a/src/components/Office/DefinitionLists/ShipmentInfoListSelector.jsx +++ b/src/components/Office/DefinitionLists/ShipmentInfoListSelector.jsx @@ -1,11 +1,13 @@ import React from 'react'; import * as PropTypes from 'prop-types'; +// import BoatShipmentInfoList from 'components/Office/DefinitionLists/BoatShipmentInfoList'; import { ShipmentShape } from 'types/shipment'; import ShipmentInfoList from 'components/Office/DefinitionLists/ShipmentInfoList'; import PPMShipmentInfoList from 'components/Office/DefinitionLists/PPMShipmentInfoList'; import NTSRShipmentInfoList from 'components/Office/DefinitionLists/NTSRShipmentInfoList'; import NTSShipmentInfoList from 'components/Office/DefinitionLists/NTSShipmentInfoList'; +import MobileHomeShipmentInfoList from 'components/Office/DefinitionLists/MobileHomeShipmentInfoList'; import { SHIPMENT_OPTIONS } from 'shared/constants'; import { fieldValidationShape } from 'utils/displayFlags'; @@ -78,6 +80,20 @@ const ShipmentInfoListSelector = ({ isForEvaluationReport={isForEvaluationReport} /> ); + case SHIPMENT_OPTIONS.MOBILE_HOME: + return ( + + ); default: return ( { + return ( +
+ +

Mobile Home Information

+
+
+ { + setFieldError('year', null); + }} + onBlur={() => { + setFieldTouched('year', true); + setFieldError('year', null); + validateForm(); + }} + required + /> +
+
+
+ + +
+
+ +

Mobile Home Dimensions

+

Enter the total outside dimensions of the mobile home.

+
+ +

+ The dimensions do not meet the requirements for a mobile home shipment. Please cancel and select a + different shipment type. +

+
+
+
+ Length + Required +
+
+
+ { + setFieldError('heightFeet', null); + setFieldError('widthFeet', null); + setFieldError('lengthFeet', null); + }} + /> +
+
+ +
+
+
+
+
+ Width + Required +
+
+
+ { + setFieldError('heightFeet', null); + setFieldError('widthFeet', null); + setFieldError('lengthFeet', null); + }} + /> +
+
+ +
+
+
+
+
+ Height + Required +
+
+
+ { + setFieldError('heightFeet', null); + setFieldError('widthFeet', null); + setFieldError('lengthFeet', null); + }} + /> +
+
+ +
+
+
+
+
+
+ ); +}; + +export default MobileHomeShipmentForm; + +MobileHomeShipmentForm.propTypes = { + lengthHasError: func.isRequired, + widthHasError: func.isRequired, + heightHasError: func.isRequired, + setFieldTouched: func.isRequired, + setFieldError: func.isRequired, + validateForm: func.isRequired, +}; diff --git a/src/components/Office/ShipmentForm/MobileHomeShipmentForm/MobileHomeShipmentForm.module.scss b/src/components/Office/ShipmentForm/MobileHomeShipmentForm/MobileHomeShipmentForm.module.scss new file mode 100644 index 00000000000..093d03a6cd4 --- /dev/null +++ b/src/components/Office/ShipmentForm/MobileHomeShipmentForm/MobileHomeShipmentForm.module.scss @@ -0,0 +1,121 @@ +@import 'shared/styles/_basics'; +@import 'shared/styles/_variables'; +@import 'shared/styles/colors'; + +.formContainer { + :global(.usa-legend) { + max-width: none; + } + .formTextFieldWrapper { + :global(.usa-form-group){ + margin-top: 0; + } + .hide { + display: none; + } + } + .formFieldContainer { + margin-top: 1.6rem; + margin-bottom: 0; + padding-bottom: 0; + border: none; + } + .form { + max-width: none; + + :global(.usa-input) { + @include u-display('inline-block'); + width: unset; + } + + :global(.usa-form-group--error), + :global(.usa-form-group.warning) { + margin-top: 1.6rem; + } + + :global(.usa-form-group:first-of-type .usa-label) { + margin-top: 0; + } + + // last section wrapper on mobile shouldn't render divider + @include at-media-max('tablet') { + .sectionWrapper:nth-last-child(2) { + border-bottom: none; + @include u-padding-bottom(0); + @include u-margin-bottom(3); + } + } + } + + h2 { + @include u-margin-bottom(2.5); + } + + // fixes collapsing margins cross-browser for Storage section + h2 + fieldset legend { + @include u-padding-top(1.5); + } + + .sectionWrapper { + border-bottom: 1px solid $base-lighter; + + @include at-media-max('tablet') { + @include u-padding-bottom(4); + @include u-margin-top(4); + } + } + + .sectionWrapper:last-of-type { + border-bottom: none; + } + + fieldset { + @include u-margin-top(2); + @include u-padding-top(0); + + legend:global(.usa-label) { + @include u-margin-top(0); + } + } + + :global(.usa-label), + :global(.usa-checkbox__label) { + @include u-margin-top(2); + } + + :global(.usa-label.usa-label--error) { + @include u-margin-top(0); + } + + :global(.usa-legend) { + @include u-margin-top(0); + } + + .innerHint { + @include u-margin-top(1); + } + + .hint { + @include u-margin-top(2); + } +} + +.buttonContainer { + @include u-display(flex); + flex-wrap: wrap; + + button:global(.usa-button) { + @include u-margin-top(2); + @include u-margin-bottom(0); + } + + @include at-media-max(mobile-lg) { + .backButton { + order: 2; + } + + .saveButton { + order: 1; + } + } +} diff --git a/src/components/Office/ShipmentForm/MobileHomeShipmentForm/mobileHomeShipmentSchema.js b/src/components/Office/ShipmentForm/MobileHomeShipmentForm/mobileHomeShipmentSchema.js new file mode 100644 index 00000000000..bc64db9ea22 --- /dev/null +++ b/src/components/Office/ShipmentForm/MobileHomeShipmentForm/mobileHomeShipmentSchema.js @@ -0,0 +1,108 @@ +import * as Yup from 'yup'; + +import { + AdditionalAddressSchema, + RequiredPlaceSchema, + OptionalPlaceSchema, +} from 'components/Customer/MtoShipmentForm/validationSchemas'; +import { toTotalInches } from 'utils/formatMtoShipment'; + +const currentYear = new Date().getFullYear(); +const maxYear = currentYear + 2; + +const mobileHomeShipmentSchema = () => { + const formSchema = Yup.object() + .shape({ + year: Yup.number().required('Required').min(1700, 'Invalid year').max(maxYear, 'Invalid year'), + + make: Yup.string().required('Required'), + + model: Yup.string().required('Required'), + + lengthFeet: Yup.number() + .min(0) + .nullable() + .when('lengthInches', { + is: (lengthInches) => !lengthInches, + then: (schema) => schema.required('Required'), + otherwise: (schema) => schema.notRequired(), + }), + + lengthInches: Yup.number().min(0).nullable(), + + widthFeet: Yup.number() + .min(0) + .nullable() + .when('widthInches', { + is: (widthInches) => !widthInches, + then: (schema) => schema.required('Required'), + otherwise: (schema) => schema.notRequired(), + }), + + widthInches: Yup.number().min(0).nullable(), + + heightFeet: Yup.number() + .min(0) + .nullable() + .when('heightInches', { + is: (heightInches) => !heightInches, + then: (schema) => schema.required('Required'), + otherwise: (schema) => schema.notRequired(), + }), + + heightInches: Yup.number().min(0).nullable(), + + pickup: RequiredPlaceSchema, + delivery: OptionalPlaceSchema, + secondaryPickup: AdditionalAddressSchema, + secondaryDelivery: AdditionalAddressSchema, + tertiaryPickup: AdditionalAddressSchema, + tertiaryDelivery: AdditionalAddressSchema, + counselorRemarks: Yup.string(), + customerRemarks: Yup.string(), + }) + .test('dimension-check', 'Dimensions requirements.', function dimensionTest(values) { + const { lengthFeet, lengthInches, widthFeet, widthInches, heightFeet, heightInches } = values; + const hasLength = lengthFeet !== undefined || lengthInches !== undefined; + const hasWidth = widthFeet !== undefined || widthInches !== undefined; + const hasHeight = heightFeet !== undefined || heightInches !== undefined; + + if (hasLength && hasWidth && hasHeight) { + const lengthInInches = toTotalInches(lengthFeet, lengthInches); + const widthInInches = toTotalInches(widthFeet, widthInches); + const heightInInches = toTotalInches(heightFeet, heightInches); + + if (lengthInInches < 1 && widthInInches < 1 && heightInInches < 1) { + const errors = []; + errors.push( + this.createError({ + path: 'lengthFeet', + message: 'Dimensions do not meet the requirement.', + }), + ); + + errors.push( + this.createError({ + path: 'widthFeet', + message: 'Dimensions do not meet the requirement.', + }), + ); + + errors.push( + this.createError({ + path: 'heightFeet', + message: 'Dimensions do not meet the requirement.', + }), + ); + + if (errors.length) { + throw new Yup.ValidationError(errors); + } + } + } + return true; + }); + return formSchema; +}; + +export default mobileHomeShipmentSchema; diff --git a/src/components/Office/ShipmentForm/ShipmentForm.jsx b/src/components/Office/ShipmentForm/ShipmentForm.jsx index fe86089d481..cdb75d8caa6 100644 --- a/src/components/Office/ShipmentForm/ShipmentForm.jsx +++ b/src/components/Office/ShipmentForm/ShipmentForm.jsx @@ -12,6 +12,8 @@ import { CloseoutOfficeInput } from '../../form/fields/CloseoutOfficeInput'; import ppmShipmentSchema from './ppmShipmentSchema'; import styles from './ShipmentForm.module.scss'; +import MobileHomeShipmentForm from './MobileHomeShipmentForm/MobileHomeShipmentForm'; +import mobileHomeShipmentSchema from './MobileHomeShipmentForm/mobileHomeShipmentSchema'; import ppmStyles from 'components/Customer/PPM/PPM.module.scss'; import SERVICE_MEMBER_AGENCIES from 'content/serviceMemberAgencies'; @@ -56,6 +58,9 @@ import { formatMtoShipmentForDisplay, formatPpmShipmentForAPI, formatPpmShipmentForDisplay, + formatMobileHomeShipmentForDisplay, + formatMobileHomeShipmentForAPI, + formatBoatShipmentForDisplay, } from 'utils/formatMtoShipment'; import { formatWeight, dropdownInputOptions } from 'utils/formatters'; import { validateDate } from 'utils/validation'; @@ -245,6 +250,11 @@ const ShipmentForm = (props) => { const isNTS = shipmentType === SHIPMENT_OPTIONS.NTS; const isNTSR = shipmentType === SHIPMENT_OPTIONS.NTSR; const isPPM = shipmentType === SHIPMENT_OPTIONS.PPM; + const isMobileHome = shipmentType === SHIPMENT_OPTIONS.MOBILE_HOME; + const isBoat = + shipmentType === SHIPMENT_OPTIONS.BOAT || + shipmentType === SHIPMENT_OPTIONS.BOAT_HAUL_AWAY || + shipmentType === SHIPMENT_OPTIONS.BOAT_TOW_AWAY; const showAccountingCodes = isNTS || isNTSR; @@ -260,21 +270,34 @@ const ShipmentForm = (props) => { const shipmentDestinationAddressOptions = dropdownInputOptions(shipmentDestinationTypes); const shipmentNumber = isHHG ? getShipmentNumber() : null; - const initialValues = isPPM - ? formatPpmShipmentForDisplay( - isCreatePage - ? { closeoutOffice: move.closeoutOffice } - : { - counselorRemarks: mtoShipment.counselorRemarks, - ppmShipment: mtoShipment.ppmShipment, - closeoutOffice: move.closeoutOffice, - }, - ) - : formatMtoShipmentForDisplay( - isCreatePage - ? { userRole, shipmentType } - : { userRole, shipmentType, agents: mtoShipment.mtoAgents, ...mtoShipment }, - ); + let initialValues = {}; + if (isPPM) { + initialValues = formatPpmShipmentForDisplay( + isCreatePage + ? { closeoutOffice: move.closeoutOffice } + : { + counselorRemarks: mtoShipment.counselorRemarks, + ppmShipment: mtoShipment.ppmShipment, + closeoutOffice: move.closeoutOffice, + }, + ); + } else if (isMobileHome) { + const hhgInitialValues = formatMtoShipmentForDisplay( + isCreatePage ? { userRole } : { userRole, shipmentType, agents: mtoShipment.mtoAgents, ...mtoShipment }, + ); + initialValues = formatMobileHomeShipmentForDisplay(mtoShipment?.mobileHomeShipment, hhgInitialValues); + } else if (isBoat) { + const hhgInitialValues = formatMtoShipmentForDisplay( + isCreatePage ? { userRole } : { userRole, shipmentType, agents: mtoShipment.mtoAgents, ...mtoShipment }, + ); + initialValues = formatBoatShipmentForDisplay(mtoShipment?.boatShipment, hhgInitialValues); + } else { + initialValues = formatMtoShipmentForDisplay( + isCreatePage + ? { userRole, shipmentType } + : { userRole, shipmentType, agents: mtoShipment.mtoAgents, ...mtoShipment }, + ); + } let showDeliveryFields; let showPickupFields; @@ -290,6 +313,10 @@ const ShipmentForm = (props) => { showCloseoutOffice, sitEstimatedWeightMax: estimatedWeightValue || 0, }); + } else if (isMobileHome) { + schema = mobileHomeShipmentSchema(); + showDeliveryFields = true; + showPickupFields = true; } else { const shipmentOptions = getShipmentOptions(shipmentType, userRole); @@ -497,7 +524,7 @@ const ShipmentForm = (props) => { nullableSacType = typeof sacType === 'undefined' ? '' : sacType; } - const pendingMtoShipment = formatMtoShipmentForAPI({ + let pendingMtoShipment = formatMtoShipmentForAPI({ shipmentType, moveCode, customerRemarks, @@ -521,6 +548,15 @@ const ShipmentForm = (props) => { tertiaryDelivery: hasTertiaryDelivery === 'yes' ? tertiaryDelivery : {}, }); + // Mobile Home Shipment + if (isMobileHome) { + const mobileHomeShipmentBody = formatMobileHomeShipmentForAPI(formValues); + pendingMtoShipment = { + ...pendingMtoShipment, + ...mobileHomeShipmentBody, + }; + } + const updateMTOShipmentPayload = { moveTaskOrderID, shipmentID: mtoShipment.id, @@ -578,7 +614,18 @@ const ShipmentForm = (props) => { validationSchema={schema} onSubmit={submitMTOShipment} > - {({ values, isValid, isSubmitting, setValues, handleSubmit, errors }) => { + {({ + values, + isValid, + isSubmitting, + setValues, + handleSubmit, + errors, + touched, + setFieldTouched, + setFieldError, + validateForm, + }) => { const { hasSecondaryDestination, hasTertiaryDestination, @@ -589,6 +636,36 @@ const ShipmentForm = (props) => { hasTertiaryDelivery, } = values; + const lengthHasError = !!( + (touched.lengthFeet && errors.lengthFeet === 'Required') || + (touched.lengthInches && errors.lengthFeet === 'Required') + ); + const widthHasError = !!( + (touched.widthFeet && errors.widthFeet === 'Required') || + (touched.widthInches && errors.widthFeet === 'Required') + ); + const heightHasError = !!( + (touched.heightFeet && errors.heightFeet === 'Required') || + (touched.heightInches && errors.heightFeet === 'Required') + ); + const dimensionError = !!( + (touched.lengthFeet && errors.lengthFeet?.includes('Dimensions')) || + (touched.lengthInches && errors.lengthFeet?.includes('Dimensions')) + ); + if (touched.lengthInches && !touched.lengthFeet) { + setFieldTouched('lengthFeet', true); + } + if (touched.widthInches && !touched.widthFeet) { + setFieldTouched('widthFeet', true); + } + if (touched.heightInches && !touched.heightFeet) { + setFieldTouched('heightFeet', true); + } + // manually turn off 'required' error when page loads if field is empty. + if (values.year === null && !touched.year && errors.year === 'Required') { + setFieldError('year', null); + } + const handleUseCurrentResidenceChange = (e) => { const { checked } = e.target; if (checked) { @@ -758,10 +835,23 @@ const ShipmentForm = (props) => {
- {isTOO && !isHHG && !isPPM && } + {isTOO && !isHHG && !isPPM && !isBoat && !isMobileHome && } {isNTSR && } + {isMobileHome && ( + + )} + {showPickupFields && (

Pickup details

diff --git a/src/components/Office/ShipmentForm/ShipmentForm.test.jsx b/src/components/Office/ShipmentForm/ShipmentForm.test.jsx index f64874791e9..49e15429ed3 100644 --- a/src/components/Office/ShipmentForm/ShipmentForm.test.jsx +++ b/src/components/Office/ShipmentForm/ShipmentForm.test.jsx @@ -2079,4 +2079,187 @@ describe('ShipmentForm component', () => { expect(screen.queryByRole('heading', { level: 2, name: /Storage in transit \(SIT\)/ })).not.toBeInTheDocument(); }); }); + + describe('creating a new Boat shipment', () => { + it('renders the Boat shipment form correctly', async () => { + renderWithRouter(); + + expect(await screen.findByTestId('tag')).toHaveTextContent('Boat'); + expect(await screen.findByText('Boat')).toBeInTheDocument(); + expect(screen.getByLabelText('Year')).toBeInTheDocument(); + expect(screen.getByLabelText('Make')).toBeInTheDocument(); + expect(screen.getByLabelText('Model')).toBeInTheDocument(); + expect(await screen.findByText('Length')).toBeInTheDocument(); + expect(await screen.findByText('Width')).toBeInTheDocument(); + expect(await screen.findByText('Height')).toBeInTheDocument(); + expect(await screen.findByText('Does the boat have a trailer?')).toBeInTheDocument(); + expect(await screen.findByText('What is the method of shipment?')).toBeInTheDocument(); + expect(await screen.findByText('Pickup details')).toBeInTheDocument(); + expect(await screen.findByText('Delivery details')).toBeInTheDocument(); + expect(await screen.findByText('Remarks')).toBeInTheDocument(); + }); + + it('validates length and width input fields to ensure they accept only numeric values', async () => { + renderWithRouter(); + + const lengthInput = await screen.findByTestId('lengthFeet'); + const widthInput = await screen.findByTestId('widthFeet'); + + await act(async () => { + userEvent.type(lengthInput, 'abc'); + userEvent.type(widthInput, 'xyz'); + }); + + await waitFor(() => { + expect(lengthInput).toHaveValue(''); + expect(widthInput).toHaveValue(''); + }); + }); + + it('validates required fields for boat shipment', async () => { + renderWithRouter(); + + const submitButton = screen.getByRole('button', { name: 'Save' }); + + await act(async () => { + userEvent.click(submitButton); + }); + + expect(submitButton).toBeDisabled(); + }); + + it('validates the year field is within the valid range', async () => { + renderWithRouter(); + + await act(async () => { + await userEvent.click(screen.getByTestId('year')); + await userEvent.type(screen.getByTestId('year'), '1600'); + const submitButton = screen.getByRole('button', { name: 'Save' }); + userEvent.click(submitButton); + }); + + expect(await screen.findByText('Invalid year')).toBeInTheDocument(); + }); + + it('validates dimensions - fail', async () => { + renderWithRouter(); + + // Enter dimensions below the required minimums + await act(async () => { + await userEvent.click(screen.getByTestId('lengthFeet')); + await userEvent.type(screen.getByTestId('lengthFeet'), '10'); + await userEvent.click(screen.getByTestId('widthFeet')); + await userEvent.type(screen.getByTestId('widthFeet'), '5'); + await userEvent.click(screen.getByTestId('heightFeet')); + await userEvent.type(screen.getByTestId('heightFeet'), '6'); + const submitButton = screen.getByRole('button', { name: 'Save' }); + userEvent.click(submitButton); + }); + + expect( + screen.queryByText( + 'The dimensions do not meet the requirements for a boat shipment. Please cancel and select a different shipment type.', + ), + ).toBeInTheDocument(); + }); + + it('validates dimensions - pass', async () => { + renderWithRouter(); + + // Enter dimensions below the required minimums + await act(async () => { + await userEvent.click(screen.getByTestId('lengthFeet')); + await userEvent.type(screen.getByTestId('lengthFeet'), '15'); + await userEvent.click(screen.getByTestId('widthFeet')); + await userEvent.type(screen.getByTestId('widthFeet'), '5'); + await userEvent.click(screen.getByTestId('heightFeet')); + await userEvent.type(screen.getByTestId('heightFeet'), '6'); + const submitButton = screen.getByRole('button', { name: 'Save' }); + userEvent.click(submitButton); + }); + + expect( + screen.queryByText( + 'The dimensions do not meet the requirements for a boat shipment. Please cancel and select a different shipment type.', + ), + ).not.toBeInTheDocument(); + }); + }); + + describe('creating a new Mobile Home shipment', () => { + it('renders the Mobile Home shipment form correctly', async () => { + renderWithRouter(); + + expect(screen.getByLabelText('Year')).toBeInTheDocument(); + expect(screen.getByLabelText('Make')).toBeInTheDocument(); + expect(screen.getByLabelText('Model')).toBeInTheDocument(); + expect(await screen.findByText('Length')).toBeInTheDocument(); + expect(await screen.findByText('Width')).toBeInTheDocument(); + expect(await screen.findByText('Height')).toBeInTheDocument(); + expect(await screen.findByText('Remarks')).toBeInTheDocument(); + }); + + it('validates length and width input fields to ensure they accept only numeric values', async () => { + renderWithRouter(); + + const lengthInput = await screen.findByTestId('lengthFeet'); + const heightInput = await screen.findByTestId('heightFeet'); + const widthInput = await screen.findByTestId('widthFeet'); + + await act(async () => { + userEvent.type(lengthInput, 'abc'); + userEvent.type(heightInput, 'xyz'); + userEvent.type(widthInput, 'zyz'); + }); + + await waitFor(() => { + expect(lengthInput).toHaveValue(''); + expect(heightInput).toHaveValue(''); + expect(widthInput).toHaveValue(''); + }); + }); + + it('validates required fields for Mobile Home shipment', async () => { + renderWithRouter(); + + const submitButton = screen.getByRole('button', { name: 'Save' }); + + await act(async () => { + userEvent.click(submitButton); + }); + + expect(submitButton).toBeDisabled(); + }); + + it('validates the year field is within the valid range', async () => { + renderWithRouter(); + + await act(async () => { + await userEvent.click(screen.getByTestId('year')); + await userEvent.type(screen.getByTestId('year'), '1600'); + const submitButton = screen.getByRole('button', { name: 'Save' }); + userEvent.click(submitButton); + }); + + expect(await screen.findByText('Invalid year')).toBeInTheDocument(); + }); + + it('validates dimensions - pass', async () => { + renderWithRouter(); + + // Enter dimensions below the required minimums + await act(async () => { + await userEvent.click(screen.getByTestId('lengthFeet')); + await userEvent.type(screen.getByTestId('lengthFeet'), '15'); + await userEvent.click(screen.getByTestId('widthFeet')); + await userEvent.type(screen.getByTestId('widthFeet'), '5'); + await userEvent.click(screen.getByTestId('heightFeet')); + await userEvent.type(screen.getByTestId('heightFeet'), '6'); + const submitButton = screen.getByRole('button', { name: 'Save' }); + userEvent.click(submitButton); + }); + + expect(screen.queryByText('Where and when should the movers deliver your mobile home?')).not.toBeInTheDocument(); + }); + }); }); diff --git a/src/components/Office/ShipmentFormRemarks/ShipmentFormRemarks.jsx b/src/components/Office/ShipmentFormRemarks/ShipmentFormRemarks.jsx index 4f2e4f54d54..66a94a24a8b 100644 --- a/src/components/Office/ShipmentFormRemarks/ShipmentFormRemarks.jsx +++ b/src/components/Office/ShipmentFormRemarks/ShipmentFormRemarks.jsx @@ -68,6 +68,10 @@ ShipmentFormRemarks.propTypes = { SHIPMENT_OPTIONS.NTS, SHIPMENT_OPTIONS.NTSR, SHIPMENT_OPTIONS.PPM, + SHIPMENT_OPTIONS.MOBILE_HOME, + SHIPMENT_OPTIONS.BOAT, + SHIPMENT_OPTIONS.BOAT_HAUL_AWAY, + SHIPMENT_OPTIONS.BOAT_TOW_AWAY, ]).isRequired, customerRemarks: PropTypes.string, counselorRemarks: PropTypes.string, diff --git a/src/pages/Office/AddShipment/AddShipment.jsx b/src/pages/Office/AddShipment/AddShipment.jsx index 6c4b9da2e0e..7c545dc86cb 100644 --- a/src/pages/Office/AddShipment/AddShipment.jsx +++ b/src/pages/Office/AddShipment/AddShipment.jsx @@ -23,6 +23,10 @@ const AddShipment = () => { if (shipmentType === SHIPMENT_OPTIONS_URL.NTSrelease) { shipmentType = SHIPMENT_OPTIONS.NTSR; + } else if (shipmentType === SHIPMENT_OPTIONS_URL.BOAT) { + shipmentType = SHIPMENT_OPTIONS.BOAT; + } else if (shipmentType === SHIPMENT_OPTIONS_URL.MOBILE_HOME) { + shipmentType = SHIPMENT_OPTIONS.MOBILE_HOME; } else { shipmentType = SHIPMENT_OPTIONS[shipmentType]; } diff --git a/src/pages/Office/MoveDetails/MoveDetails.jsx b/src/pages/Office/MoveDetails/MoveDetails.jsx index e2fb69d7450..dab9d58fde2 100644 --- a/src/pages/Office/MoveDetails/MoveDetails.jsx +++ b/src/pages/Office/MoveDetails/MoveDetails.jsx @@ -412,6 +412,8 @@ const MoveDetails = ({ + + )} diff --git a/src/pages/Office/ServicesCounselingAddShipment/ServicesCounselingAddShipment.jsx b/src/pages/Office/ServicesCounselingAddShipment/ServicesCounselingAddShipment.jsx index 038a6a9cf5b..e27dee8c4b7 100644 --- a/src/pages/Office/ServicesCounselingAddShipment/ServicesCounselingAddShipment.jsx +++ b/src/pages/Office/ServicesCounselingAddShipment/ServicesCounselingAddShipment.jsx @@ -23,6 +23,10 @@ const ServicesCounselingAddShipment = () => { if (shipmentType === SHIPMENT_OPTIONS_URL.NTSrelease) { shipmentType = SHIPMENT_OPTIONS.NTSR; + } else if (shipmentType === SHIPMENT_OPTIONS_URL.BOAT) { + shipmentType = SHIPMENT_OPTIONS.BOAT; + } else if (shipmentType === SHIPMENT_OPTIONS_URL.MOBILE_HOME) { + shipmentType = SHIPMENT_OPTIONS.MOBILE_HOME; } else { shipmentType = SHIPMENT_OPTIONS[shipmentType]; } diff --git a/src/pages/Office/ServicesCounselingMoveDetails/ServicesCounselingMoveDetails.jsx b/src/pages/Office/ServicesCounselingMoveDetails/ServicesCounselingMoveDetails.jsx index 104aac73b43..6dcc0e3c6a7 100644 --- a/src/pages/Office/ServicesCounselingMoveDetails/ServicesCounselingMoveDetails.jsx +++ b/src/pages/Office/ServicesCounselingMoveDetails/ServicesCounselingMoveDetails.jsx @@ -600,6 +600,12 @@ const ServicesCounselingMoveDetails = ({ + + ) } diff --git a/src/shared/constants.js b/src/shared/constants.js index 7852275dc1b..40c377d2b43 100644 --- a/src/shared/constants.js +++ b/src/shared/constants.js @@ -117,6 +117,7 @@ export const shipmentOptionLabels = [ { key: SHIPMENT_OPTIONS.HHG, label: 'HHG' }, { key: SHIPMENT_OPTIONS.PPM, label: 'PPM' }, { key: SHIPMENT_OPTIONS.BOAT, label: 'Boat' }, + { key: SHIPMENT_OPTIONS.MOBILE_HOME, label: 'Mobile Home' }, { key: SHIPMENT_TYPES.BOAT_HAUL_AWAY, label: 'Boat' }, { key: SHIPMENT_TYPES.BOAT_TOW_AWAY, label: 'Boat' }, ]; diff --git a/src/utils/formatMtoShipment.js b/src/utils/formatMtoShipment.js index 30acd350c93..9979cd71161 100644 --- a/src/utils/formatMtoShipment.js +++ b/src/utils/formatMtoShipment.js @@ -518,6 +518,76 @@ export function convertInchesToFeetAndInches(totalInches) { return { feet, inches }; } +// Initial values for mobile home shipment +export function formatMobileHomeShipmentForDisplay(mobileHomeShipment, initialValues) { + const { year, make, model, lengthInInches, widthInInches, heightInInches } = mobileHomeShipment || {}; + + const length = convertInchesToFeetAndInches(lengthInInches); + const width = convertInchesToFeetAndInches(widthInInches); + const height = convertInchesToFeetAndInches(heightInInches); + + const displayValues = { + year: year?.toString() || null, + make: make || '', + model: model || '', + lengthFeet: length.feet, + lengthInches: length.inches, + widthFeet: width.feet, + widthInches: width.inches, + heightFeet: height.feet, + heightInches: height.inches, + ...initialValues, + }; + + return displayValues; +} + +export function formatMobileHomeShipmentForAPI(values) { + const totalLengthInInches = toTotalInches(values.lengthFeet, values.lengthInches); + const totalWidthInInches = toTotalInches(values.widthFeet, values.widthInches); + const totalHeightInInches = toTotalInches(values.heightFeet, values.heightInches); + + const mobileHomeShipment = { + year: Number(values.year), + make: values.make, + model: values.model, + lengthInInches: totalLengthInInches, + widthInInches: totalWidthInInches, + heightInInches: totalHeightInInches, + }; + + return { + mobileHomeShipment, + }; +} + +// Initial values for boat shipment +export function formatBoatShipmentForDisplay(boatShipment, initialValues) { + const { year, make, model, lengthInInches, widthInInches, heightInInches, hasTrailer, isRoadworthy } = + boatShipment || {}; + + const length = convertInchesToFeetAndInches(lengthInInches); + const width = convertInchesToFeetAndInches(widthInInches); + const height = convertInchesToFeetAndInches(heightInInches); + + const displayValues = { + year: year?.toString() || null, + make: make || '', + model: model || '', + lengthFeet: length.feet, + lengthInches: length.inches, + widthFeet: width.feet, + widthInches: width.inches, + heightFeet: height.feet, + heightInches: height.inches, + hasTrailer: hasTrailer ? 'true' : 'false', + isRoadworthy: isRoadworthy === null ? '' : isRoadworthy?.toString(), + ...initialValues, + }; + + return displayValues; +} + export default { formatMtoShipmentForAPI, formatMtoShipmentForDisplay, @@ -527,4 +597,7 @@ export default { getMtoShipmentLabel, toTotalInches, convertInchesToFeetAndInches, + formatMobileHomeShipmentForDisplay, + formatMobileHomeShipmentForAPI, + formatBoatShipmentForDisplay, }; From 4fe98d1eb644260c426cbb5efc42f97a61d0df49 Mon Sep 17 00:00:00 2001 From: JamesHawks224 <146897935+JamesHawks224@users.noreply.github.com> Date: Thu, 10 Oct 2024 15:46:02 -0500 Subject: [PATCH 2/8] code changes. --- src/components/ShipmentList/ShipmentList.jsx | 35 ++++++++++++++------ src/shared/ToolTip/ToolTip.jsx | 1 + 2 files changed, 26 insertions(+), 10 deletions(-) diff --git a/src/components/ShipmentList/ShipmentList.jsx b/src/components/ShipmentList/ShipmentList.jsx index fb7cf1366c8..b2fd7fab796 100644 --- a/src/components/ShipmentList/ShipmentList.jsx +++ b/src/components/ShipmentList/ShipmentList.jsx @@ -25,6 +25,7 @@ export const ShipmentListItem = ({ showShipmentWeight, isOverweight, isMissingWeight, + showShipmentTooltip, }) => { const isMobileHome = shipment.shipmentType === SHIPMENT_OPTIONS.MOBILE_HOME; const isPPM = shipment.shipmentType === SHIPMENT_OPTIONS.PPM; @@ -63,15 +64,17 @@ export const ShipmentListItem = ({ {showNumber && ` ${shipmentNumber}`} {' '}
- {(shipment.shipmentType === SHIPMENT_OPTIONS.HHG || - shipment.shipmentType === SHIPMENT_OPTIONS.NTS || - isBoat) && ( - <> - {formatWeight(shipment.primeEstimatedWeight * WEIGHT_ADJUSTMENT)} - - - )} - {shipment.shipmentType === SHIPMENT_OPTIONS.NTSR && ( + {showShipmentTooltip && + (shipment.shipmentType === SHIPMENT_OPTIONS.HHG || + shipment.shipmentType === SHIPMENT_OPTIONS.NTS || + isBoat || + isMobileHome) && ( + <> + {formatWeight(shipment.primeEstimatedWeight * WEIGHT_ADJUSTMENT)} + + + )} + {showShipmentTooltip && shipment.shipmentType === SHIPMENT_OPTIONS.NTSR && ( <> {formatWeight(shipment.ntsRecordedWeight * WEIGHT_ADJUSTMENT)} @@ -142,6 +145,7 @@ ShipmentListItem.propTypes = { showNumber: bool, showIncomplete: bool, showShipmentWeight: bool, + showShipmentTooltip: bool, isOverweight: bool, isMissingWeight: bool, }; @@ -150,13 +154,21 @@ ShipmentListItem.defaultProps = { showNumber: true, showIncomplete: false, showShipmentWeight: false, + showShipmentTooltip: false, isOverweight: false, isMissingWeight: false, onShipmentClick: null, onDeleteClick: null, }; -const ShipmentList = ({ shipments, onShipmentClick, onDeleteClick, moveSubmitted, showShipmentWeight }) => { +const ShipmentList = ({ + shipments, + onShipmentClick, + onDeleteClick, + moveSubmitted, + showShipmentWeight, + showShipmentTooltip, +}) => { const shipmentNumbersByType = {}; const shipmentCountByType = {}; shipments.forEach((shipment) => { @@ -224,6 +236,7 @@ const ShipmentList = ({ shipments, onShipmentClick, onDeleteClick, moveSubmitted shipmentNumber={shipmentNumber} showNumber={showNumber} showShipmentWeight={showShipmentWeight} + showShipmentTooltip={showShipmentTooltip} canEditOrDelete={canEditOrDelete} isOverweight={isOverweight} showIncomplete={isIncomplete} @@ -244,10 +257,12 @@ ShipmentList.propTypes = { onDeleteClick: func, moveSubmitted: bool.isRequired, showShipmentWeight: bool, + showShipmentTooltip: bool, }; ShipmentList.defaultProps = { showShipmentWeight: false, + showShipmentTooltip: false, onShipmentClick: null, onDeleteClick: null, }; diff --git a/src/shared/ToolTip/ToolTip.jsx b/src/shared/ToolTip/ToolTip.jsx index 5f613fa13d8..e4519798aea 100644 --- a/src/shared/ToolTip/ToolTip.jsx +++ b/src/shared/ToolTip/ToolTip.jsx @@ -48,6 +48,7 @@ const ToolTip = ({ text, position, icon, color, closeOnLeave }) => { return (
setIsVisible(true)} onMouseLeave={() => closeOnMouseLeave()} onClick={() => determineIsVisible()} From 678c97cd83a095695d09218b9c13864145615af1 Mon Sep 17 00:00:00 2001 From: JamesHawks224 <146897935+JamesHawks224@users.noreply.github.com> Date: Thu, 10 Oct 2024 16:21:25 -0500 Subject: [PATCH 3/8] tests added. --- .../ShipmentList/ShipmentList.test.jsx | 48 ++++++++++++++++++- src/shared/ToolTip/ToolTip.test.jsx | 10 ++++ 2 files changed, 57 insertions(+), 1 deletion(-) diff --git a/src/components/ShipmentList/ShipmentList.test.jsx b/src/components/ShipmentList/ShipmentList.test.jsx index 6e46c06d6d2..5bb673bc46f 100644 --- a/src/components/ShipmentList/ShipmentList.test.jsx +++ b/src/components/ShipmentList/ShipmentList.test.jsx @@ -4,7 +4,7 @@ import userEvent from '@testing-library/user-event'; import ShipmentList from './ShipmentList'; -import { SHIPMENT_OPTIONS } from 'shared/constants'; +import { SHIPMENT_OPTIONS, SHIPMENT_TYPES } from 'shared/constants'; import { formatWeight } from 'utils/formatters'; beforeEach(() => { @@ -91,6 +91,52 @@ describe('ShipmentList component', () => { }); }); +describe('ShipmentList shipment weight tooltip', () => { + const defaultProps = { + moveSubmitted: false, + }; + + it.each([ + [SHIPMENT_OPTIONS.HHG, 'ID-2', '110% Prime Estimated Weight'], + [SHIPMENT_OPTIONS.NTS, 'ID-3', '110% Prime Estimated Weight'], + [SHIPMENT_OPTIONS.NTSR, 'ID-4', '110% Previously Recorded Weight'], + [SHIPMENT_TYPES.BOAT_HAUL_AWAY, 'ID-5', '110% Prime Estimated Weight'], + [SHIPMENT_TYPES.BOAT_TOW_AWAY, 'ID-6', '110% Prime Estimated Weight'], + [SHIPMENT_OPTIONS.MOBILE_HOME, 'ID-7', '110% Prime Estimated Weight'], + ])('shipment weight tooltip, show is true. %s', async (type, id, expectedTooltipText) => { + // Render component + const props = { ...defaultProps, showShipmentTooltip: true, shipments: [{ id, shipmentType: type }] }; + render(); + + // Verify tooltip exists + const tooltipIcon = screen.getByTestId('tooltip-container'); + expect(tooltipIcon).toBeInTheDocument(); + + // Click the tooltip + await userEvent.hover(tooltipIcon); + + // Verify tooltip text + const tooltipText = await screen.findByText(expectedTooltipText); + expect(tooltipText).toBeInTheDocument(); + }); + + it.each([ + [SHIPMENT_OPTIONS.HHG, 'ID-2'], + [SHIPMENT_OPTIONS.NTS, 'ID-3'], + [SHIPMENT_OPTIONS.NTSR, 'ID-4'], + [SHIPMENT_TYPES.BOAT_HAUL_AWAY, 'ID-5'], + [SHIPMENT_TYPES.BOAT_TOW_AWAY, 'ID-6'], + [SHIPMENT_OPTIONS.MOBILE_HOME, 'ID-7'], + ])('shipment weight tooltip, show is false. %s', async (type, id) => { + // Render component + const props = { ...defaultProps, showShipmentTooltip: false, shipments: [{ id, shipmentType: type }] }; + render(); + + // Verify tooltip doesn't exist + expect(screen.queryByTestId('tooltip-container')).not.toBeInTheDocument(); + }); +}); + describe('Shipment List being used for billable weight', () => { it('renders maximum billable weight, actual billable weight, actual weight and weight allowance with no flags', () => { const shipments = [ diff --git a/src/shared/ToolTip/ToolTip.test.jsx b/src/shared/ToolTip/ToolTip.test.jsx index 63c0a2f1912..ce1dab97a84 100644 --- a/src/shared/ToolTip/ToolTip.test.jsx +++ b/src/shared/ToolTip/ToolTip.test.jsx @@ -1,5 +1,6 @@ import React from 'react'; import { mount } from 'enzyme'; +import { render, screen } from '@testing-library/react'; import ToolTip from './ToolTip'; @@ -56,4 +57,13 @@ describe('ToolTip', () => { // Assert that the tooltip content is displayed expect(tooltipContent.text()).toBe(text); }); + + it('verify data-testid is present', () => { + const text = 'Test Text'; + render(); + + // Verify data-testid is present + const tooltipIcon = screen.getByTestId('tooltip-container'); + expect(tooltipIcon).toBeInTheDocument(); + }); }); From 4d4b9b93434e67015f8ed3419708a84ec9615d51 Mon Sep 17 00:00:00 2001 From: AaronW Date: Thu, 17 Oct 2024 20:07:39 +0000 Subject: [PATCH 4/8] removed extra code --- src/utils/formatMtoShipment.js | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/utils/formatMtoShipment.js b/src/utils/formatMtoShipment.js index 7611f251b73..f4480f42425 100644 --- a/src/utils/formatMtoShipment.js +++ b/src/utils/formatMtoShipment.js @@ -519,9 +519,6 @@ export function convertInchesToFeetAndInches(totalInches) { return { feet, inches }; } -// Initial values for mobile home shipment -export function formatMobileHomeShipmentForDisplay(mobileHomeShipment, initialValues) { - const { year, make, model, lengthInInches, widthInInches, heightInInches } = mobileHomeShipment || {}; // Initial values for boat shipment export function formatBoatShipmentForDisplay(boatShipment, initialValues) { const { year, make, model, lengthInInches, widthInInches, heightInInches, hasTrailer, isRoadworthy } = @@ -577,8 +574,7 @@ export function formatBoatShipmentForAPI(values) { } export function formatMobileHomeShipmentForDisplay(mobileHomeShipment, initialValues) { - const { year, make, model, lengthInInches, widthInInches, heightInInches } = - mobileHomeShipment || {}; + const { year, make, model, lengthInInches, widthInInches, heightInInches } = mobileHomeShipment || {}; const length = convertInchesToFeetAndInches(lengthInInches); const width = convertInchesToFeetAndInches(widthInInches); From 2cb6991da6da8441ee2037058702c8a1d20e0b17 Mon Sep 17 00:00:00 2001 From: AaronW Date: Thu, 17 Oct 2024 16:38:07 -0500 Subject: [PATCH 5/8] re-added the formatForBoatShipment import --- src/components/Office/ShipmentForm/ShipmentForm.jsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/components/Office/ShipmentForm/ShipmentForm.jsx b/src/components/Office/ShipmentForm/ShipmentForm.jsx index 08e26a6c5f1..fba9286ccce 100644 --- a/src/components/Office/ShipmentForm/ShipmentForm.jsx +++ b/src/components/Office/ShipmentForm/ShipmentForm.jsx @@ -62,6 +62,7 @@ import { formatPpmShipmentForDisplay, formatMobileHomeShipmentForDisplay, formatMobileHomeShipmentForAPI, + formatBoatShipmentForDisplay, formatBoatShipmentForAPI, } from 'utils/formatMtoShipment'; import { formatWeight, dropdownInputOptions } from 'utils/formatters'; @@ -560,7 +561,7 @@ const ShipmentForm = (props) => { ...mobileHomeShipmentBody, }; } - + // Boat Shipment if (isBoat) { const boatShipmentBody = formatBoatShipmentForAPI(formValues); From 42289dd234c886164568d729f554ed19714a6e37 Mon Sep 17 00:00:00 2001 From: AaronW Date: Thu, 3 Oct 2024 16:49:52 +0000 Subject: [PATCH 6/8] added additional testing From bfebbf37c80e531e42a2a2918e633751d7887ad1 Mon Sep 17 00:00:00 2001 From: Tae Jung Date: Tue, 22 Oct 2024 15:27:17 +0000 Subject: [PATCH 7/8] remove comment --- .../Office/DefinitionLists/ShipmentInfoListSelector.jsx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/components/Office/DefinitionLists/ShipmentInfoListSelector.jsx b/src/components/Office/DefinitionLists/ShipmentInfoListSelector.jsx index 2d4a805b5fb..fa152b443d9 100644 --- a/src/components/Office/DefinitionLists/ShipmentInfoListSelector.jsx +++ b/src/components/Office/DefinitionLists/ShipmentInfoListSelector.jsx @@ -1,6 +1,5 @@ import React from 'react'; import * as PropTypes from 'prop-types'; -// import BoatShipmentInfoList from 'components/Office/DefinitionLists/BoatShipmentInfoList'; import { ShipmentShape } from 'types/shipment'; import ShipmentInfoList from 'components/Office/DefinitionLists/ShipmentInfoList'; From c0564c6b4be7a4929baa52291b802fb9265eb770 Mon Sep 17 00:00:00 2001 From: Tae Jung Date: Tue, 22 Oct 2024 15:36:44 +0000 Subject: [PATCH 8/8] fixing changes to match IntegrationBranch --- .../Office/ShipmentForm/ShipmentForm.jsx | 2 + .../Office/ShipmentForm/ShipmentForm.test.jsx | 1 + src/utils/formatMtoShipment.js | 45 ++++++++++--------- 3 files changed, 26 insertions(+), 22 deletions(-) diff --git a/src/components/Office/ShipmentForm/ShipmentForm.jsx b/src/components/Office/ShipmentForm/ShipmentForm.jsx index fba9286ccce..5a3b1d9e9aa 100644 --- a/src/components/Office/ShipmentForm/ShipmentForm.jsx +++ b/src/components/Office/ShipmentForm/ShipmentForm.jsx @@ -318,6 +318,8 @@ const ShipmentForm = (props) => { }); } else if (isMobileHome) { schema = mobileHomeShipmentSchema(); + showDeliveryFields = true; + showPickupFields = true; } else if (isBoat) { schema = boatShipmentSchema(); showDeliveryFields = true; diff --git a/src/components/Office/ShipmentForm/ShipmentForm.test.jsx b/src/components/Office/ShipmentForm/ShipmentForm.test.jsx index 1cd37cc16d9..d6f3c01a1d8 100644 --- a/src/components/Office/ShipmentForm/ShipmentForm.test.jsx +++ b/src/components/Office/ShipmentForm/ShipmentForm.test.jsx @@ -2238,6 +2238,7 @@ describe('ShipmentForm component', () => { ).not.toBeInTheDocument(); }); }); + describe('creating a new Mobile Home shipment', () => { it('renders the Mobile Home shipment form correctly', async () => { renderWithRouter(); diff --git a/src/utils/formatMtoShipment.js b/src/utils/formatMtoShipment.js index f4480f42425..73b0902c448 100644 --- a/src/utils/formatMtoShipment.js +++ b/src/utils/formatMtoShipment.js @@ -519,10 +519,9 @@ export function convertInchesToFeetAndInches(totalInches) { return { feet, inches }; } -// Initial values for boat shipment -export function formatBoatShipmentForDisplay(boatShipment, initialValues) { - const { year, make, model, lengthInInches, widthInInches, heightInInches, hasTrailer, isRoadworthy } = - boatShipment || {}; +// Initial values for mobile home shipment +export function formatMobileHomeShipmentForDisplay(mobileHomeShipment, initialValues) { + const { year, make, model, lengthInInches, widthInInches, heightInInches } = mobileHomeShipment || {}; const length = convertInchesToFeetAndInches(lengthInInches); const width = convertInchesToFeetAndInches(widthInInches); @@ -538,43 +537,35 @@ export function formatBoatShipmentForDisplay(boatShipment, initialValues) { widthInches: width.inches, heightFeet: height.feet, heightInches: height.inches, - hasTrailer: hasTrailer ? 'true' : 'false', - isRoadworthy: isRoadworthy === null ? '' : isRoadworthy?.toString(), ...initialValues, }; return displayValues; } -export function formatBoatShipmentForAPI(values) { +export function formatMobileHomeShipmentForAPI(values) { const totalLengthInInches = toTotalInches(values.lengthFeet, values.lengthInches); const totalWidthInInches = toTotalInches(values.widthFeet, values.widthInches); const totalHeightInInches = toTotalInches(values.heightFeet, values.heightInches); - const hasTrailerBool = values.hasTrailer === 'true'; - const isRoadworthyBool = values.isRoadworthy && hasTrailerBool ? values.isRoadworthy === 'true' : null; - const boatShipment = { - type: values.type, + const mobileHomeShipment = { year: Number(values.year), make: values.make, model: values.model, lengthInInches: totalLengthInInches, widthInInches: totalWidthInInches, heightInInches: totalHeightInInches, - hasTrailer: values.hasTrailer === 'true', - isRoadworthy: values.hasTrailer === 'true' ? isRoadworthyBool : null, }; - const mtoShipmentType = - boatShipment.type === boatShipmentTypes.TOW_AWAY ? SHIPMENT_TYPES.BOAT_TOW_AWAY : SHIPMENT_TYPES.BOAT_HAUL_AWAY; return { - shipmentType: mtoShipmentType, - boatShipment, + mobileHomeShipment, }; } -export function formatMobileHomeShipmentForDisplay(mobileHomeShipment, initialValues) { - const { year, make, model, lengthInInches, widthInInches, heightInInches } = mobileHomeShipment || {}; +// Initial values for boat shipment +export function formatBoatShipmentForDisplay(boatShipment, initialValues) { + const { year, make, model, lengthInInches, widthInInches, heightInInches, hasTrailer, isRoadworthy } = + boatShipment || {}; const length = convertInchesToFeetAndInches(lengthInInches); const width = convertInchesToFeetAndInches(widthInInches); @@ -590,28 +581,38 @@ export function formatMobileHomeShipmentForDisplay(mobileHomeShipment, initialVa widthInches: width.inches, heightFeet: height.feet, heightInches: height.inches, + hasTrailer: hasTrailer ? 'true' : 'false', + isRoadworthy: isRoadworthy === null ? '' : isRoadworthy?.toString(), ...initialValues, }; return displayValues; } -export function formatMobileHomeShipmentForAPI(values) { +export function formatBoatShipmentForAPI(values) { const totalLengthInInches = toTotalInches(values.lengthFeet, values.lengthInches); const totalWidthInInches = toTotalInches(values.widthFeet, values.widthInches); const totalHeightInInches = toTotalInches(values.heightFeet, values.heightInches); + const hasTrailerBool = values.hasTrailer === 'true'; + const isRoadworthyBool = values.isRoadworthy && hasTrailerBool ? values.isRoadworthy === 'true' : null; - const mobileHomeShipment = { + const boatShipment = { + type: values.type, year: Number(values.year), make: values.make, model: values.model, lengthInInches: totalLengthInInches, widthInInches: totalWidthInInches, heightInInches: totalHeightInInches, + hasTrailer: values.hasTrailer === 'true', + isRoadworthy: values.hasTrailer === 'true' ? isRoadworthyBool : null, }; + const mtoShipmentType = + boatShipment.type === boatShipmentTypes.TOW_AWAY ? SHIPMENT_TYPES.BOAT_TOW_AWAY : SHIPMENT_TYPES.BOAT_HAUL_AWAY; return { - mobileHomeShipment, + shipmentType: mtoShipmentType, + boatShipment, }; }